| ... | @@ -983,7 +983,7 @@ fn detectAbiAndDynamicLinker( | ... | @@ -983,7 +983,7 @@ fn detectAbiAndDynamicLinker( |
| 983 | | 983 | |
| 984 | // Best case scenario: the executable is dynamically linked, and we can iterate | 984 | // Best case scenario: the executable is dynamically linked, and we can iterate |
| 985 | // over our own shared objects and find a dynamic linker. | 985 | // over our own shared objects and find a dynamic linker. |
| 986 | const elf_file = blk: { | 986 | const elf_file = elf_file: { |
| 987 | // This block looks for a shebang line in /usr/bin/env, | 987 | // This block looks for a shebang line in /usr/bin/env, |
| 988 | // if it finds one, then instead of using /usr/bin/env as the ELF file to examine, it uses the file it references instead, | 988 | // if it finds one, then instead of using /usr/bin/env as the ELF file to examine, it uses the file it references instead, |
| 989 | // doing the same logic recursively in case it finds another shebang line. | 989 | // doing the same logic recursively in case it finds another shebang line. |
| ... | @@ -995,9 +995,22 @@ fn detectAbiAndDynamicLinker( | ... | @@ -995,9 +995,22 @@ fn detectAbiAndDynamicLinker( |
| 995 | // Haiku does not have a /usr root directory. | 995 | // Haiku does not have a /usr root directory. |
| 996 | .haiku => "/bin/env", | 996 | .haiku => "/bin/env", |
| 997 | }; | 997 | }; |
| 998 | // #! (2) + 255 (max length of shebang line since Linux 5.1) + \n (1) | 998 | |
| 999 | var buffer: [258]u8 = undefined; | 999 | // According to `man 2 execve`: |
| | 1000 | // |
| | 1001 | // The kernel imposes a maximum length on the text |
| | 1002 | // that follows the "#!" characters at the start of a script; |
| | 1003 | // characters beyond the limit are ignored. |
| | 1004 | // Before Linux 5.1, the limit is 127 characters. |
| | 1005 | // Since Linux 5.1, the limit is 255 characters. |
| | 1006 | // |
| | 1007 | // Tests show that bash and zsh consider 255 as total limit, |
| | 1008 | // *including* "#!" characters and ignoring newline. |
| | 1009 | // For safety, we set max length as 255 + \n (1). |
| | 1010 | var buffer: [255 + 1]u8 = undefined; |
| 1000 | while (true) { | 1011 | while (true) { |
| | 1012 | // Interpreter path can be relative on Linux, but |
| | 1013 | // for simplicity we are asserting it is an absolute path. |
| 1001 | const file = fs.openFileAbsolute(file_name, .{}) catch |err| switch (err) { | 1014 | const file = fs.openFileAbsolute(file_name, .{}) catch |err| switch (err) { |
| 1002 | error.NoSpaceLeft => unreachable, | 1015 | error.NoSpaceLeft => unreachable, |
| 1003 | error.NameTooLong => unreachable, | 1016 | error.NameTooLong => unreachable, |
| ... | @@ -1027,27 +1040,55 @@ fn detectAbiAndDynamicLinker( | ... | @@ -1027,27 +1040,55 @@ fn detectAbiAndDynamicLinker( |
| 1027 | | 1040 | |
| 1028 | else => |e| return e, | 1041 | else => |e| return e, |
| 1029 | }; | 1042 | }; |
| 1030 | errdefer file.close(); | 1043 | var is_elf_file = false; |
| 1031 | | 1044 | defer if (is_elf_file == false) file.close(); |
| 1032 | const len = preadAtLeast(file, &buffer, 0, buffer.len) catch |err| switch (err) { | 1045 | |
| | 1046 | // Shortest working interpreter path is "#!/i" (4) |
| | 1047 | // (interpreter is "/i", assuming all pathes are absolute, like in above comment). |
| | 1048 | // ELF magic number length is also 4. |
| | 1049 | // |
| | 1050 | // If file is shorter than that, it is definitely not ELF file |
| | 1051 | // nor file with "shebang" line. |
| | 1052 | const min_len: usize = 4; |
| | 1053 | |
| | 1054 | const len = preadAtLeast(file, &buffer, 0, min_len) catch |err| switch (err) { |
| 1033 | error.UnexpectedEndOfFile, | 1055 | error.UnexpectedEndOfFile, |
| 1034 | error.UnableToReadElfFile, | 1056 | error.UnableToReadElfFile, |
| 1035 | => break :blk file, | 1057 | => return defaultAbiAndDynamicLinker(cpu, os, query), |
| 1036 | | 1058 | |
| 1037 | else => |e| return e, | 1059 | else => |e| return e, |
| 1038 | }; | 1060 | }; |
| 1039 | const newline = mem.indexOfScalar(u8, buffer[0..len], '\n') orelse break :blk file; | 1061 | const content = buffer[0..len]; |
| 1040 | const line = buffer[0..newline]; | 1062 | |
| 1041 | if (!mem.startsWith(u8, line, "#!")) break :blk file; | 1063 | if (mem.eql(u8, content[0..4], std.elf.MAGIC)) { |
| 1042 | var it = mem.tokenizeScalar(u8, line[2..], ' '); | 1064 | // It is very likely ELF file! |
| 1043 | file_name = it.next() orelse return defaultAbiAndDynamicLinker(cpu, os, query); | 1065 | is_elf_file = true; |
| 1044 | file.close(); | 1066 | break :elf_file file; |
| | 1067 | } else if (mem.eql(u8, content[0..2], "#!")) { |
| | 1068 | // We detected shebang, now parse entire line. |
| | 1069 | |
| | 1070 | // Trim leading "#!", spaces and tabs. |
| | 1071 | const trimmed_line = mem.trimLeft(u8, content[2..], &.{ ' ', '\t' }); |
| | 1072 | |
| | 1073 | // This line can have: |
| | 1074 | // * Interpreter path only, |
| | 1075 | // * Interpreter path and arguments, all separated by space, tab or NUL character. |
| | 1076 | // And optionally newline at the end. |
| | 1077 | const path_maybe_args = mem.trimRight(u8, trimmed_line, "\n"); |
| | 1078 | |
| | 1079 | // Separate path and args. |
| | 1080 | const path_end = mem.indexOfAny(u8, path_maybe_args, &.{ ' ', '\t', 0 }) orelse path_maybe_args.len; |
| | 1081 | |
| | 1082 | file_name = path_maybe_args[0..path_end]; |
| | 1083 | continue; |
| | 1084 | } else { |
| | 1085 | // Not a ELF file, not a shell script with "shebang line", invalid duck. |
| | 1086 | return defaultAbiAndDynamicLinker(cpu, os, query); |
| | 1087 | } |
| 1045 | } | 1088 | } |
| 1046 | }; | 1089 | }; |
| 1047 | defer elf_file.close(); | 1090 | defer elf_file.close(); |
| 1048 | | 1091 | |
| 1049 | // If Zig is statically linked, such as via distributed binary static builds, the above | | |
| 1050 | // trick (block self_exe) won't work. The next thing we fall back to is the same thing, but for elf_file. | | |
| 1051 | // TODO: inline this function and combine the buffer we already read above to find | 1092 | // TODO: inline this function and combine the buffer we already read above to find |
| 1052 | // the possible shebang line with the buffer we use for the ELF header. | 1093 | // the possible shebang line with the buffer we use for the ELF header. |
| 1053 | return abiAndDynamicLinkerFromFile(elf_file, cpu, os, ld_info_list, query) catch |err| switch (err) { | 1094 | return abiAndDynamicLinkerFromFile(elf_file, cpu, os, ld_info_list, query) catch |err| switch (err) { |
| ... | @@ -1075,7 +1116,7 @@ fn detectAbiAndDynamicLinker( | ... | @@ -1075,7 +1116,7 @@ fn detectAbiAndDynamicLinker( |
| 1075 | }; | 1116 | }; |
| 1076 | } | 1117 | } |
| 1077 | | 1118 | |
| 1078 | fn defaultAbiAndDynamicLinker(cpu: Target.Cpu, os: Target.Os, query: Target.Query) !Target { | 1119 | fn defaultAbiAndDynamicLinker(cpu: Target.Cpu, os: Target.Os, query: Target.Query) Target { |
| 1079 | const abi = query.abi orelse Target.Abi.default(cpu.arch, os); | 1120 | const abi = query.abi orelse Target.Abi.default(cpu.arch, os); |
| 1080 | return .{ | 1121 | return .{ |
| 1081 | .cpu = cpu, | 1122 | .cpu = cpu, |