| ... | ... | @@ -983,7 +983,7 @@ fn detectAbiAndDynamicLinker( |
| 983 | 983 | |
| 984 | 984 | // Best case scenario: the executable is dynamically linked, and we can iterate |
| 985 | 985 | // over our own shared objects and find a dynamic linker. |
| 986 | | const elf_file = blk: { |
| 986 | const elf_file = elf_file: { |
| 987 | 987 | // This block looks for a shebang line in /usr/bin/env, |
| 988 | 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 | 989 | // doing the same logic recursively in case it finds another shebang line. |
| ... | ... | @@ -995,9 +995,22 @@ fn detectAbiAndDynamicLinker( |
| 995 | 995 | // Haiku does not have a /usr root directory. |
| 996 | 996 | .haiku => "/bin/env", |
| 997 | 997 | }; |
| 998 | | // #! (2) + 255 (max length of shebang line since Linux 5.1) + \n (1) |
| 999 | | var buffer: [258]u8 = undefined; |
| 998 | |
| 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 | 1011 | while (true) { |
| 1012 | // Interpreter path can be relative on Linux, but |
| 1013 | // for simplicity we are asserting it is an absolute path. |
| 1001 | 1014 | const file = fs.openFileAbsolute(file_name, .{}) catch |err| switch (err) { |
| 1002 | 1015 | error.NoSpaceLeft => unreachable, |
| 1003 | 1016 | error.NameTooLong => unreachable, |
| ... | ... | @@ -1027,27 +1040,55 @@ fn detectAbiAndDynamicLinker( |
| 1027 | 1040 | |
| 1028 | 1041 | else => |e| return e, |
| 1029 | 1042 | }; |
| 1030 | | errdefer file.close(); |
| 1031 | | |
| 1032 | | const len = preadAtLeast(file, &buffer, 0, buffer.len) catch |err| switch (err) { |
| 1043 | var is_elf_file = false; |
| 1044 | defer if (is_elf_file == false) file.close(); |
| 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 | 1055 | error.UnexpectedEndOfFile, |
| 1034 | 1056 | error.UnableToReadElfFile, |
| 1035 | | => break :blk file, |
| 1057 | => return defaultAbiAndDynamicLinker(cpu, os, query), |
| 1036 | 1058 | |
| 1037 | 1059 | else => |e| return e, |
| 1038 | 1060 | }; |
| 1039 | | const newline = mem.indexOfScalar(u8, buffer[0..len], '\n') orelse break :blk file; |
| 1040 | | const line = buffer[0..newline]; |
| 1041 | | if (!mem.startsWith(u8, line, "#!")) break :blk file; |
| 1042 | | var it = mem.tokenizeScalar(u8, line[2..], ' '); |
| 1043 | | file_name = it.next() orelse return defaultAbiAndDynamicLinker(cpu, os, query); |
| 1044 | | file.close(); |
| 1061 | const content = buffer[0..len]; |
| 1062 | |
| 1063 | if (mem.eql(u8, content[0..4], std.elf.MAGIC)) { |
| 1064 | // It is very likely ELF file! |
| 1065 | is_elf_file = true; |
| 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 | 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 | 1092 | // TODO: inline this function and combine the buffer we already read above to find |
| 1052 | 1093 | // the possible shebang line with the buffer we use for the ELF header. |
| 1053 | 1094 | return abiAndDynamicLinkerFromFile(elf_file, cpu, os, ld_info_list, query) catch |err| switch (err) { |
| ... | ... | @@ -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 | 1120 | const abi = query.abi orelse Target.Abi.default(cpu.arch, os); |
| 1080 | 1121 | return .{ |
| 1081 | 1122 | .cpu = cpu, |