| ... | @@ -419,6 +419,7 @@ pub fn resolveTargetQuery(io: Io, query: Target.Query) DetectError!Target { | ... | @@ -419,6 +419,7 @@ pub fn resolveTargetQuery(io: Io, query: Target.Query) DetectError!Target { |
| 419 | var result = detectAbiAndDynamicLinker(io, cpu, os, query) catch |err| switch (err) { | 419 | var result = detectAbiAndDynamicLinker(io, cpu, os, query) catch |err| switch (err) { |
| 420 | error.Canceled => |e| return e, | 420 | error.Canceled => |e| return e, |
| 421 | error.Unexpected => |e| return e, | 421 | error.Unexpected => |e| return e, |
| | 422 | error.ApiLevelQueryFailed => |e| return e, |
| 422 | error.WouldBlock => return error.Unexpected, | 423 | error.WouldBlock => return error.Unexpected, |
| 423 | error.ConnectionResetByPeer => return error.Unexpected, | 424 | error.ConnectionResetByPeer => return error.Unexpected, |
| 424 | error.NotOpenForReading => return error.Unexpected, | 425 | error.NotOpenForReading => return error.Unexpected, |
| ... | @@ -497,27 +498,6 @@ pub fn resolveTargetQuery(io: Io, query: Target.Query) DetectError!Target { | ... | @@ -497,27 +498,6 @@ pub fn resolveTargetQuery(io: Io, query: Target.Query) DetectError!Target { |
| 497 | } | 498 | } |
| 498 | } | 499 | } |
| 499 | | 500 | |
| 500 | if (builtin.os.tag == .linux and result.isBionicLibC() and query.os_tag == null and query.android_api_level == null) { | | |
| 501 | result.os.version_range.linux.android = detectAndroidApiLevel(io) catch |err| return switch (err) { | | |
| 502 | error.InvalidWtf8, | | |
| 503 | error.InvalidBatchScriptArg, | | |
| 504 | => unreachable, // Windows-only | | |
| 505 | error.ApiLevelQueryFailed => |e| e, | | |
| 506 | else => blk: { | | |
| 507 | std.log.err("spawning or reading from getprop failed ({s})", .{@errorName(err)}); | | |
| 508 | switch (err) { | | |
| 509 | error.SystemResources, | | |
| 510 | error.FileSystem, | | |
| 511 | error.ProcessFdQuotaExceeded, | | |
| 512 | error.SystemFdQuotaExceeded, | | |
| 513 | error.SymLinkLoop, | | |
| 514 | => |e| break :blk e, | | |
| 515 | else => break :blk error.ApiLevelQueryFailed, | | |
| 516 | } | | |
| 517 | }, | | |
| 518 | }; | | |
| 519 | } | | |
| 520 | | | |
| 521 | return result; | 501 | return result; |
| 522 | } | 502 | } |
| 523 | | 503 | |
| ... | @@ -591,11 +571,13 @@ fn abiAndDynamicLinkerFromFile( | ... | @@ -591,11 +571,13 @@ fn abiAndDynamicLinkerFromFile( |
| 591 | ld_info_list: []const LdInfo, | 571 | ld_info_list: []const LdInfo, |
| 592 | query: Target.Query, | 572 | query: Target.Query, |
| 593 | ) AbiAndDynamicLinkerFromFileError!Target { | 573 | ) AbiAndDynamicLinkerFromFileError!Target { |
| | 574 | assert(query.abi == null); // See the early exit in `detectAbiAndDynamicLinker`. |
| | 575 | |
| 594 | const io = file_reader.io; | 576 | const io = file_reader.io; |
| 595 | var result: Target = .{ | 577 | var result: Target = .{ |
| 596 | .cpu = cpu, | 578 | .cpu = cpu, |
| 597 | .os = os, | 579 | .os = os, |
| 598 | .abi = query.abi orelse Target.Abi.default(cpu.arch, os.tag), | 580 | .abi = Target.Abi.default(cpu.arch, os.tag), |
| 599 | .ofmt = query.ofmt orelse Target.ObjectFormat.default(os.tag, cpu.arch), | 581 | .ofmt = query.ofmt orelse Target.ObjectFormat.default(os.tag, cpu.arch), |
| 600 | .dynamic_linker = query.dynamic_linker orelse .none, | 582 | .dynamic_linker = query.dynamic_linker orelse .none, |
| 601 | }; | 583 | }; |
| ... | @@ -609,26 +591,25 @@ fn abiAndDynamicLinkerFromFile( | ... | @@ -609,26 +591,25 @@ fn abiAndDynamicLinkerFromFile( |
| 609 | .INTERP => { | 591 | .INTERP => { |
| 610 | got_dyn_section = true; | 592 | got_dyn_section = true; |
| 611 | | 593 | |
| 612 | if (look_for_ld) { | 594 | var interp: Target.DynamicLinker = .none; |
| 613 | const p_filesz = phdr.filesz; | 595 | const p_filesz = phdr.filesz; |
| 614 | if (p_filesz > result.dynamic_linker.buffer.len) return error.NameTooLong; | 596 | if (p_filesz < 2) return error.InvalidElfFile; |
| 615 | const filesz: usize = @intCast(p_filesz); | 597 | if (p_filesz > interp.buffer.len) return error.NameTooLong; |
| 616 | try file_reader.seekTo(phdr.offset); | 598 | const filesz: usize = @intCast(p_filesz); |
| 617 | try file_reader.interface.readSliceAll(result.dynamic_linker.buffer[0..filesz]); | 599 | try file_reader.seekTo(phdr.offset); |
| 618 | // PT.INTERP includes a null byte in filesz. | 600 | try file_reader.interface.readSliceAll(interp.buffer[0..filesz]); |
| 619 | const len = filesz - 1; | 601 | // PT.INTERP includes a null byte in filesz. |
| 620 | // dynamic_linker.max_byte is "max", not "len". | 602 | const len = filesz - 1; |
| 621 | // We know it will fit in u8 because we check against dynamic_linker.buffer.len above. | 603 | // dynamic_linker.max_byte is "max", not "len". |
| 622 | result.dynamic_linker.len = @intCast(len); | 604 | // We know it will fit in u8 because we check against dynamic_linker.buffer.len above. |
| 623 | | 605 | interp.len = @intCast(len); |
| 624 | // Use it to determine ABI. | 606 | |
| 625 | const full_ld_path = result.dynamic_linker.buffer[0..len]; | 607 | // Use it to determine ABI. |
| 626 | for (ld_info_list) |ld_info| { | 608 | for (ld_info_list) |ld_info| { |
| 627 | const standard_ld_basename = fs.path.basename(ld_info.ld.get().?); | 609 | if (std.mem.endsWith(u8, interp.get().?, fs.path.basename(ld_info.ld.get().?))) { |
| 628 | if (std.mem.endsWith(u8, full_ld_path, standard_ld_basename)) { | 610 | result.abi = ld_info.abi; |
| 629 | result.abi = ld_info.abi; | 611 | if (look_for_ld) result.dynamic_linker = interp; |
| 630 | break; | 612 | break; |
| 631 | } | | |
| 632 | } | 613 | } |
| 633 | } | 614 | } |
| 634 | }, | 615 | }, |
| ... | @@ -650,6 +631,10 @@ fn abiAndDynamicLinkerFromFile( | ... | @@ -650,6 +631,10 @@ fn abiAndDynamicLinkerFromFile( |
| 650 | }; | 631 | }; |
| 651 | } | 632 | } |
| 652 | | 633 | |
| | 634 | if (look_for_ld and result.dynamic_linker.get() == null) { |
| | 635 | result.dynamic_linker = .standard(cpu, os.tag, result.abi); |
| | 636 | } |
| | 637 | |
| 653 | if (!got_dyn_section) { | 638 | if (!got_dyn_section) { |
| 654 | return error.StaticElfFile; | 639 | return error.StaticElfFile; |
| 655 | } | 640 | } |
| ... | @@ -951,17 +936,14 @@ fn glibcVerFromSoFile(file_reader: *Io.File.Reader) !std.SemanticVersion { | ... | @@ -951,17 +936,14 @@ fn glibcVerFromSoFile(file_reader: *Io.File.Reader) !std.SemanticVersion { |
| 951 | /// file recursively. If that does not provide the answer, then the function falls back to | 936 | /// file recursively. If that does not provide the answer, then the function falls back to |
| 952 | /// defaults. | 937 | /// defaults. |
| 953 | fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Target.Query) !Target { | 938 | fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Target.Query) !Target { |
| 954 | const native_target_has_ld = comptime Target.DynamicLinker.kind(builtin.os.tag) != .none; | 939 | const native_os_uses_elf = comptime Target.ObjectFormat.default(builtin.target.os.tag, builtin.target.cpu.arch) == .elf; |
| 955 | const is_linux = builtin.target.os.tag == .linux; | 940 | const native_ld_needs_abi = comptime Target.DynamicLinker.kind(builtin.target.os.tag) == .arch_os_abi; |
| 956 | const is_illumos = builtin.target.os.tag == .illumos; | 941 | if (!native_os_uses_elf or !native_ld_needs_abi or query.os_tag != null or query.abi != null) { |
| 957 | const is_darwin = builtin.target.os.tag.isDarwin(); | | |
| 958 | const have_all_info = query.dynamic_linker != null and | | |
| 959 | query.abi != null and (!is_linux or query.abi.?.isGnu()); | | |
| 960 | const os_is_non_native = query.os_tag != null; | | |
| 961 | // The illumos environment is always the same. | | |
| 962 | if (!native_target_has_ld or have_all_info or os_is_non_native or is_illumos or is_darwin) { | | |
| 963 | return defaultAbiAndDynamicLinker(cpu, os, query); | 942 | return defaultAbiAndDynamicLinker(cpu, os, query); |
| 964 | } | 943 | } |
| | 944 | |
| | 945 | assert(os.tag == builtin.target.os.tag); |
| | 946 | |
| 965 | // The current target's ABI cannot be relied on for this. For example, we may build the zig | 947 | // The current target's ABI cannot be relied on for this. For example, we may build the zig |
| 966 | // compiler for target riscv64-linux-musl and provide a tarball for users to download. | 948 | // compiler for target riscv64-linux-musl and provide a tarball for users to download. |
| 967 | // A user could then run that zig compiler on riscv64-linux-gnu. This use case is well-defined | 949 | // A user could then run that zig compiler on riscv64-linux-gnu. This use case is well-defined |
| ... | @@ -979,30 +961,17 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ | ... | @@ -979,30 +961,17 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ |
| 979 | var ld_info_list_buffer: [all_abis.len]LdInfo = undefined; | 961 | var ld_info_list_buffer: [all_abis.len]LdInfo = undefined; |
| 980 | var ld_info_list_len: usize = 0; | 962 | var ld_info_list_len: usize = 0; |
| 981 | | 963 | |
| 982 | switch (Target.DynamicLinker.kind(os.tag)) { | 964 | for (all_abis) |abi| { |
| 983 | // The OS has no dynamic linker. Leave the list empty and rely on `Abi.default()` to pick | 965 | const ld: Target.DynamicLinker = .standard(cpu, os.tag, abi); |
| 984 | // something sensible in `abiAndDynamicLinkerFromFile()`. | 966 | |
| 985 | .none => {}, | 967 | // Does the generated target triple actually have a standard dynamic linker path? |
| 986 | // The OS has a system-wide dynamic linker. Unfortunately, this implies that there's no | 968 | if (ld.get() == null) continue; |
| 987 | // useful ABI information that we can glean from it merely being present. That means the | 969 | |
| 988 | // best we can do for this case (for now) is also `Abi.default()`. | 970 | ld_info_list_buffer[ld_info_list_len] = .{ |
| 989 | .arch_os => {}, | 971 | .ld = ld, |
| 990 | // The OS can have different dynamic linker paths depending on libc/ABI. In this case, we | 972 | .abi = abi, |
| 991 | // need to gather all the valid arch/OS/ABI combinations. `abiAndDynamicLinkerFromFile()` | 973 | }; |
| 992 | // will then look for a dynamic linker with a matching path on the system and pick the ABI | 974 | ld_info_list_len += 1; |
| 993 | // we associated it with here. | | |
| 994 | .arch_os_abi => for (all_abis) |abi| { | | |
| 995 | const ld = Target.DynamicLinker.standard(cpu, os.tag, abi); | | |
| 996 | | | |
| 997 | // Does the generated target triple actually have a standard dynamic linker path? | | |
| 998 | if (ld.get() == null) continue; | | |
| 999 | | | |
| 1000 | ld_info_list_buffer[ld_info_list_len] = .{ | | |
| 1001 | .ld = ld, | | |
| 1002 | .abi = abi, | | |
| 1003 | }; | | |
| 1004 | ld_info_list_len += 1; | | |
| 1005 | }, | | |
| 1006 | } | 975 | } |
| 1007 | | 976 | |
| 1008 | const ld_info_list = ld_info_list_buffer[0..ld_info_list_len]; | 977 | const ld_info_list = ld_info_list_buffer[0..ld_info_list_len]; |
| ... | @@ -1031,13 +1000,9 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ | ... | @@ -1031,13 +1000,9 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ |
| 1031 | // it uses the file it references instead, doing the same logic | 1000 | // it uses the file it references instead, doing the same logic |
| 1032 | // recursively in case it finds another shebang line. | 1001 | // recursively in case it finds another shebang line. |
| 1033 | | 1002 | |
| 1034 | var file_name: []const u8 = switch (os.tag) { | 1003 | // Since /usr/bin/env is hard-coded into the shebang line of many |
| 1035 | // Since /usr/bin/env is hard-coded into the shebang line of many | 1004 | // portable scripts, it's a reasonably reliable path to start with. |
| 1036 | // portable scripts, it's a reasonably reliable path to start with. | 1005 | var file_name: []const u8 = "/usr/bin/env"; |
| 1037 | else => "/usr/bin/env", | | |
| 1038 | // Haiku does not have a /usr root directory. | | |
| 1039 | .haiku => "/bin/env", | | |
| 1040 | }; | | |
| 1041 | | 1006 | |
| 1042 | while (true) { | 1007 | while (true) { |
| 1043 | const file = Io.Dir.openFileAbsolute(io, file_name, .{}) catch |err| switch (err) { | 1008 | const file = Io.Dir.openFileAbsolute(io, file_name, .{}) catch |err| switch (err) { |
| ... | @@ -1059,7 +1024,7 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ | ... | @@ -1059,7 +1024,7 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ |
| 1059 | error.NetworkNotFound, | 1024 | error.NetworkNotFound, |
| 1060 | error.FileTooBig, | 1025 | error.FileTooBig, |
| 1061 | error.Unexpected, | 1026 | error.Unexpected, |
| 1062 | => |e| if (e == error.FileNotFound and os.tag == .linux and mem.eql(u8, file_name, "/usr/bin/env")) { | 1027 | => |e| if (builtin.target.os.tag == .linux and e == error.FileNotFound and mem.eql(u8, file_name, "/usr/bin/env")) { |
| 1063 | // Android does not have a /usr directory, so try again | 1028 | // Android does not have a /usr directory, so try again |
| 1064 | file_name = "/system/bin/env"; | 1029 | file_name = "/system/bin/env"; |
| 1065 | continue; | 1030 | continue; |
| ... | @@ -1113,7 +1078,7 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ | ... | @@ -1113,7 +1078,7 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ |
| 1113 | }; | 1078 | }; |
| 1114 | defer file_reader.file.close(io); | 1079 | defer file_reader.file.close(io); |
| 1115 | | 1080 | |
| 1116 | return abiAndDynamicLinkerFromFile(&file_reader, &header, cpu, os, ld_info_list, query) catch |err| switch (err) { | 1081 | var result = abiAndDynamicLinkerFromFile(&file_reader, &header, cpu, os, ld_info_list, query) catch |err| switch (err) { |
| 1117 | error.FileSystem, | 1082 | error.FileSystem, |
| 1118 | error.SystemResources, | 1083 | error.SystemResources, |
| 1119 | error.SymLinkLoop, | 1084 | error.SymLinkLoop, |
| ... | @@ -1129,6 +1094,29 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ | ... | @@ -1129,6 +1094,29 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ |
| 1129 | return defaultAbiAndDynamicLinker(cpu, os, query); | 1094 | return defaultAbiAndDynamicLinker(cpu, os, query); |
| 1130 | }, | 1095 | }, |
| 1131 | }; | 1096 | }; |
| | 1097 | |
| | 1098 | if (builtin.target.os.tag == .linux and result.isBionicLibC() and query.android_api_level == null) { |
| | 1099 | result.os.version_range.linux.android = detectAndroidApiLevel(io) catch |err| return switch (err) { |
| | 1100 | error.InvalidWtf8, |
| | 1101 | error.InvalidBatchScriptArg, |
| | 1102 | => unreachable, // Windows-only |
| | 1103 | error.ApiLevelQueryFailed => |e| e, |
| | 1104 | else => blk: { |
| | 1105 | std.log.err("spawning or reading from getprop failed ({s})", .{@errorName(err)}); |
| | 1106 | switch (err) { |
| | 1107 | error.SystemResources, |
| | 1108 | error.FileSystem, |
| | 1109 | error.ProcessFdQuotaExceeded, |
| | 1110 | error.SystemFdQuotaExceeded, |
| | 1111 | error.SymLinkLoop, |
| | 1112 | => |e| break :blk e, |
| | 1113 | else => break :blk error.ApiLevelQueryFailed, |
| | 1114 | } |
| | 1115 | }, |
| | 1116 | }; |
| | 1117 | } |
| | 1118 | |
| | 1119 | return result; |
| 1132 | } | 1120 | } |
| 1133 | | 1121 | |
| 1134 | fn defaultAbiAndDynamicLinker(cpu: Target.Cpu, os: Target.Os, query: Target.Query) Target { | 1122 | fn defaultAbiAndDynamicLinker(cpu: Target.Cpu, os: Target.Os, query: Target.Query) Target { |
| ... | @@ -1166,13 +1154,6 @@ fn detectAndroidApiLevel(io: Io) !u32 { | ... | @@ -1166,13 +1154,6 @@ fn detectAndroidApiLevel(io: Io) !u32 { |
| 1166 | var stdout_buf: [92 + 1]u8 = undefined; | 1154 | var stdout_buf: [92 + 1]u8 = undefined; |
| 1167 | var reader = child.stdout.?.readerStreaming(io, &.{}); | 1155 | var reader = child.stdout.?.readerStreaming(io, &.{}); |
| 1168 | const n = try reader.interface.readSliceShort(&stdout_buf); | 1156 | const n = try reader.interface.readSliceShort(&stdout_buf); |
| 1169 | const api_level = std.fmt.parseInt(u32, stdout_buf[0 .. n - 1], 10) catch |e| { | | |
| 1170 | std.log.err( | | |
| 1171 | "Could not parse API level, unexpected getprop output '{s}' ({s})", | | |
| 1172 | .{ stdout_buf[0 .. n - 1], @errorName(e) }, | | |
| 1173 | ); | | |
| 1174 | return error.ApiLevelQueryFailed; | | |
| 1175 | }; | | |
| 1176 | | 1157 | |
| 1177 | switch (try child.wait(io)) { | 1158 | switch (try child.wait(io)) { |
| 1178 | .exited => |code| if (code != 0) { | 1159 | .exited => |code| if (code != 0) { |
| ... | @@ -1193,7 +1174,14 @@ fn detectAndroidApiLevel(io: Io) !u32 { | ... | @@ -1193,7 +1174,14 @@ fn detectAndroidApiLevel(io: Io) !u32 { |
| 1193 | }, | 1174 | }, |
| 1194 | } | 1175 | } |
| 1195 | | 1176 | |
| 1196 | return api_level; | 1177 | const result = mem.trimEnd(u8, stdout_buf[0..n], "\n"); |
| | 1178 | return std.fmt.parseInt(u32, result, 10) catch |e| { |
| | 1179 | std.log.err( |
| | 1180 | "Could not parse API level, unexpected getprop output '{s}' ({s})", |
| | 1181 | .{ result, @errorName(e) }, |
| | 1182 | ); |
| | 1183 | return error.ApiLevelQueryFailed; |
| | 1184 | }; |
| 1197 | } | 1185 | } |
| 1198 | | 1186 | |
| 1199 | test { | 1187 | test { |