authorgravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-09-17 22:28:42+02:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-09-17 22:28:42+02:00
log955228b682dc48a3815707f318280af12a3daf00
tree10899c853010cf4117778eb6d75ae287f5d77528
parent6b3b6ec32c5c45095c77f4af99ec21f7d75b82ef
parentb61678c1b631f78c3a5da4eca2e77ae3577c1093

Merge pull request '`std.Target.Query`: rework native ABI detection a bit' (#36867) from alexrp/zig:abi-detect into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/36867 Reviewed-by: Andrew Kelley <andrew@ziglang.org>

5 files changed, 143 insertions(+), 180 deletions(-)

lib/std/Target.zig+9-16
...@@ -499,7 +499,10 @@ pub const Os = struct {...@@ -499,7 +499,10 @@ pub const Os = struct {
499499
500 break :blk default_min;500 break :blk default_min;
501 },501 },
502 .android = 29,502 .android = switch (arch) {
503 .riscv64 => 35,
504 else => 29,
505 },
503 },506 },
504 },507 },
505 .rtems => .{508 .rtems => .{
...@@ -2532,8 +2535,8 @@ pub const DynamicLinker = struct {...@@ -2532,8 +2535,8 @@ pub const DynamicLinker = struct {
2532 /// the ABI; it does not necessarily mean that `abi` makes any sense at all for that platform.2535 /// the ABI; it does not necessarily mean that `abi` makes any sense at all for that platform.
2533 /// The responsibility for determining whether `abi` is valid in this case rests with the2536 /// The responsibility for determining whether `abi` is valid in this case rests with the
2534 /// caller. `Abi.default()` can be used to pick a best-effort default ABI for such platforms.2537 /// caller. `Abi.default()` can be used to pick a best-effort default ABI for such platforms.
2535 pub fn standard(cpu: Cpu, os: Os, abi: Abi) DynamicLinker {2538 pub fn standard(cpu: Cpu, os: Os.Tag, abi: Abi) DynamicLinker {
2536 return switch (os.tag) {2539 return switch (os) {
2537 .fuchsia => switch (cpu.arch) {2540 .fuchsia => switch (cpu.arch) {
2538 .arm,2541 .arm,
2539 .aarch64,2542 .aarch64,
...@@ -2833,12 +2836,7 @@ pub const DynamicLinker = struct {...@@ -2833,12 +2836,7 @@ pub const DynamicLinker = struct {
2833 else => none,2836 else => none,
2834 },2837 },
28352838
2836 .dragonfly => if (cpu.arch == .x86_64) initFmt("{s}/libexec/ld-elf.so.2", .{2839 .dragonfly => if (cpu.arch == .x86_64) init("/libexec/ld-elf.so.2") else none,
2837 if (os.version_range.semver.isAtLeast(.{ .major = 3, .minor = 8, .patch = 0 }) orelse false)
2838 ""
2839 else
2840 "/usr",
2841 }) else none,
28422840
2843 .freebsd => switch (cpu.arch) {2841 .freebsd => switch (cpu.arch) {
2844 .arm,2842 .arm,
...@@ -2849,12 +2847,7 @@ pub const DynamicLinker = struct {...@@ -2849,12 +2847,7 @@ pub const DynamicLinker = struct {
2849 .riscv64,2847 .riscv64,
2850 .x86,2848 .x86,
2851 .x86_64,2849 .x86_64,
2852 => initFmt("{s}/libexec/ld-elf.so.1", .{2850 => init("/libexec/ld-elf.so.1"),
2853 if (os.version_range.semver.isAtLeast(.{ .major = 6, .minor = 0, .patch = 0 }) orelse false)
2854 ""
2855 else
2856 "/usr",
2857 }),
2858 else => none,2851 else => none,
2859 },2852 },
28602853
...@@ -2968,7 +2961,7 @@ pub const DynamicLinker = struct {...@@ -2968,7 +2961,7 @@ pub const DynamicLinker = struct {
2968};2961};
29692962
2970pub fn standardDynamicLinkerPath(target: *const Target) DynamicLinker {2963pub fn standardDynamicLinkerPath(target: *const Target) DynamicLinker {
2971 return DynamicLinker.standard(target.cpu, target.os, target.abi);2964 return DynamicLinker.standard(target.cpu, target.os.tag, target.abi);
2972}2965}
29732966
2974pub fn ptrBitWidth_cpu_abi(cpu: Cpu, abi: Abi) u16 {2967pub fn ptrBitWidth_cpu_abi(cpu: Cpu, abi: Abi) u16 {
lib/std/Target/Query.zig+36-43
...@@ -26,33 +26,37 @@ cpu_features_sub: Target.Cpu.Feature.Set = .empty,...@@ -26,33 +26,37 @@ cpu_features_sub: Target.Cpu.Feature.Set = .empty,
26/// `null` means native.26/// `null` means native.
27os_tag: ?Target.Os.Tag = null,27os_tag: ?Target.Os.Tag = null,
2828
29/// `null` means the default version range for `os_tag`. If `os_tag` is `null` (native)29/// `null` means native when `os_tag` is native; otherwise, the default minimum version for
30/// then `null` for this field means native.30/// `cpu_arch`, `os_tag`, and `abi`.
31os_version_min: ?OsVersion = null,31os_version_min: ?OsVersion = null,
3232
33/// When cross compiling, `null` means default (latest known OS version).33/// `null` means native when `os_tag` is native; otherwise, the latest known OS version.
34/// When `os_tag` is native, `null` means equal to the native OS version.
35os_version_max: ?OsVersion = null,34os_version_max: ?OsVersion = null,
3635
37/// `null` means default when cross compiling, or native when `os_tag` is native.36/// `null` means native when `abi` is native; otherwise, the default minimum version for `cpu_arch`,
38/// If `isGnu()` is `false`, this must be `null` and is ignored.37/// `os_tag`, and `abi`.
38///
39/// Must be `null` when `abi` is `null` or `isGnu()` is `false`.
39glibc_version: ?SemanticVersion = null,40glibc_version: ?SemanticVersion = null,
4041
41/// `null` means default when cross compiling, or native when `os_tag` is native.42/// `null` means native when `abi` is native; otherwise, the default minimum version for `cpu_arch`
42/// If `isAndroid()` is `false`, this must be `null` and is ignored.43/// and `abi`.
44///
45/// Must be `null` when `abi` is `null` or `isAndroid()` is `false`.
43android_api_level: ?u32 = null,46android_api_level: ?u32 = null,
4447
45/// `null` means the native C ABI, if `os_tag` is native, otherwise it means the default C ABI.48/// `null` means native if `os_tag` is native; otherwise, best-effort default for `cpu_arch` and
49/// `os_tag`.
46abi: ?Target.Abi = null,50abi: ?Target.Abi = null,
4751
48/// When `os_tag` is `null`, then `null` means native. Otherwise it means the standard path52/// `null` means default for the `cpu_arch` and `os_tag` combination.
49/// based on the `os_tag`. When `dynamic_linker` is a non-`null` empty string, no dynamic
50/// linker is used regardless of `os_tag`.
51dynamic_linker: ?Target.DynamicLinker = null,
52
53/// `null` means default for the cpu/arch/os combo.
54ofmt: ?Target.ObjectFormat = null,53ofmt: ?Target.ObjectFormat = null,
5554
55/// `null` means native when `abi` is native; otherwise, the standard path based on `cpu_arch`,
56/// `os_tag`, and `abi`. A non-`null` empty string means no dynamic linker is used regardless of the
57/// values of other fields.
58dynamic_linker: ?Target.DynamicLinker = null,
59
56pub const CpuModel = union(enum) {60pub const CpuModel = union(enum) {
57 /// Always native61 /// Always native
58 native,62 native,
...@@ -175,12 +179,12 @@ pub const ParseOptions = struct {...@@ -175,12 +179,12 @@ pub const ParseOptions = struct {
175 /// parsed CPU Architecture. If native, then this will be "native". Otherwise, it will be "baseline".179 /// parsed CPU Architecture. If native, then this will be "native". Otherwise, it will be "baseline".
176 cpu_features: ?[]const u8 = null,180 cpu_features: ?[]const u8 = null,
177181
182 object_format: ?[]const u8 = null,
183
178 /// Absolute path to dynamic linker, to override the default, which is either a natively184 /// Absolute path to dynamic linker, to override the default, which is either a natively
179 /// detected path, or a standard path.185 /// detected path, or a standard path.
180 dynamic_linker: ?[]const u8 = null,186 dynamic_linker: ?[]const u8 = null,
181187
182 object_format: ?[]const u8 = null,
183
184 /// If this is provided, the function will populate some information about parsing failures,188 /// If this is provided, the function will populate some information about parsing failures,
185 /// so that user-friendly error messages can be delivered.189 /// so that user-friendly error messages can be delivered.
186 diagnostics: ?*Diagnostics = null,190 diagnostics: ?*Diagnostics = null,
...@@ -381,12 +385,16 @@ pub fn isNativeCpu(self: Query) bool {...@@ -381,12 +385,16 @@ pub fn isNativeCpu(self: Query) bool {
381}385}
382386
383pub fn isNativeOs(self: Query) bool {387pub fn isNativeOs(self: Query) bool {
384 return self.os_tag == null and self.os_version_min == null and self.os_version_max == null and388 return self.os_tag == null and
385 self.dynamic_linker == null and self.glibc_version == null and self.android_api_level == null;389 self.os_version_min == null and
390 self.os_version_max == null;
386}391}
387392
388pub fn isNativeAbi(self: Query) bool {393pub fn isNativeAbi(self: Query) bool {
389 return self.os_tag == null and self.abi == null;394 return self.os_tag == null and
395 self.abi == null and
396 self.glibc_version == null and
397 self.android_api_level == null;
390}398}
391399
392pub fn isNativeTriple(self: Query) bool {400pub fn isNativeTriple(self: Query) bool {
...@@ -394,11 +402,13 @@ pub fn isNativeTriple(self: Query) bool {...@@ -394,11 +402,13 @@ pub fn isNativeTriple(self: Query) bool {
394}402}
395403
396pub fn isNative(self: Query) bool {404pub fn isNative(self: Query) bool {
397 return self.isNativeTriple() and self.ofmt == null;405 return self.isNativeTriple() and
406 self.ofmt == null and
407 self.dynamic_linker == null;
398}408}
399409
400pub fn canDetectLibC(self: Query) bool {410pub fn canDetectLibC(self: Query) bool {
401 if (self.isNativeOs()) return true;411 if (self.isNativeOs() and self.isNativeAbi()) return true;
402 if (self.os_tag) |os| {412 if (self.os_tag) |os| {
403 if (builtin.os.tag == .macos and os.isDarwin()) return true;413 if (builtin.os.tag == .macos and os.isDarwin()) return true;
404 if (os == .linux) {414 if (os == .linux) {
...@@ -459,14 +469,14 @@ pub fn zigTriple(self: Query, gpa: Allocator) Allocator.Error![]u8 {...@@ -459,14 +469,14 @@ pub fn zigTriple(self: Query, gpa: Allocator) Allocator.Error![]u8 {
459 }469 }
460470
461 if (self.glibc_version) |v| {471 if (self.glibc_version) |v| {
462 const name = if (self.abi) |abi| @tagName(abi) else "gnu";472 const name = @tagName(self.abi.?);
463 try result.ensureUnusedCapacity(gpa, name.len + 2);473 try result.ensureUnusedCapacity(gpa, name.len + 2);
464 result.appendAssumeCapacity('-');474 result.appendAssumeCapacity('-');
465 result.appendSliceAssumeCapacity(name);475 result.appendSliceAssumeCapacity(name);
466 result.appendAssumeCapacity('.');476 result.appendAssumeCapacity('.');
467 try formatVersion(v, gpa, &result);477 try formatVersion(v, gpa, &result);
468 } else if (self.android_api_level) |lvl| {478 } else if (self.android_api_level) |lvl| {
469 const name = if (self.abi) |abi| @tagName(abi) else "android";479 const name = @tagName(self.abi.?);
470 try result.ensureUnusedCapacity(gpa, name.len + 2);480 try result.ensureUnusedCapacity(gpa, name.len + 2);
471 result.appendAssumeCapacity('-');481 result.appendAssumeCapacity('-');
472 result.appendSliceAssumeCapacity(name);482 result.appendSliceAssumeCapacity(name);
...@@ -538,6 +548,7 @@ pub fn allocDescription(self: Query, allocator: Allocator) ![]u8 {...@@ -538,6 +548,7 @@ pub fn allocDescription(self: Query, allocator: Allocator) ![]u8 {
538 return self.zigTriple(allocator);548 return self.zigTriple(allocator);
539}549}
540550
551/// Deprecated; set `glibc_version` directly. Will be removed in 0.18.0.
541pub fn setGnuLibCVersion(self: *Query, major: u32, minor: u32, patch: u32) void {552pub fn setGnuLibCVersion(self: *Query, major: u32, minor: u32, patch: u32) void {
542 self.glibc_version = SemanticVersion{ .major = major, .minor = minor, .patch = patch };553 self.glibc_version = SemanticVersion{ .major = major, .minor = minor, .patch = patch };
543}554}
...@@ -599,8 +610,8 @@ pub fn eql(a: Query, b: Query) bool {...@@ -599,8 +610,8 @@ pub fn eql(a: Query, b: Query) bool {
599 if (!versionEqualOpt(a.glibc_version, b.glibc_version)) return false;610 if (!versionEqualOpt(a.glibc_version, b.glibc_version)) return false;
600 if (a.android_api_level != b.android_api_level) return false;611 if (a.android_api_level != b.android_api_level) return false;
601 if (a.abi != b.abi) return false;612 if (a.abi != b.abi) return false;
602 if (!dynamicLinkerEqualOpt(a.dynamic_linker, b.dynamic_linker)) return false;
603 if (a.ofmt != b.ofmt) return false;613 if (a.ofmt != b.ofmt) return false;
614 if (!dynamicLinkerEqualOpt(a.dynamic_linker, b.dynamic_linker)) return false;
604615
605 return true;616 return true;
606}617}
...@@ -620,24 +631,6 @@ fn dynamicLinkerEqualOpt(a: ?Target.DynamicLinker, b: ?Target.DynamicLinker) boo...@@ -620,24 +631,6 @@ fn dynamicLinkerEqualOpt(a: ?Target.DynamicLinker, b: ?Target.DynamicLinker) boo
620test parse {631test parse {
621 const io = std.testing.io;632 const io = std.testing.io;
622633
623 if (builtin.target.isGnuLibC()) {
624 var query = try Query.parse(.{});
625 query.setGnuLibCVersion(2, 1, 1);
626
627 const text = try query.zigTriple(std.testing.allocator);
628 defer std.testing.allocator.free(text);
629
630 try std.testing.expectEqualSlices(u8, "native-native-gnu.2.1.1", text);
631 }
632 if (builtin.target.abi.isAndroid()) {
633 var query = try Query.parse(.{});
634 query.android_api_level = 30;
635
636 const text = try query.zigTriple(std.testing.allocator);
637 defer std.testing.allocator.free(text);
638
639 try std.testing.expectEqualSlices(u8, "native-native-android.30", text);
640 }
641 {634 {
642 const query = try Query.parse(.{635 const query = try Query.parse(.{
643 .arch_os_abi = "aarch64-linux",636 .arch_os_abi = "aarch64-linux",
lib/std/zig/system.zig+92-107
...@@ -374,16 +374,14 @@ pub fn resolveTargetQuery(io: Io, query: Target.Query) DetectError!Target {...@@ -374,16 +374,14 @@ pub fn resolveTargetQuery(io: Io, query: Target.Query) DetectError!Target {
374 }374 }
375375
376 var cpu = switch (query.cpu_model) {376 var cpu = switch (query.cpu_model) {
377 .native => detectNativeCpuAndFeatures(io, query_cpu_arch, os, query),377 .native => detectNativeCpuAndFeatures(io, query_cpu_arch),
378 .baseline => Target.Cpu.baseline(query_cpu_arch, os),378 .baseline => null,
379 .determined_by_arch_os => if (query.cpu_arch == null)379 .determined_by_arch_os => if (query.cpu_arch == null)
380 detectNativeCpuAndFeatures(io, query_cpu_arch, os, query)380 detectNativeCpuAndFeatures(io, query_cpu_arch)
381 else381 else
382 Target.Cpu.baseline(query_cpu_arch, os),382 null,
383 .explicit => |model| model.toCpu(query_cpu_arch),383 .explicit => |model| model.toCpu(query_cpu_arch),
384 } orelse backup_cpu_detection: {384 } orelse Target.Cpu.baseline(query_cpu_arch, os);
385 break :backup_cpu_detection Target.Cpu.baseline(query_cpu_arch, os);
386 };
387385
388 // For x86, we need to populate some CPU feature flags depending on architecture386 // For x86, we need to populate some CPU feature flags depending on architecture
389 // and mode:387 // and mode:
...@@ -421,6 +419,7 @@ pub fn resolveTargetQuery(io: Io, query: Target.Query) DetectError!Target {...@@ -421,6 +419,7 @@ pub fn resolveTargetQuery(io: Io, query: Target.Query) DetectError!Target {
421 var result = detectAbiAndDynamicLinker(io, cpu, os, query) catch |err| switch (err) {419 var result = detectAbiAndDynamicLinker(io, cpu, os, query) catch |err| switch (err) {
422 error.Canceled => |e| return e,420 error.Canceled => |e| return e,
423 error.Unexpected => |e| return e,421 error.Unexpected => |e| return e,
422 error.ApiLevelQueryFailed => |e| return e,
424 error.WouldBlock => return error.Unexpected,423 error.WouldBlock => return error.Unexpected,
425 error.ConnectionResetByPeer => return error.Unexpected,424 error.ConnectionResetByPeer => return error.Unexpected,
426 error.NotOpenForReading => return error.Unexpected,425 error.NotOpenForReading => return error.Unexpected,
...@@ -499,27 +498,6 @@ pub fn resolveTargetQuery(io: Io, query: Target.Query) DetectError!Target {...@@ -499,27 +498,6 @@ pub fn resolveTargetQuery(io: Io, query: Target.Query) DetectError!Target {
499 }498 }
500 }499 }
501500
502 if (builtin.os.tag == .linux and result.isBionicLibC() and query.os_tag == null and query.android_api_level == null) {
503 result.os.version_range.linux.android = detectAndroidApiLevel(io) catch |err| return switch (err) {
504 error.InvalidWtf8,
505 error.InvalidBatchScriptArg,
506 => unreachable, // Windows-only
507 error.ApiLevelQueryFailed => |e| e,
508 else => blk: {
509 std.log.err("spawning or reading from getprop failed ({s})", .{@errorName(err)});
510 switch (err) {
511 error.SystemResources,
512 error.FileSystem,
513 error.ProcessFdQuotaExceeded,
514 error.SystemFdQuotaExceeded,
515 error.SymLinkLoop,
516 => |e| break :blk e,
517 else => break :blk error.ApiLevelQueryFailed,
518 }
519 },
520 };
521 }
522
523 return result;501 return result;
524}502}
525503
...@@ -535,13 +513,12 @@ fn updateCpuFeatures(...@@ -535,13 +513,12 @@ fn updateCpuFeatures(
535 set.removeFeatureSet(sub_set);513 set.removeFeatureSet(sub_set);
536}514}
537515
538fn detectNativeCpuAndFeatures(io: Io, cpu_arch: Target.Cpu.Arch, os: Target.Os, query: Target.Query) ?Target.Cpu {516fn detectNativeCpuAndFeatures(io: Io, cpu_arch: Target.Cpu.Arch) ?Target.Cpu {
539 // Here we switch on a comptime value rather than `cpu_arch`. This is valid because `cpu_arch`,517 const family = builtin.target.cpu.arch.family();
540 // although it is a runtime value, is guaranteed to be one of the architectures in the set518 assert(cpu_arch.family() == family);
541 // of the respective switch prong.519 switch (family) {
542 switch (builtin.cpu.arch) {520 .loongarch => return @import("system/loongarch.zig").detectNativeCpuAndFeatures(cpu_arch),
543 .loongarch32, .loongarch64 => return @import("system/loongarch.zig").detectNativeCpuAndFeatures(cpu_arch, os, query),521 .x86 => return @import("system/x86.zig").detectNativeCpuAndFeatures(cpu_arch),
544 .x86_64, .x86 => return @import("system/x86.zig").detectNativeCpuAndFeatures(cpu_arch, os, query),
545 else => {},522 else => {},
546 }523 }
547524
...@@ -594,11 +571,13 @@ fn abiAndDynamicLinkerFromFile(...@@ -594,11 +571,13 @@ fn abiAndDynamicLinkerFromFile(
594 ld_info_list: []const LdInfo,571 ld_info_list: []const LdInfo,
595 query: Target.Query,572 query: Target.Query,
596) AbiAndDynamicLinkerFromFileError!Target {573) AbiAndDynamicLinkerFromFileError!Target {
574 assert(query.abi == null); // See the early exit in `detectAbiAndDynamicLinker`.
575
597 const io = file_reader.io;576 const io = file_reader.io;
598 var result: Target = .{577 var result: Target = .{
599 .cpu = cpu,578 .cpu = cpu,
600 .os = os,579 .os = os,
601 .abi = query.abi orelse Target.Abi.default(cpu.arch, os.tag),580 .abi = Target.Abi.default(cpu.arch, os.tag),
602 .ofmt = query.ofmt orelse Target.ObjectFormat.default(os.tag, cpu.arch),581 .ofmt = query.ofmt orelse Target.ObjectFormat.default(os.tag, cpu.arch),
603 .dynamic_linker = query.dynamic_linker orelse .none,582 .dynamic_linker = query.dynamic_linker orelse .none,
604 };583 };
...@@ -612,26 +591,25 @@ fn abiAndDynamicLinkerFromFile(...@@ -612,26 +591,25 @@ fn abiAndDynamicLinkerFromFile(
612 .INTERP => {591 .INTERP => {
613 got_dyn_section = true;592 got_dyn_section = true;
614593
615 if (look_for_ld) {594 var interp: Target.DynamicLinker = .none;
616 const p_filesz = phdr.filesz;595 const p_filesz = phdr.filesz;
617 if (p_filesz > result.dynamic_linker.buffer.len) return error.NameTooLong;596 if (p_filesz < 2) return error.InvalidElfFile;
618 const filesz: usize = @intCast(p_filesz);597 if (p_filesz > interp.buffer.len) return error.NameTooLong;
619 try file_reader.seekTo(phdr.offset);598 const filesz: usize = @intCast(p_filesz);
620 try file_reader.interface.readSliceAll(result.dynamic_linker.buffer[0..filesz]);599 try file_reader.seekTo(phdr.offset);
621 // PT.INTERP includes a null byte in filesz.600 try file_reader.interface.readSliceAll(interp.buffer[0..filesz]);
622 const len = filesz - 1;601 // PT.INTERP includes a null byte in filesz.
623 // dynamic_linker.max_byte is "max", not "len".602 const len = filesz - 1;
624 // 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".
625 result.dynamic_linker.len = @intCast(len);604 // We know it will fit in u8 because we check against dynamic_linker.buffer.len above.
626605 interp.len = @intCast(len);
627 // Use it to determine ABI.606
628 const full_ld_path = result.dynamic_linker.buffer[0..len];607 // Use it to determine ABI.
629 for (ld_info_list) |ld_info| {608 for (ld_info_list) |ld_info| {
630 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().?))) {
631 if (std.mem.endsWith(u8, full_ld_path, standard_ld_basename)) {610 result.abi = ld_info.abi;
632 result.abi = ld_info.abi;611 if (look_for_ld) result.dynamic_linker = interp;
633 break;612 break;
634 }
635 }613 }
636 }614 }
637 },615 },
...@@ -653,6 +631,10 @@ fn abiAndDynamicLinkerFromFile(...@@ -653,6 +631,10 @@ fn abiAndDynamicLinkerFromFile(
653 };631 };
654 }632 }
655633
634 if (look_for_ld and result.dynamic_linker.get() == null) {
635 result.dynamic_linker = .standard(cpu, os.tag, result.abi);
636 }
637
656 if (!got_dyn_section) {638 if (!got_dyn_section) {
657 return error.StaticElfFile;639 return error.StaticElfFile;
658 }640 }
...@@ -954,17 +936,14 @@ fn glibcVerFromSoFile(file_reader: *Io.File.Reader) !std.SemanticVersion {...@@ -954,17 +936,14 @@ fn glibcVerFromSoFile(file_reader: *Io.File.Reader) !std.SemanticVersion {
954/// file recursively. If that does not provide the answer, then the function falls back to936/// file recursively. If that does not provide the answer, then the function falls back to
955/// defaults.937/// defaults.
956fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Target.Query) !Target {938fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Target.Query) !Target {
957 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;
958 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;
959 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) {
960 const is_darwin = builtin.target.os.tag.isDarwin();
961 const have_all_info = query.dynamic_linker != null and
962 query.abi != null and (!is_linux or query.abi.?.isGnu());
963 const os_is_non_native = query.os_tag != null;
964 // The illumos environment is always the same.
965 if (!native_target_has_ld or have_all_info or os_is_non_native or is_illumos or is_darwin) {
966 return defaultAbiAndDynamicLinker(cpu, os, query);942 return defaultAbiAndDynamicLinker(cpu, os, query);
967 }943 }
944
945 assert(os.tag == builtin.target.os.tag);
946
968 // The current target's ABI cannot be relied on for this. For example, we may build the zig947 // The current target's ABI cannot be relied on for this. For example, we may build the zig
969 // 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.
970 // A user could then run that zig compiler on riscv64-linux-gnu. This use case is well-defined949 // A user could then run that zig compiler on riscv64-linux-gnu. This use case is well-defined
...@@ -982,30 +961,17 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ...@@ -982,30 +961,17 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ
982 var ld_info_list_buffer: [all_abis.len]LdInfo = undefined;961 var ld_info_list_buffer: [all_abis.len]LdInfo = undefined;
983 var ld_info_list_len: usize = 0;962 var ld_info_list_len: usize = 0;
984963
985 switch (Target.DynamicLinker.kind(os.tag)) {964 for (all_abis) |abi| {
986 // The OS has no dynamic linker. Leave the list empty and rely on `Abi.default()` to pick965 const ld: Target.DynamicLinker = .standard(cpu, os.tag, abi);
987 // something sensible in `abiAndDynamicLinkerFromFile()`.966
988 .none => {},967 // Does the generated target triple actually have a standard dynamic linker path?
989 // The OS has a system-wide dynamic linker. Unfortunately, this implies that there's no968 if (ld.get() == null) continue;
990 // useful ABI information that we can glean from it merely being present. That means the969
991 // best we can do for this case (for now) is also `Abi.default()`.970 ld_info_list_buffer[ld_info_list_len] = .{
992 .arch_os => {},971 .ld = ld,
993 // The OS can have different dynamic linker paths depending on libc/ABI. In this case, we972 .abi = abi,
994 // need to gather all the valid arch/OS/ABI combinations. `abiAndDynamicLinkerFromFile()`973 };
995 // will then look for a dynamic linker with a matching path on the system and pick the ABI974 ld_info_list_len += 1;
996 // we associated it with here.
997 .arch_os_abi => for (all_abis) |abi| {
998 const ld = Target.DynamicLinker.standard(cpu, os, abi);
999
1000 // Does the generated target triple actually have a standard dynamic linker path?
1001 if (ld.get() == null) continue;
1002
1003 ld_info_list_buffer[ld_info_list_len] = .{
1004 .ld = ld,
1005 .abi = abi,
1006 };
1007 ld_info_list_len += 1;
1008 },
1009 }975 }
1010976
1011 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];
...@@ -1034,13 +1000,9 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ...@@ -1034,13 +1000,9 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ
1034 // it uses the file it references instead, doing the same logic1000 // it uses the file it references instead, doing the same logic
1035 // recursively in case it finds another shebang line.1001 // recursively in case it finds another shebang line.
10361002
1037 var file_name: []const u8 = switch (os.tag) {1003 // Since /usr/bin/env is hard-coded into the shebang line of many
1038 // Since /usr/bin/env is hard-coded into the shebang line of many1004 // portable scripts, it's a reasonably reliable path to start with.
1039 // portable scripts, it's a reasonably reliable path to start with.1005 var file_name: []const u8 = "/usr/bin/env";
1040 else => "/usr/bin/env",
1041 // Haiku does not have a /usr root directory.
1042 .haiku => "/bin/env",
1043 };
10441006
1045 while (true) {1007 while (true) {
1046 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) {
...@@ -1062,7 +1024,7 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ...@@ -1062,7 +1024,7 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ
1062 error.NetworkNotFound,1024 error.NetworkNotFound,
1063 error.FileTooBig,1025 error.FileTooBig,
1064 error.Unexpected,1026 error.Unexpected,
1065 => |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")) {
1066 // Android does not have a /usr directory, so try again1028 // Android does not have a /usr directory, so try again
1067 file_name = "/system/bin/env";1029 file_name = "/system/bin/env";
1068 continue;1030 continue;
...@@ -1116,7 +1078,7 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ...@@ -1116,7 +1078,7 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ
1116 };1078 };
1117 defer file_reader.file.close(io);1079 defer file_reader.file.close(io);
11181080
1119 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) {
1120 error.FileSystem,1082 error.FileSystem,
1121 error.SystemResources,1083 error.SystemResources,
1122 error.SymLinkLoop,1084 error.SymLinkLoop,
...@@ -1132,6 +1094,29 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ...@@ -1132,6 +1094,29 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ
1132 return defaultAbiAndDynamicLinker(cpu, os, query);1094 return defaultAbiAndDynamicLinker(cpu, os, query);
1133 },1095 },
1134 };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;
1135}1120}
11361121
1137fn defaultAbiAndDynamicLinker(cpu: Target.Cpu, os: Target.Os, query: Target.Query) Target {1122fn defaultAbiAndDynamicLinker(cpu: Target.Cpu, os: Target.Os, query: Target.Query) Target {
...@@ -1141,7 +1126,7 @@ fn defaultAbiAndDynamicLinker(cpu: Target.Cpu, os: Target.Os, query: Target.Quer...@@ -1141,7 +1126,7 @@ fn defaultAbiAndDynamicLinker(cpu: Target.Cpu, os: Target.Os, query: Target.Quer
1141 .os = os,1126 .os = os,
1142 .abi = abi,1127 .abi = abi,
1143 .ofmt = query.ofmt orelse Target.ObjectFormat.default(os.tag, cpu.arch),1128 .ofmt = query.ofmt orelse Target.ObjectFormat.default(os.tag, cpu.arch),
1144 .dynamic_linker = query.dynamic_linker orelse .standard(cpu, os, abi),1129 .dynamic_linker = query.dynamic_linker orelse .standard(cpu, os.tag, abi),
1145 };1130 };
1146}1131}
11471132
...@@ -1169,13 +1154,6 @@ fn detectAndroidApiLevel(io: Io) !u32 {...@@ -1169,13 +1154,6 @@ fn detectAndroidApiLevel(io: Io) !u32 {
1169 var stdout_buf: [92 + 1]u8 = undefined;1154 var stdout_buf: [92 + 1]u8 = undefined;
1170 var reader = child.stdout.?.readerStreaming(io, &.{});1155 var reader = child.stdout.?.readerStreaming(io, &.{});
1171 const n = try reader.interface.readSliceShort(&stdout_buf);1156 const n = try reader.interface.readSliceShort(&stdout_buf);
1172 const api_level = std.fmt.parseInt(u32, stdout_buf[0 .. n - 1], 10) catch |e| {
1173 std.log.err(
1174 "Could not parse API level, unexpected getprop output '{s}' ({s})",
1175 .{ stdout_buf[0 .. n - 1], @errorName(e) },
1176 );
1177 return error.ApiLevelQueryFailed;
1178 };
11791157
1180 switch (try child.wait(io)) {1158 switch (try child.wait(io)) {
1181 .exited => |code| if (code != 0) {1159 .exited => |code| if (code != 0) {
...@@ -1196,7 +1174,14 @@ fn detectAndroidApiLevel(io: Io) !u32 {...@@ -1196,7 +1174,14 @@ fn detectAndroidApiLevel(io: Io) !u32 {
1196 },1174 },
1197 }1175 }
11981176
1199 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 };
1200}1185}
12011186
1202test {1187test {
lib/std/zig/system/loongarch.zig+1-8
...@@ -18,14 +18,7 @@ fn setFeature(cpu: *std.Target.Cpu, feature: std.Target.loongarch.Feature, enabl...@@ -18,14 +18,7 @@ fn setFeature(cpu: *std.Target.Cpu, feature: std.Target.loongarch.Feature, enabl
18 if (enabled) cpu.features.addFeature(idx) else cpu.features.removeFeature(idx);18 if (enabled) cpu.features.addFeature(idx) else cpu.features.removeFeature(idx);
19}19}
2020
21pub fn detectNativeCpuAndFeatures(21pub fn detectNativeCpuAndFeatures(arch: std.Target.Cpu.Arch) ?std.Target.Cpu {
22 arch: std.Target.Cpu.Arch,
23 os: std.Target.Os,
24 query: std.Target.Query,
25) ?std.Target.Cpu {
26 _ = os;
27 _ = query;
28
29 const variant: Variant = @fromBackingInt(@intCast(cpucfg(1) & 0b11));22 const variant: Variant = @fromBackingInt(@intCast(cpucfg(1) & 0b11));
3023
31 var cpu: std.Target.Cpu = .{24 var cpu: std.Target.Cpu = .{
lib/std/zig/system/x86.zig+5-6
...@@ -41,8 +41,7 @@ inline fn hasMask(input: u32, mask: u32) bool {...@@ -41,8 +41,7 @@ inline fn hasMask(input: u32, mask: u32) bool {
41 return (input & mask) == mask;41 return (input & mask) == mask;
42}42}
4343
44pub fn detectNativeCpuAndFeatures(arch: Target.Cpu.Arch, os: Target.Os, query: Target.Query) Target.Cpu {44pub fn detectNativeCpuAndFeatures(arch: Target.Cpu.Arch) Target.Cpu {
45 _ = query;
46 var cpu = Target.Cpu{45 var cpu = Target.Cpu{
47 .arch = arch,46 .arch = arch,
48 .model = Target.Cpu.Model.generic(arch),47 .model = Target.Cpu.Model.generic(arch),
...@@ -50,7 +49,7 @@ pub fn detectNativeCpuAndFeatures(arch: Target.Cpu.Arch, os: Target.Os, query: T...@@ -50,7 +49,7 @@ pub fn detectNativeCpuAndFeatures(arch: Target.Cpu.Arch, os: Target.Os, query: T
50 };49 };
5150
52 // First we detect features, to use as hints when detecting CPU Model.51 // First we detect features, to use as hints when detecting CPU Model.
53 detectNativeFeatures(&cpu, os.tag);52 detectNativeFeatures(&cpu);
5453
55 var leaf = cpuid(0, 0);54 var leaf = cpuid(0, 0);
56 const max_leaf = leaf.eax;55 const max_leaf = leaf.eax;
...@@ -90,7 +89,7 @@ pub fn detectNativeCpuAndFeatures(arch: Target.Cpu.Arch, os: Target.Os, query: T...@@ -90,7 +89,7 @@ pub fn detectNativeCpuAndFeatures(arch: Target.Cpu.Arch, os: Target.Os, query: T
90 // Add the CPU model's feature set into the working set, but then89 // Add the CPU model's feature set into the working set, but then
91 // override with actual detected features again.90 // override with actual detected features again.
92 cpu.features.addFeatureSet(cpu.model.features);91 cpu.features.addFeatureSet(cpu.model.features);
93 detectNativeFeatures(&cpu, os.tag);92 detectNativeFeatures(&cpu);
9493
95 cpu.features.populateDependencies(cpu.arch.allFeaturesList());94 cpu.features.populateDependencies(cpu.arch.allFeaturesList());
9695
...@@ -215,7 +214,7 @@ fn detectAmdProcessor(cpu: *const Target.Cpu, family: u32, model: u32) ?*const T...@@ -215,7 +214,7 @@ fn detectAmdProcessor(cpu: *const Target.Cpu, family: u32, model: u32) ?*const T
215 };214 };
216}215}
217216
218fn detectNativeFeatures(cpu: *Target.Cpu, os_tag: Target.Os.Tag) void {217fn detectNativeFeatures(cpu: *Target.Cpu) void {
219 var leaf = cpuid(0, 0);218 var leaf = cpuid(0, 0);
220219
221 const max_level = leaf.eax;220 const max_level = leaf.eax;
...@@ -268,7 +267,7 @@ fn detectNativeFeatures(cpu: *Target.Cpu, os_tag: Target.Os.Tag) void {...@@ -268,7 +267,7 @@ fn detectNativeFeatures(cpu: *Target.Cpu, os_tag: Target.Os.Tag) void {
268 // Darwin lazily saves the AVX512 context on first use: trust that the OS will267 // Darwin lazily saves the AVX512 context on first use: trust that the OS will
269 // save the AVX512 context if we use AVX512 instructions, even if the bit is not268 // save the AVX512 context if we use AVX512 instructions, even if the bit is not
270 // set right now.269 // set right now.
271 const has_avx512_save = if (os_tag.isDarwin())270 const has_avx512_save = if (builtin.target.os.tag.isDarwin())
272 true271 true
273 else272 else
274 xcr0.zmm_hi256 and xcr0.hi16_zmm;273 xcr0.zmm_hi256 and xcr0.hi16_zmm;