| ... | ... | @@ -419,6 +419,7 @@ pub fn resolveTargetQuery(io: Io, query: Target.Query) DetectError!Target { |
| 419 | 419 | var result = detectAbiAndDynamicLinker(io, cpu, os, query) catch |err| switch (err) { |
| 420 | 420 | error.Canceled => |e| return e, |
| 421 | 421 | error.Unexpected => |e| return e, |
| 422 | error.ApiLevelQueryFailed => |e| return e, |
| 422 | 423 | error.WouldBlock => return error.Unexpected, |
| 423 | 424 | error.ConnectionResetByPeer => return error.Unexpected, |
| 424 | 425 | error.NotOpenForReading => return error.Unexpected, |
| ... | ... | @@ -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 | 501 | return result; |
| 522 | 502 | } |
| 523 | 503 | |
| ... | ... | @@ -591,11 +571,13 @@ fn abiAndDynamicLinkerFromFile( |
| 591 | 571 | ld_info_list: []const LdInfo, |
| 592 | 572 | query: Target.Query, |
| 593 | 573 | ) AbiAndDynamicLinkerFromFileError!Target { |
| 574 | assert(query.abi == null); // See the early exit in `detectAbiAndDynamicLinker`. |
| 575 | |
| 594 | 576 | const io = file_reader.io; |
| 595 | 577 | var result: Target = .{ |
| 596 | 578 | .cpu = cpu, |
| 597 | 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 | 581 | .ofmt = query.ofmt orelse Target.ObjectFormat.default(os.tag, cpu.arch), |
| 600 | 582 | .dynamic_linker = query.dynamic_linker orelse .none, |
| 601 | 583 | }; |
| ... | ... | @@ -609,26 +591,25 @@ fn abiAndDynamicLinkerFromFile( |
| 609 | 591 | .INTERP => { |
| 610 | 592 | got_dyn_section = true; |
| 611 | 593 | |
| 612 | | if (look_for_ld) { |
| 613 | | const p_filesz = phdr.filesz; |
| 614 | | if (p_filesz > result.dynamic_linker.buffer.len) return error.NameTooLong; |
| 615 | | const filesz: usize = @intCast(p_filesz); |
| 616 | | try file_reader.seekTo(phdr.offset); |
| 617 | | try file_reader.interface.readSliceAll(result.dynamic_linker.buffer[0..filesz]); |
| 618 | | // PT.INTERP includes a null byte in filesz. |
| 619 | | const len = filesz - 1; |
| 620 | | // dynamic_linker.max_byte is "max", not "len". |
| 621 | | // We know it will fit in u8 because we check against dynamic_linker.buffer.len above. |
| 622 | | result.dynamic_linker.len = @intCast(len); |
| 623 | | |
| 624 | | // Use it to determine ABI. |
| 625 | | const full_ld_path = result.dynamic_linker.buffer[0..len]; |
| 626 | | for (ld_info_list) |ld_info| { |
| 627 | | const standard_ld_basename = fs.path.basename(ld_info.ld.get().?); |
| 628 | | if (std.mem.endsWith(u8, full_ld_path, standard_ld_basename)) { |
| 629 | | result.abi = ld_info.abi; |
| 630 | | break; |
| 631 | | } |
| 594 | var interp: Target.DynamicLinker = .none; |
| 595 | const p_filesz = phdr.filesz; |
| 596 | if (p_filesz < 2) return error.InvalidElfFile; |
| 597 | if (p_filesz > interp.buffer.len) return error.NameTooLong; |
| 598 | const filesz: usize = @intCast(p_filesz); |
| 599 | try file_reader.seekTo(phdr.offset); |
| 600 | try file_reader.interface.readSliceAll(interp.buffer[0..filesz]); |
| 601 | // PT.INTERP includes a null byte in filesz. |
| 602 | const len = filesz - 1; |
| 603 | // dynamic_linker.max_byte is "max", not "len". |
| 604 | // We know it will fit in u8 because we check against dynamic_linker.buffer.len above. |
| 605 | interp.len = @intCast(len); |
| 606 | |
| 607 | // Use it to determine ABI. |
| 608 | for (ld_info_list) |ld_info| { |
| 609 | if (std.mem.endsWith(u8, interp.get().?, fs.path.basename(ld_info.ld.get().?))) { |
| 610 | result.abi = ld_info.abi; |
| 611 | if (look_for_ld) result.dynamic_linker = interp; |
| 612 | break; |
| 632 | 613 | } |
| 633 | 614 | } |
| 634 | 615 | }, |
| ... | ... | @@ -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 | 638 | if (!got_dyn_section) { |
| 654 | 639 | return error.StaticElfFile; |
| 655 | 640 | } |
| ... | ... | @@ -951,17 +936,14 @@ fn glibcVerFromSoFile(file_reader: *Io.File.Reader) !std.SemanticVersion { |
| 951 | 936 | /// file recursively. If that does not provide the answer, then the function falls back to |
| 952 | 937 | /// defaults. |
| 953 | 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; |
| 955 | | const is_linux = builtin.target.os.tag == .linux; |
| 956 | | const is_illumos = builtin.target.os.tag == .illumos; |
| 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) { |
| 939 | const native_os_uses_elf = comptime Target.ObjectFormat.default(builtin.target.os.tag, builtin.target.cpu.arch) == .elf; |
| 940 | const native_ld_needs_abi = comptime Target.DynamicLinker.kind(builtin.target.os.tag) == .arch_os_abi; |
| 941 | if (!native_os_uses_elf or !native_ld_needs_abi or query.os_tag != null or query.abi != null) { |
| 963 | 942 | return defaultAbiAndDynamicLinker(cpu, os, query); |
| 964 | 943 | } |
| 944 | |
| 945 | assert(os.tag == builtin.target.os.tag); |
| 946 | |
| 965 | 947 | // The current target's ABI cannot be relied on for this. For example, we may build the zig |
| 966 | 948 | // compiler for target riscv64-linux-musl and provide a tarball for users to download. |
| 967 | 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 | 961 | var ld_info_list_buffer: [all_abis.len]LdInfo = undefined; |
| 980 | 962 | var ld_info_list_len: usize = 0; |
| 981 | 963 | |
| 982 | | switch (Target.DynamicLinker.kind(os.tag)) { |
| 983 | | // The OS has no dynamic linker. Leave the list empty and rely on `Abi.default()` to pick |
| 984 | | // something sensible in `abiAndDynamicLinkerFromFile()`. |
| 985 | | .none => {}, |
| 986 | | // The OS has a system-wide dynamic linker. Unfortunately, this implies that there's no |
| 987 | | // useful ABI information that we can glean from it merely being present. That means the |
| 988 | | // best we can do for this case (for now) is also `Abi.default()`. |
| 989 | | .arch_os => {}, |
| 990 | | // The OS can have different dynamic linker paths depending on libc/ABI. In this case, we |
| 991 | | // need to gather all the valid arch/OS/ABI combinations. `abiAndDynamicLinkerFromFile()` |
| 992 | | // will then look for a dynamic linker with a matching path on the system and pick the ABI |
| 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 | | }, |
| 964 | for (all_abis) |abi| { |
| 965 | const ld: Target.DynamicLinker = .standard(cpu, os.tag, abi); |
| 966 | |
| 967 | // Does the generated target triple actually have a standard dynamic linker path? |
| 968 | if (ld.get() == null) continue; |
| 969 | |
| 970 | ld_info_list_buffer[ld_info_list_len] = .{ |
| 971 | .ld = ld, |
| 972 | .abi = abi, |
| 973 | }; |
| 974 | ld_info_list_len += 1; |
| 1006 | 975 | } |
| 1007 | 976 | |
| 1008 | 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 | 1000 | // it uses the file it references instead, doing the same logic |
| 1032 | 1001 | // recursively in case it finds another shebang line. |
| 1033 | 1002 | |
| 1034 | | var file_name: []const u8 = switch (os.tag) { |
| 1035 | | // Since /usr/bin/env is hard-coded into the shebang line of many |
| 1036 | | // portable scripts, it's a reasonably reliable path to start with. |
| 1037 | | else => "/usr/bin/env", |
| 1038 | | // Haiku does not have a /usr root directory. |
| 1039 | | .haiku => "/bin/env", |
| 1040 | | }; |
| 1003 | // 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. |
| 1005 | var file_name: []const u8 = "/usr/bin/env"; |
| 1041 | 1006 | |
| 1042 | 1007 | while (true) { |
| 1043 | 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 | 1024 | error.NetworkNotFound, |
| 1060 | 1025 | error.FileTooBig, |
| 1061 | 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 | 1028 | // Android does not have a /usr directory, so try again |
| 1064 | 1029 | file_name = "/system/bin/env"; |
| 1065 | 1030 | continue; |
| ... | ... | @@ -1113,7 +1078,7 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ |
| 1113 | 1078 | }; |
| 1114 | 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 | 1082 | error.FileSystem, |
| 1118 | 1083 | error.SystemResources, |
| 1119 | 1084 | error.SymLinkLoop, |
| ... | ... | @@ -1129,6 +1094,29 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ |
| 1129 | 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 | 1122 | fn defaultAbiAndDynamicLinker(cpu: Target.Cpu, os: Target.Os, query: Target.Query) Target { |
| ... | ... | @@ -1166,13 +1154,6 @@ fn detectAndroidApiLevel(io: Io) !u32 { |
| 1166 | 1154 | var stdout_buf: [92 + 1]u8 = undefined; |
| 1167 | 1155 | var reader = child.stdout.?.readerStreaming(io, &.{}); |
| 1168 | 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 | 1158 | switch (try child.wait(io)) { |
| 1178 | 1159 | .exited => |code| if (code != 0) { |
| ... | ... | @@ -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 | 1187 | test { |