authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-12-04 15:26:57-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-01-01 17:51:18-07:00
logdbdb87502d6936597424fe86842c15978ba86918
tree8e9fc73fad1d855368a85b7bceab4a725494b47b
parent3179f58c414b5e4845b9bf3acdf276fe8e2b88a0

std.Target: add DynamicLinker


16 files changed, 1280 insertions(+), 1295 deletions(-)

CMakeLists.txt-1
...@@ -516,7 +516,6 @@ set(ZIG_STAGE2_SOURCES...@@ -516,7 +516,6 @@ set(ZIG_STAGE2_SOURCES
516 "${CMAKE_SOURCE_DIR}/lib/std/zig/string_literal.zig"516 "${CMAKE_SOURCE_DIR}/lib/std/zig/string_literal.zig"
517 "${CMAKE_SOURCE_DIR}/lib/std/zig/system.zig"517 "${CMAKE_SOURCE_DIR}/lib/std/zig/system.zig"
518 "${CMAKE_SOURCE_DIR}/lib/std/zig/system/NativePaths.zig"518 "${CMAKE_SOURCE_DIR}/lib/std/zig/system/NativePaths.zig"
519 "${CMAKE_SOURCE_DIR}/lib/std/zig/system/NativeTargetInfo.zig"
520 "${CMAKE_SOURCE_DIR}/lib/std/zig/system/x86.zig"519 "${CMAKE_SOURCE_DIR}/lib/std/zig/system/x86.zig"
521 "${CMAKE_SOURCE_DIR}/lib/std/zig/tokenizer.zig"520 "${CMAKE_SOURCE_DIR}/lib/std/zig/tokenizer.zig"
522 "${CMAKE_SOURCE_DIR}/src/Air.zig"521 "${CMAKE_SOURCE_DIR}/src/Air.zig"
lib/build_runner.zig+1-3
...@@ -46,11 +46,9 @@ pub fn main() !void {...@@ -46,11 +46,9 @@ pub fn main() !void {
46 return error.InvalidArgs;46 return error.InvalidArgs;
47 };47 };
4848
49 const detected = try std.zig.system.NativeTargetInfo.detect(.{});
50 const host: std.Build.ResolvedTarget = .{49 const host: std.Build.ResolvedTarget = .{
51 .query = .{},50 .query = .{},
52 .target = detected.target,51 .target = try std.zig.system.resolveTargetQuery(.{}),
53 .dynamic_linker = detected.dynamic_linker,
54 };52 };
5553
56 const build_root_directory: std.Build.Cache.Directory = .{54 const build_root_directory: std.Build.Cache.Directory = .{
lib/std/Build.zig+2-13
...@@ -2129,14 +2129,6 @@ pub fn hex64(x: u64) [16]u8 {...@@ -2129,14 +2129,6 @@ pub fn hex64(x: u64) [16]u8 {
2129pub const ResolvedTarget = struct {2129pub const ResolvedTarget = struct {
2130 query: Target.Query,2130 query: Target.Query,
2131 target: Target,2131 target: Target,
2132 dynamic_linker: Target.DynamicLinker,
2133
2134 pub fn toNativeTargetInfo(self: ResolvedTarget) std.zig.system.NativeTargetInfo {
2135 return .{
2136 .target = self.target,
2137 .dynamic_linker = self.dynamic_linker,
2138 };
2139 }
2140};2132};
21412133
2142/// Converts a target query into a fully resolved target that can be passed to2134/// Converts a target query into a fully resolved target that can be passed to
...@@ -2146,13 +2138,10 @@ pub fn resolveTargetQuery(b: *Build, query: Target.Query) ResolvedTarget {...@@ -2146,13 +2138,10 @@ pub fn resolveTargetQuery(b: *Build, query: Target.Query) ResolvedTarget {
2146 // resolved via a WASI API or via the build protocol.2138 // resolved via a WASI API or via the build protocol.
2147 _ = b;2139 _ = b;
21482140
2149 const result = std.zig.system.NativeTargetInfo.detect(query) catch
2150 @panic("unable to resolve target query");
2151
2152 return .{2141 return .{
2153 .query = query,2142 .query = query,
2154 .target = result.target,2143 .target = std.zig.system.resolveTargetQuery(query) catch
2155 .dynamic_linker = result.dynamic_linker,2144 @panic("unable to resolve target query"),
2156 };2145 };
2157}2146}
21582147
lib/std/Build/Module.zig-1
...@@ -746,5 +746,4 @@ const Module = @This();...@@ -746,5 +746,4 @@ const Module = @This();
746const std = @import("std");746const std = @import("std");
747const assert = std.debug.assert;747const assert = std.debug.assert;
748const LazyPath = std.Build.LazyPath;748const LazyPath = std.Build.LazyPath;
749const NativeTargetInfo = std.zig.system.NativeTargetInfo;
750const Step = std.Build.Step;749const Step = std.Build.Step;
lib/std/Build/Step/Compile.zig-1
...@@ -9,7 +9,6 @@ const StringHashMap = std.StringHashMap;...@@ -9,7 +9,6 @@ const StringHashMap = std.StringHashMap;
9const Sha256 = std.crypto.hash.sha2.Sha256;9const Sha256 = std.crypto.hash.sha2.Sha256;
10const Allocator = mem.Allocator;10const Allocator = mem.Allocator;
11const Step = std.Build.Step;11const Step = std.Build.Step;
12const NativeTargetInfo = std.zig.system.NativeTargetInfo;
13const LazyPath = std.Build.LazyPath;12const LazyPath = std.Build.LazyPath;
14const PkgConfigPkg = std.Build.PkgConfigPkg;13const PkgConfigPkg = std.Build.PkgConfigPkg;
15const PkgConfigError = std.Build.PkgConfigError;14const PkgConfigError = std.Build.PkgConfigError;
lib/std/Build/Step/Options.zig+1-3
...@@ -294,11 +294,9 @@ test Options {...@@ -294,11 +294,9 @@ test Options {
294 var arena = std.heap.ArenaAllocator.init(std.testing.allocator);294 var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
295 defer arena.deinit();295 defer arena.deinit();
296296
297 const detected = try std.zig.system.NativeTargetInfo.detect(.{});
298 const host: std.Build.ResolvedTarget = .{297 const host: std.Build.ResolvedTarget = .{
299 .query = .{},298 .query = .{},
300 .target = detected.target,299 .target = try std.zig.system.resolveTargetQuery(.{}),
301 .dynamic_linker = detected.dynamic_linker,
302 };300 };
303301
304 var cache: std.Build.Cache = .{302 var cache: std.Build.Cache = .{
lib/std/Build/Step/Run.zig+3-3
...@@ -678,8 +678,8 @@ fn runCommand(...@@ -678,8 +678,8 @@ fn runCommand(
678678
679 const need_cross_glibc = exe.rootModuleTarget().isGnuLibC() and679 const need_cross_glibc = exe.rootModuleTarget().isGnuLibC() and
680 exe.is_linking_libc;680 exe.is_linking_libc;
681 const other_target_info = exe.root_module.target.?.toNativeTargetInfo();681 const other_target = exe.root_module.target.?.target;
682 switch (b.host.toNativeTargetInfo().getExternalExecutor(&other_target_info, .{682 switch (std.zig.system.getExternalExecutor(b.host.target, &other_target, .{
683 .qemu_fixes_dl = need_cross_glibc and b.glibc_runtimes_dir != null,683 .qemu_fixes_dl = need_cross_glibc and b.glibc_runtimes_dir != null,
684 .link_libc = exe.is_linking_libc,684 .link_libc = exe.is_linking_libc,
685 })) {685 })) {
...@@ -752,7 +752,7 @@ fn runCommand(...@@ -752,7 +752,7 @@ fn runCommand(
752 .bad_dl => |foreign_dl| {752 .bad_dl => |foreign_dl| {
753 if (allow_skip) return error.MakeSkipped;753 if (allow_skip) return error.MakeSkipped;
754754
755 const host_dl = b.host.dynamic_linker.get() orelse "(none)";755 const host_dl = b.host.target.dynamic_linker.get() orelse "(none)";
756756
757 return step.fail(757 return step.fail(
758 \\the host system is unable to execute binaries from the target758 \\the host system is unable to execute binaries from the target
lib/std/Target.zig+46-26
...@@ -1,7 +1,13 @@...@@ -1,7 +1,13 @@
1//! All the details about the machine that will be executing code.
2//! Unlike `Query` which might leave some things as "default" or "host", this
3//! data is fully resolved into a concrete set of OS versions, CPU features,
4//! etc.
5
1cpu: Cpu,6cpu: Cpu,
2os: Os,7os: Os,
3abi: Abi,8abi: Abi,
4ofmt: ObjectFormat,9ofmt: ObjectFormat,
10dynamic_linker: DynamicLinker = DynamicLinker.none,
511
6pub const Query = @import("Target/Query.zig");12pub const Query = @import("Target/Query.zig");
713
...@@ -1529,13 +1535,19 @@ pub inline fn hasDynamicLinker(self: Target) bool {...@@ -1529,13 +1535,19 @@ pub inline fn hasDynamicLinker(self: Target) bool {
1529}1535}
15301536
1531pub const DynamicLinker = struct {1537pub const DynamicLinker = struct {
1532 /// Contains the memory used to store the dynamic linker path. This field should1538 /// Contains the memory used to store the dynamic linker path. This field
1533 /// not be used directly. See `get` and `set`. This field exists so that this API requires no allocator.1539 /// should not be used directly. See `get` and `set`. This field exists so
1534 buffer: [255]u8 = undefined,1540 /// that this API requires no allocator.
1541 buffer: [255]u8,
15351542
1536 /// Used to construct the dynamic linker path. This field should not be used1543 /// Used to construct the dynamic linker path. This field should not be used
1537 /// directly. See `get` and `set`.1544 /// directly. See `get` and `set`.
1538 max_byte: ?u8 = null,1545 max_byte: ?u8,
1546
1547 pub const none: DynamicLinker = .{
1548 .buffer = undefined,
1549 .max_byte = null,
1550 };
15391551
1540 /// Asserts that the length is less than or equal to 255 bytes.1552 /// Asserts that the length is less than or equal to 255 bytes.
1541 pub fn init(dl_or_null: ?[]const u8) DynamicLinker {1553 pub fn init(dl_or_null: ?[]const u8) DynamicLinker {
...@@ -1561,8 +1573,12 @@ pub const DynamicLinker = struct {...@@ -1561,8 +1573,12 @@ pub const DynamicLinker = struct {
1561 }1573 }
1562};1574};
15631575
1564pub fn standardDynamicLinkerPath(self: Target) DynamicLinker {1576pub fn standardDynamicLinkerPath(target: Target) DynamicLinker {
1565 var result: DynamicLinker = .{};1577 return standardDynamicLinkerPath_cpu_os_abi(target.cpu, target.os.tag, target.abi);
1578}
1579
1580pub fn standardDynamicLinkerPath_cpu_os_abi(cpu: Cpu, os_tag: Os.Tag, abi: Abi) DynamicLinker {
1581 var result = DynamicLinker.none;
1566 const S = struct {1582 const S = struct {
1567 fn print(r: *DynamicLinker, comptime fmt: []const u8, args: anytype) DynamicLinker {1583 fn print(r: *DynamicLinker, comptime fmt: []const u8, args: anytype) DynamicLinker {
1568 r.max_byte = @as(u8, @intCast((std.fmt.bufPrint(&r.buffer, fmt, args) catch unreachable).len - 1));1584 r.max_byte = @as(u8, @intCast((std.fmt.bufPrint(&r.buffer, fmt, args) catch unreachable).len - 1));
...@@ -1577,32 +1593,32 @@ pub fn standardDynamicLinkerPath(self: Target) DynamicLinker {...@@ -1577,32 +1593,32 @@ pub fn standardDynamicLinkerPath(self: Target) DynamicLinker {
1577 const print = S.print;1593 const print = S.print;
1578 const copy = S.copy;1594 const copy = S.copy;
15791595
1580 if (self.abi == .android) {1596 if (abi == .android) {
1581 const suffix = if (self.ptrBitWidth() == 64) "64" else "";1597 const suffix = if (ptrBitWidth_cpu_abi(cpu, abi) == 64) "64" else "";
1582 return print(&result, "/system/bin/linker{s}", .{suffix});1598 return print(&result, "/system/bin/linker{s}", .{suffix});
1583 }1599 }
15841600
1585 if (self.abi.isMusl()) {1601 if (abi.isMusl()) {
1586 const is_arm = switch (self.cpu.arch) {1602 const is_arm = switch (cpu.arch) {
1587 .arm, .armeb, .thumb, .thumbeb => true,1603 .arm, .armeb, .thumb, .thumbeb => true,
1588 else => false,1604 else => false,
1589 };1605 };
1590 const arch_part = switch (self.cpu.arch) {1606 const arch_part = switch (cpu.arch) {
1591 .arm, .thumb => "arm",1607 .arm, .thumb => "arm",
1592 .armeb, .thumbeb => "armeb",1608 .armeb, .thumbeb => "armeb",
1593 else => |arch| @tagName(arch),1609 else => |arch| @tagName(arch),
1594 };1610 };
1595 const arch_suffix = if (is_arm and self.abi.floatAbi() == .hard) "hf" else "";1611 const arch_suffix = if (is_arm and abi.floatAbi() == .hard) "hf" else "";
1596 return print(&result, "/lib/ld-musl-{s}{s}.so.1", .{ arch_part, arch_suffix });1612 return print(&result, "/lib/ld-musl-{s}{s}.so.1", .{ arch_part, arch_suffix });
1597 }1613 }
15981614
1599 switch (self.os.tag) {1615 switch (os_tag) {
1600 .freebsd => return copy(&result, "/libexec/ld-elf.so.1"),1616 .freebsd => return copy(&result, "/libexec/ld-elf.so.1"),
1601 .netbsd => return copy(&result, "/libexec/ld.elf_so"),1617 .netbsd => return copy(&result, "/libexec/ld.elf_so"),
1602 .openbsd => return copy(&result, "/usr/libexec/ld.so"),1618 .openbsd => return copy(&result, "/usr/libexec/ld.so"),
1603 .dragonfly => return copy(&result, "/libexec/ld-elf.so.2"),1619 .dragonfly => return copy(&result, "/libexec/ld-elf.so.2"),
1604 .solaris, .illumos => return copy(&result, "/lib/64/ld.so.1"),1620 .solaris, .illumos => return copy(&result, "/lib/64/ld.so.1"),
1605 .linux => switch (self.cpu.arch) {1621 .linux => switch (cpu.arch) {
1606 .x86,1622 .x86,
1607 .sparc,1623 .sparc,
1608 .sparcel,1624 .sparcel,
...@@ -1616,7 +1632,7 @@ pub fn standardDynamicLinkerPath(self: Target) DynamicLinker {...@@ -1616,7 +1632,7 @@ pub fn standardDynamicLinkerPath(self: Target) DynamicLinker {
1616 .armeb,1632 .armeb,
1617 .thumb,1633 .thumb,
1618 .thumbeb,1634 .thumbeb,
1619 => return copy(&result, switch (self.abi.floatAbi()) {1635 => return copy(&result, switch (abi.floatAbi()) {
1620 .hard => "/lib/ld-linux-armhf.so.3",1636 .hard => "/lib/ld-linux-armhf.so.3",
1621 else => "/lib/ld-linux.so.3",1637 else => "/lib/ld-linux.so.3",
1622 }),1638 }),
...@@ -1626,12 +1642,12 @@ pub fn standardDynamicLinkerPath(self: Target) DynamicLinker {...@@ -1626,12 +1642,12 @@ pub fn standardDynamicLinkerPath(self: Target) DynamicLinker {
1626 .mips64,1642 .mips64,
1627 .mips64el,1643 .mips64el,
1628 => {1644 => {
1629 const lib_suffix = switch (self.abi) {1645 const lib_suffix = switch (abi) {
1630 .gnuabin32, .gnux32 => "32",1646 .gnuabin32, .gnux32 => "32",
1631 .gnuabi64 => "64",1647 .gnuabi64 => "64",
1632 else => "",1648 else => "",
1633 };1649 };
1634 const is_nan_2008 = mips.featureSetHas(self.cpu.features, .nan2008);1650 const is_nan_2008 = mips.featureSetHas(cpu.features, .nan2008);
1635 const loader = if (is_nan_2008) "ld-linux-mipsn8.so.1" else "ld.so.1";1651 const loader = if (is_nan_2008) "ld-linux-mipsn8.so.1" else "ld.so.1";
1636 return print(&result, "/lib{s}/{s}", .{ lib_suffix, loader });1652 return print(&result, "/lib{s}/{s}", .{ lib_suffix, loader });
1637 },1653 },
...@@ -1640,7 +1656,7 @@ pub fn standardDynamicLinkerPath(self: Target) DynamicLinker {...@@ -1640,7 +1656,7 @@ pub fn standardDynamicLinkerPath(self: Target) DynamicLinker {
1640 .powerpc64, .powerpc64le => return copy(&result, "/lib64/ld64.so.2"),1656 .powerpc64, .powerpc64le => return copy(&result, "/lib64/ld64.so.2"),
1641 .s390x => return copy(&result, "/lib64/ld64.so.1"),1657 .s390x => return copy(&result, "/lib64/ld64.so.1"),
1642 .sparc64 => return copy(&result, "/lib64/ld-linux.so.2"),1658 .sparc64 => return copy(&result, "/lib64/ld-linux.so.2"),
1643 .x86_64 => return copy(&result, switch (self.abi) {1659 .x86_64 => return copy(&result, switch (abi) {
1644 .gnux32 => "/libx32/ld-linux-x32.so.2",1660 .gnux32 => "/libx32/ld-linux-x32.so.2",
1645 else => "/lib64/ld-linux-x86-64.so.2",1661 else => "/lib64/ld-linux-x86-64.so.2",
1646 }),1662 }),
...@@ -1862,17 +1878,17 @@ pub fn maxIntAlignment(target: Target) u16 {...@@ -1862,17 +1878,17 @@ pub fn maxIntAlignment(target: Target) u16 {
1862 };1878 };
1863}1879}
18641880
1865pub fn ptrBitWidth(target: Target) u16 {1881pub fn ptrBitWidth_cpu_abi(cpu: Cpu, abi: Abi) u16 {
1866 switch (target.abi) {1882 switch (abi) {
1867 .gnux32, .muslx32, .gnuabin32, .gnuilp32 => return 32,1883 .gnux32, .muslx32, .gnuabin32, .gnuilp32 => return 32,
1868 .gnuabi64 => return 64,1884 .gnuabi64 => return 64,
1869 else => {},1885 else => {},
1870 }1886 }
1871 switch (target.cpu.arch) {1887 return switch (cpu.arch) {
1872 .avr,1888 .avr,
1873 .msp430,1889 .msp430,
1874 .spu_2,1890 .spu_2,
1875 => return 16,1891 => 16,
18761892
1877 .arc,1893 .arc,
1878 .arm,1894 .arm,
...@@ -1908,7 +1924,7 @@ pub fn ptrBitWidth(target: Target) u16 {...@@ -1908,7 +1924,7 @@ pub fn ptrBitWidth(target: Target) u16 {
1908 .loongarch32,1924 .loongarch32,
1909 .dxil,1925 .dxil,
1910 .xtensa,1926 .xtensa,
1911 => return 32,1927 => 32,
19121928
1913 .aarch64,1929 .aarch64,
1914 .aarch64_be,1930 .aarch64_be,
...@@ -1933,10 +1949,14 @@ pub fn ptrBitWidth(target: Target) u16 {...@@ -1933,10 +1949,14 @@ pub fn ptrBitWidth(target: Target) u16 {
1933 .ve,1949 .ve,
1934 .spirv64,1950 .spirv64,
1935 .loongarch64,1951 .loongarch64,
1936 => return 64,1952 => 64,
19371953
1938 .sparc => return if (std.Target.sparc.featureSetHas(target.cpu.features, .v9)) 64 else 32,1954 .sparc => if (std.Target.sparc.featureSetHas(cpu.features, .v9)) 64 else 32,
1939 }1955 };
1956}
1957
1958pub fn ptrBitWidth(target: Target) u16 {
1959 return ptrBitWidth_cpu_abi(target.cpu, target.abi);
1940}1960}
19411961
1942pub fn stackAlignment(target: Target) u16 {1962pub fn stackAlignment(target: Target) u16 {
lib/std/Target/Query.zig+12-14
...@@ -34,7 +34,7 @@ abi: ?Target.Abi = null,...@@ -34,7 +34,7 @@ abi: ?Target.Abi = null,
3434
35/// When `os_tag` is `null`, then `null` means native. Otherwise it means the standard path35/// When `os_tag` is `null`, then `null` means native. Otherwise it means the standard path
36/// based on the `os_tag`.36/// based on the `os_tag`.
37dynamic_linker: DynamicLinker = DynamicLinker{},37dynamic_linker: Target.DynamicLinker = Target.DynamicLinker.none,
3838
39/// `null` means default for the cpu/arch/os combo.39/// `null` means default for the cpu/arch/os combo.
40ofmt: ?Target.ObjectFormat = null,40ofmt: ?Target.ObjectFormat = null,
...@@ -61,8 +61,6 @@ pub const OsVersion = union(enum) {...@@ -61,8 +61,6 @@ pub const OsVersion = union(enum) {
6161
62pub const SemanticVersion = std.SemanticVersion;62pub const SemanticVersion = std.SemanticVersion;
6363
64pub const DynamicLinker = Target.DynamicLinker;
65
66pub fn fromTarget(target: Target) Query {64pub fn fromTarget(target: Target) Query {
67 var result: Query = .{65 var result: Query = .{
68 .cpu_arch = target.cpu.arch,66 .cpu_arch = target.cpu.arch,
...@@ -164,7 +162,7 @@ fn updateOsVersionRange(self: *Query, os: Target.Os) void {...@@ -164,7 +162,7 @@ fn updateOsVersionRange(self: *Query, os: Target.Os) void {
164 }162 }
165}163}
166164
167/// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`.165/// TODO deprecated, use `std.zig.system.resolveTargetQuery`.
168pub fn toTarget(self: Query) Target {166pub fn toTarget(self: Query) Target {
169 return .{167 return .{
170 .cpu = self.getCpu(),168 .cpu = self.getCpu(),
...@@ -232,7 +230,7 @@ pub fn parse(args: ParseOptions) !Query {...@@ -232,7 +230,7 @@ pub fn parse(args: ParseOptions) !Query {
232 const diags = args.diagnostics orelse &dummy_diags;230 const diags = args.diagnostics orelse &dummy_diags;
233231
234 var result: Query = .{232 var result: Query = .{
235 .dynamic_linker = DynamicLinker.init(args.dynamic_linker),233 .dynamic_linker = Target.DynamicLinker.init(args.dynamic_linker),
236 };234 };
237235
238 var it = mem.splitScalar(u8, args.arch_os_abi, '-');236 var it = mem.splitScalar(u8, args.arch_os_abi, '-');
...@@ -379,13 +377,13 @@ test parseVersion {...@@ -379,13 +377,13 @@ test parseVersion {
379 try std.testing.expectError(error.InvalidVersion, parseVersion("1.2.3.4"));377 try std.testing.expectError(error.InvalidVersion, parseVersion("1.2.3.4"));
380}378}
381379
382/// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`.380/// TODO deprecated, use `std.zig.system.resolveTargetQuery`.
383pub fn getCpu(self: Query) Target.Cpu {381pub fn getCpu(self: Query) Target.Cpu {
384 switch (self.cpu_model) {382 switch (self.cpu_model) {
385 .native => {383 .native => {
386 // This works when doing `zig build` because Zig generates a build executable using384 // This works when doing `zig build` because Zig generates a build executable using
387 // native CPU model & features. However this will not be accurate otherwise, and385 // native CPU model & features. However this will not be accurate otherwise, and
388 // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`.386 // will need to be integrated with `std.zig.system.resolveTargetQuery`.
389 return builtin.cpu;387 return builtin.cpu;
390 },388 },
391 .baseline => {389 .baseline => {
...@@ -396,7 +394,7 @@ pub fn getCpu(self: Query) Target.Cpu {...@@ -396,7 +394,7 @@ pub fn getCpu(self: Query) Target.Cpu {
396 .determined_by_cpu_arch => if (self.cpu_arch == null) {394 .determined_by_cpu_arch => if (self.cpu_arch == null) {
397 // This works when doing `zig build` because Zig generates a build executable using395 // This works when doing `zig build` because Zig generates a build executable using
398 // native CPU model & features. However this will not be accurate otherwise, and396 // native CPU model & features. However this will not be accurate otherwise, and
399 // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`.397 // will need to be integrated with `std.zig.system.resolveTargetQuery`.
400 return builtin.cpu;398 return builtin.cpu;
401 } else {399 } else {
402 var adjusted_baseline = Target.Cpu.baseline(self.getCpuArch());400 var adjusted_baseline = Target.Cpu.baseline(self.getCpuArch());
...@@ -426,11 +424,11 @@ pub fn getCpuFeatures(self: Query) Target.Cpu.Feature.Set {...@@ -426,11 +424,11 @@ pub fn getCpuFeatures(self: Query) Target.Cpu.Feature.Set {
426 return self.getCpu().features;424 return self.getCpu().features;
427}425}
428426
429/// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`.427/// TODO deprecated, use `std.zig.system.resolveTargetQuery`.
430pub fn getOs(self: Query) Target.Os {428pub fn getOs(self: Query) Target.Os {
431 // `builtin.os` works when doing `zig build` because Zig generates a build executable using429 // `builtin.os` works when doing `zig build` because Zig generates a build executable using
432 // native OS version range. However this will not be accurate otherwise, and430 // native OS version range. However this will not be accurate otherwise, and
433 // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`.431 // will need to be integrated with `std.zig.system.resolveTargetQuery`.
434 var adjusted_os = if (self.os_tag) |os_tag| os_tag.defaultVersionRange(self.getCpuArch()) else builtin.os;432 var adjusted_os = if (self.os_tag) |os_tag| os_tag.defaultVersionRange(self.getCpuArch()) else builtin.os;
435433
436 if (self.os_version_min) |min| switch (min) {434 if (self.os_version_min) |min| switch (min) {
...@@ -463,7 +461,7 @@ pub fn getOsTag(self: Query) Target.Os.Tag {...@@ -463,7 +461,7 @@ pub fn getOsTag(self: Query) Target.Os.Tag {
463 return self.os_tag orelse builtin.os.tag;461 return self.os_tag orelse builtin.os.tag;
464}462}
465463
466/// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`.464/// TODO deprecated, use `std.zig.system.resolveTargetQuery`.
467pub fn getOsVersionMin(self: Query) OsVersion {465pub fn getOsVersionMin(self: Query) OsVersion {
468 if (self.os_version_min) |version_min| return version_min;466 if (self.os_version_min) |version_min| return version_min;
469 var tmp: Query = undefined;467 var tmp: Query = undefined;
...@@ -471,7 +469,7 @@ pub fn getOsVersionMin(self: Query) OsVersion {...@@ -471,7 +469,7 @@ pub fn getOsVersionMin(self: Query) OsVersion {
471 return tmp.os_version_min.?;469 return tmp.os_version_min.?;
472}470}
473471
474/// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`.472/// TODO deprecated, use `std.zig.system.resolveTargetQuery`.
475pub fn getOsVersionMax(self: Query) OsVersion {473pub fn getOsVersionMax(self: Query) OsVersion {
476 if (self.os_version_max) |version_max| return version_max;474 if (self.os_version_max) |version_max| return version_max;
477 var tmp: Query = undefined;475 var tmp: Query = undefined;
...@@ -479,14 +477,14 @@ pub fn getOsVersionMax(self: Query) OsVersion {...@@ -479,14 +477,14 @@ pub fn getOsVersionMax(self: Query) OsVersion {
479 return tmp.os_version_max.?;477 return tmp.os_version_max.?;
480}478}
481479
482/// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`.480/// TODO deprecated, use `std.zig.system.resolveTargetQuery`.
483pub fn getAbi(self: Query) Target.Abi {481pub fn getAbi(self: Query) Target.Abi {
484 if (self.abi) |abi| return abi;482 if (self.abi) |abi| return abi;
485483
486 if (self.os_tag == null) {484 if (self.os_tag == null) {
487 // This works when doing `zig build` because Zig generates a build executable using485 // This works when doing `zig build` because Zig generates a build executable using
488 // native CPU model & features. However this will not be accurate otherwise, and486 // native CPU model & features. However this will not be accurate otherwise, and
489 // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`.487 // will need to be integrated with `std.zig.system.resolveTargetQuery`.
490 return builtin.abi;488 return builtin.abi;
491 }489 }
492490
lib/std/zig/system.zig+1116-2
...@@ -1,13 +1,1127 @@...@@ -1,13 +1,1127 @@
1pub const NativePaths = @import("system/NativePaths.zig");1pub const NativePaths = @import("system/NativePaths.zig");
2pub const NativeTargetInfo = @import("system/NativeTargetInfo.zig");
32
4pub const windows = @import("system/windows.zig");3pub const windows = @import("system/windows.zig");
5pub const darwin = @import("system/darwin.zig");4pub const darwin = @import("system/darwin.zig");
6pub const linux = @import("system/linux.zig");5pub const linux = @import("system/linux.zig");
76
7pub const Executor = union(enum) {
8 native,
9 rosetta,
10 qemu: []const u8,
11 wine: []const u8,
12 wasmtime: []const u8,
13 darling: []const u8,
14 bad_dl: []const u8,
15 bad_os_or_cpu,
16};
17
18pub const GetExternalExecutorOptions = struct {
19 allow_darling: bool = true,
20 allow_qemu: bool = true,
21 allow_rosetta: bool = true,
22 allow_wasmtime: bool = true,
23 allow_wine: bool = true,
24 qemu_fixes_dl: bool = false,
25 link_libc: bool = false,
26};
27
28/// Return whether or not the given host is capable of running executables of
29/// the other target.
30pub fn getExternalExecutor(
31 host: std.Target,
32 candidate: *const std.Target,
33 options: GetExternalExecutorOptions,
34) Executor {
35 const os_match = host.os.tag == candidate.os.tag;
36 const cpu_ok = cpu_ok: {
37 if (host.cpu.arch == candidate.cpu.arch)
38 break :cpu_ok true;
39
40 if (host.cpu.arch == .x86_64 and candidate.cpu.arch == .x86)
41 break :cpu_ok true;
42
43 if (host.cpu.arch == .aarch64 and candidate.cpu.arch == .arm)
44 break :cpu_ok true;
45
46 if (host.cpu.arch == .aarch64_be and candidate.cpu.arch == .armeb)
47 break :cpu_ok true;
48
49 // TODO additionally detect incompatible CPU features.
50 // Note that in some cases the OS kernel will emulate missing CPU features
51 // when an illegal instruction is encountered.
52
53 break :cpu_ok false;
54 };
55
56 var bad_result: Executor = .bad_os_or_cpu;
57
58 if (os_match and cpu_ok) native: {
59 if (options.link_libc) {
60 if (candidate.dynamic_linker.get()) |candidate_dl| {
61 fs.cwd().access(candidate_dl, .{}) catch {
62 bad_result = .{ .bad_dl = candidate_dl };
63 break :native;
64 };
65 }
66 }
67 return .native;
68 }
69
70 // If the OS match and OS is macOS and CPU is arm64, we can use Rosetta 2
71 // to emulate the foreign architecture.
72 if (options.allow_rosetta and os_match and
73 host.os.tag == .macos and host.cpu.arch == .aarch64)
74 {
75 switch (candidate.cpu.arch) {
76 .x86_64 => return .rosetta,
77 else => return bad_result,
78 }
79 }
80
81 // If the OS matches, we can use QEMU to emulate a foreign architecture.
82 if (options.allow_qemu and os_match and (!cpu_ok or options.qemu_fixes_dl)) {
83 return switch (candidate.cpu.arch) {
84 .aarch64 => Executor{ .qemu = "qemu-aarch64" },
85 .aarch64_be => Executor{ .qemu = "qemu-aarch64_be" },
86 .arm => Executor{ .qemu = "qemu-arm" },
87 .armeb => Executor{ .qemu = "qemu-armeb" },
88 .hexagon => Executor{ .qemu = "qemu-hexagon" },
89 .x86 => Executor{ .qemu = "qemu-i386" },
90 .m68k => Executor{ .qemu = "qemu-m68k" },
91 .mips => Executor{ .qemu = "qemu-mips" },
92 .mipsel => Executor{ .qemu = "qemu-mipsel" },
93 .mips64 => Executor{ .qemu = "qemu-mips64" },
94 .mips64el => Executor{ .qemu = "qemu-mips64el" },
95 .powerpc => Executor{ .qemu = "qemu-ppc" },
96 .powerpc64 => Executor{ .qemu = "qemu-ppc64" },
97 .powerpc64le => Executor{ .qemu = "qemu-ppc64le" },
98 .riscv32 => Executor{ .qemu = "qemu-riscv32" },
99 .riscv64 => Executor{ .qemu = "qemu-riscv64" },
100 .s390x => Executor{ .qemu = "qemu-s390x" },
101 .sparc => Executor{ .qemu = "qemu-sparc" },
102 .sparc64 => Executor{ .qemu = "qemu-sparc64" },
103 .x86_64 => Executor{ .qemu = "qemu-x86_64" },
104 else => return bad_result,
105 };
106 }
107
108 switch (candidate.os.tag) {
109 .windows => {
110 if (options.allow_wine) {
111 // x86_64 wine does not support emulating aarch64-windows and
112 // vice versa.
113 if (candidate.cpu.arch != builtin.cpu.arch) {
114 return bad_result;
115 }
116 switch (candidate.ptrBitWidth()) {
117 32 => return Executor{ .wine = "wine" },
118 64 => return Executor{ .wine = "wine64" },
119 else => return bad_result,
120 }
121 }
122 return bad_result;
123 },
124 .wasi => {
125 if (options.allow_wasmtime) {
126 switch (candidate.ptrBitWidth()) {
127 32 => return Executor{ .wasmtime = "wasmtime" },
128 else => return bad_result,
129 }
130 }
131 return bad_result;
132 },
133 .macos => {
134 if (options.allow_darling) {
135 // This check can be loosened once darling adds a QEMU-based emulation
136 // layer for non-host architectures:
137 // https://github.com/darlinghq/darling/issues/863
138 if (candidate.cpu.arch != builtin.cpu.arch) {
139 return bad_result;
140 }
141 return Executor{ .darling = "darling" };
142 }
143 return bad_result;
144 },
145 else => return bad_result,
146 }
147}
148
149pub const DetectError = error{
150 FileSystem,
151 SystemResources,
152 SymLinkLoop,
153 ProcessFdQuotaExceeded,
154 SystemFdQuotaExceeded,
155 DeviceBusy,
156 OSVersionDetectionFail,
157 Unexpected,
158};
159
160/// Given a `Target.Query`, which specifies in detail which parts of the
161/// target should be detected natively, which should be standard or default,
162/// and which are provided explicitly, this function resolves the native
163/// components by detecting the native system, and then resolves
164/// standard/default parts relative to that.
165pub fn resolveTargetQuery(query: Target.Query) DetectError!Target {
166 var os = query.getOsTag().defaultVersionRange(query.getCpuArch());
167 if (query.os_tag == null) {
168 switch (builtin.target.os.tag) {
169 .linux => {
170 const uts = std.os.uname();
171 const release = mem.sliceTo(&uts.release, 0);
172 // The release field sometimes has a weird format,
173 // `Version.parse` will attempt to find some meaningful interpretation.
174 if (std.SemanticVersion.parse(release)) |ver| {
175 os.version_range.linux.range.min = ver;
176 os.version_range.linux.range.max = ver;
177 } else |err| switch (err) {
178 error.Overflow => {},
179 error.InvalidVersion => {},
180 }
181 },
182 .solaris, .illumos => {
183 const uts = std.os.uname();
184 const release = mem.sliceTo(&uts.release, 0);
185 if (std.SemanticVersion.parse(release)) |ver| {
186 os.version_range.semver.min = ver;
187 os.version_range.semver.max = ver;
188 } else |err| switch (err) {
189 error.Overflow => {},
190 error.InvalidVersion => {},
191 }
192 },
193 .windows => {
194 const detected_version = windows.detectRuntimeVersion();
195 os.version_range.windows.min = detected_version;
196 os.version_range.windows.max = detected_version;
197 },
198 .macos => try darwin.macos.detect(&os),
199 .freebsd, .netbsd, .dragonfly => {
200 const key = switch (builtin.target.os.tag) {
201 .freebsd => "kern.osreldate",
202 .netbsd, .dragonfly => "kern.osrevision",
203 else => unreachable,
204 };
205 var value: u32 = undefined;
206 var len: usize = @sizeOf(@TypeOf(value));
207
208 std.os.sysctlbynameZ(key, &value, &len, null, 0) catch |err| switch (err) {
209 error.NameTooLong => unreachable, // constant, known good value
210 error.PermissionDenied => unreachable, // only when setting values,
211 error.SystemResources => unreachable, // memory already on the stack
212 error.UnknownName => unreachable, // constant, known good value
213 error.Unexpected => return error.OSVersionDetectionFail,
214 };
215
216 switch (builtin.target.os.tag) {
217 .freebsd => {
218 // https://www.freebsd.org/doc/en_US.ISO8859-1/books/porters-handbook/versions.html
219 // Major * 100,000 has been convention since FreeBSD 2.2 (1997)
220 // Minor * 1(0),000 summed has been convention since FreeBSD 2.2 (1997)
221 // e.g. 492101 = 4.11-STABLE = 4.(9+2)
222 const major = value / 100_000;
223 const minor1 = value % 100_000 / 10_000; // usually 0 since 5.1
224 const minor2 = value % 10_000 / 1_000; // 0 before 5.1, minor version since
225 const patch = value % 1_000;
226 os.version_range.semver.min = .{ .major = major, .minor = minor1 + minor2, .patch = patch };
227 os.version_range.semver.max = os.version_range.semver.min;
228 },
229 .netbsd => {
230 // #define __NetBSD_Version__ MMmmrrpp00
231 //
232 // M = major version
233 // m = minor version; a minor number of 99 indicates current.
234 // r = 0 (*)
235 // p = patchlevel
236 const major = value / 100_000_000;
237 const minor = value % 100_000_000 / 1_000_000;
238 const patch = value % 10_000 / 100;
239 os.version_range.semver.min = .{ .major = major, .minor = minor, .patch = patch };
240 os.version_range.semver.max = os.version_range.semver.min;
241 },
242 .dragonfly => {
243 // https://github.com/DragonFlyBSD/DragonFlyBSD/blob/cb2cde83771754aeef9bb3251ee48959138dec87/Makefile.inc1#L15-L17
244 // flat base10 format: Mmmmpp
245 // M = major
246 // m = minor; odd-numbers indicate current dev branch
247 // p = patch
248 const major = value / 100_000;
249 const minor = value % 100_000 / 100;
250 const patch = value % 100;
251 os.version_range.semver.min = .{ .major = major, .minor = minor, .patch = patch };
252 os.version_range.semver.max = os.version_range.semver.min;
253 },
254 else => unreachable,
255 }
256 },
257 .openbsd => {
258 const mib: [2]c_int = [_]c_int{
259 std.os.CTL.KERN,
260 std.os.KERN.OSRELEASE,
261 };
262 var buf: [64]u8 = undefined;
263 // consider that sysctl result includes null-termination
264 // reserve 1 byte to ensure we never overflow when appending ".0"
265 var len: usize = buf.len - 1;
266
267 std.os.sysctl(&mib, &buf, &len, null, 0) catch |err| switch (err) {
268 error.NameTooLong => unreachable, // constant, known good value
269 error.PermissionDenied => unreachable, // only when setting values,
270 error.SystemResources => unreachable, // memory already on the stack
271 error.UnknownName => unreachable, // constant, known good value
272 error.Unexpected => return error.OSVersionDetectionFail,
273 };
274
275 // append ".0" to satisfy semver
276 buf[len - 1] = '.';
277 buf[len] = '0';
278 len += 1;
279
280 if (std.SemanticVersion.parse(buf[0..len])) |ver| {
281 os.version_range.semver.min = ver;
282 os.version_range.semver.max = ver;
283 } else |_| {
284 return error.OSVersionDetectionFail;
285 }
286 },
287 else => {
288 // Unimplemented, fall back to default version range.
289 },
290 }
291 }
292
293 if (query.os_version_min) |min| switch (min) {
294 .none => {},
295 .semver => |semver| switch (query.getOsTag()) {
296 .linux => os.version_range.linux.range.min = semver,
297 else => os.version_range.semver.min = semver,
298 },
299 .windows => |win_ver| os.version_range.windows.min = win_ver,
300 };
301
302 if (query.os_version_max) |max| switch (max) {
303 .none => {},
304 .semver => |semver| switch (query.getOsTag()) {
305 .linux => os.version_range.linux.range.max = semver,
306 else => os.version_range.semver.max = semver,
307 },
308 .windows => |win_ver| os.version_range.windows.max = win_ver,
309 };
310
311 if (query.glibc_version) |glibc| {
312 assert(query.isGnuLibC());
313 os.version_range.linux.glibc = glibc;
314 }
315
316 // Until https://github.com/ziglang/zig/issues/4592 is implemented (support detecting the
317 // native CPU architecture as being different than the current target), we use this:
318 const cpu_arch = query.getCpuArch();
319
320 const cpu = switch (query.cpu_model) {
321 .native => detectNativeCpuAndFeatures(cpu_arch, os, query),
322 .baseline => Target.Cpu.baseline(cpu_arch),
323 .determined_by_cpu_arch => if (query.cpu_arch == null)
324 detectNativeCpuAndFeatures(cpu_arch, os, query)
325 else
326 Target.Cpu.baseline(cpu_arch),
327 .explicit => |model| model.toCpu(cpu_arch),
328 } orelse backup_cpu_detection: {
329 break :backup_cpu_detection Target.Cpu.baseline(cpu_arch);
330 };
331 var result = try detectAbiAndDynamicLinker(cpu, os, query);
332 // For x86, we need to populate some CPU feature flags depending on architecture
333 // and mode:
334 // * 16bit_mode => if the abi is code16
335 // * 32bit_mode => if the arch is x86
336 // However, the "mode" flags can be used as overrides, so if the user explicitly
337 // sets one of them, that takes precedence.
338 switch (cpu_arch) {
339 .x86 => {
340 if (!Target.x86.featureSetHasAny(query.cpu_features_add, .{
341 .@"16bit_mode", .@"32bit_mode",
342 })) {
343 switch (result.abi) {
344 .code16 => result.cpu.features.addFeature(
345 @intFromEnum(Target.x86.Feature.@"16bit_mode"),
346 ),
347 else => result.cpu.features.addFeature(
348 @intFromEnum(Target.x86.Feature.@"32bit_mode"),
349 ),
350 }
351 }
352 },
353 .arm, .armeb => {
354 // XXX What do we do if the target has the noarm feature?
355 // What do we do if the user specifies +thumb_mode?
356 },
357 .thumb, .thumbeb => {
358 result.cpu.features.addFeature(
359 @intFromEnum(Target.arm.Feature.thumb_mode),
360 );
361 },
362 else => {},
363 }
364 query.updateCpuFeatures(&result.cpu.features);
365 return result;
366}
367
368fn detectNativeCpuAndFeatures(cpu_arch: Target.Cpu.Arch, os: Target.Os, query: Target.Query) ?Target.Cpu {
369 // Here we switch on a comptime value rather than `cpu_arch`. This is valid because `cpu_arch`,
370 // although it is a runtime value, is guaranteed to be one of the architectures in the set
371 // of the respective switch prong.
372 switch (builtin.cpu.arch) {
373 .x86_64, .x86 => {
374 return @import("system/x86.zig").detectNativeCpuAndFeatures(cpu_arch, os, query);
375 },
376 else => {},
377 }
378
379 switch (builtin.os.tag) {
380 .linux => return linux.detectNativeCpuAndFeatures(),
381 .macos => return darwin.macos.detectNativeCpuAndFeatures(),
382 .windows => return windows.detectNativeCpuAndFeatures(),
383 else => {},
384 }
385
386 // This architecture does not have CPU model & feature detection yet.
387 // See https://github.com/ziglang/zig/issues/4591
388 return null;
389}
390
391pub const AbiAndDynamicLinkerFromFileError = error{
392 FileSystem,
393 SystemResources,
394 SymLinkLoop,
395 ProcessFdQuotaExceeded,
396 SystemFdQuotaExceeded,
397 UnableToReadElfFile,
398 InvalidElfClass,
399 InvalidElfVersion,
400 InvalidElfEndian,
401 InvalidElfFile,
402 InvalidElfMagic,
403 Unexpected,
404 UnexpectedEndOfFile,
405 NameTooLong,
406};
407
408pub fn abiAndDynamicLinkerFromFile(
409 file: fs.File,
410 cpu: Target.Cpu,
411 os: Target.Os,
412 ld_info_list: []const LdInfo,
413 query: Target.Query,
414) AbiAndDynamicLinkerFromFileError!Target {
415 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 align(@alignOf(elf.Elf64_Ehdr)) = undefined;
416 _ = try preadMin(file, &hdr_buf, 0, hdr_buf.len);
417 const hdr32 = @as(*elf.Elf32_Ehdr, @ptrCast(&hdr_buf));
418 const hdr64 = @as(*elf.Elf64_Ehdr, @ptrCast(&hdr_buf));
419 if (!mem.eql(u8, hdr32.e_ident[0..4], elf.MAGIC)) return error.InvalidElfMagic;
420 const elf_endian: std.builtin.Endian = switch (hdr32.e_ident[elf.EI_DATA]) {
421 elf.ELFDATA2LSB => .little,
422 elf.ELFDATA2MSB => .big,
423 else => return error.InvalidElfEndian,
424 };
425 const need_bswap = elf_endian != native_endian;
426 if (hdr32.e_ident[elf.EI_VERSION] != 1) return error.InvalidElfVersion;
427
428 const is_64 = switch (hdr32.e_ident[elf.EI_CLASS]) {
429 elf.ELFCLASS32 => false,
430 elf.ELFCLASS64 => true,
431 else => return error.InvalidElfClass,
432 };
433 var phoff = elfInt(is_64, need_bswap, hdr32.e_phoff, hdr64.e_phoff);
434 const phentsize = elfInt(is_64, need_bswap, hdr32.e_phentsize, hdr64.e_phentsize);
435 const phnum = elfInt(is_64, need_bswap, hdr32.e_phnum, hdr64.e_phnum);
436
437 var result: Target = .{
438 .cpu = cpu,
439 .os = os,
440 .abi = query.abi orelse Target.Abi.default(cpu.arch, os),
441 .ofmt = query.ofmt orelse Target.ObjectFormat.default(os.tag, cpu.arch),
442 .dynamic_linker = query.dynamic_linker,
443 };
444 var rpath_offset: ?u64 = null; // Found inside PT_DYNAMIC
445 const look_for_ld = query.dynamic_linker.get() == null;
446
447 var ph_buf: [16 * @sizeOf(elf.Elf64_Phdr)]u8 align(@alignOf(elf.Elf64_Phdr)) = undefined;
448 if (phentsize > @sizeOf(elf.Elf64_Phdr)) return error.InvalidElfFile;
449
450 var ph_i: u16 = 0;
451 while (ph_i < phnum) {
452 // Reserve some bytes so that we can deref the 64-bit struct fields
453 // even when the ELF file is 32-bits.
454 const ph_reserve: usize = @sizeOf(elf.Elf64_Phdr) - @sizeOf(elf.Elf32_Phdr);
455 const ph_read_byte_len = try preadMin(file, ph_buf[0 .. ph_buf.len - ph_reserve], phoff, phentsize);
456 var ph_buf_i: usize = 0;
457 while (ph_buf_i < ph_read_byte_len and ph_i < phnum) : ({
458 ph_i += 1;
459 phoff += phentsize;
460 ph_buf_i += phentsize;
461 }) {
462 const ph32: *elf.Elf32_Phdr = @ptrCast(@alignCast(&ph_buf[ph_buf_i]));
463 const ph64: *elf.Elf64_Phdr = @ptrCast(@alignCast(&ph_buf[ph_buf_i]));
464 const p_type = elfInt(is_64, need_bswap, ph32.p_type, ph64.p_type);
465 switch (p_type) {
466 elf.PT_INTERP => if (look_for_ld) {
467 const p_offset = elfInt(is_64, need_bswap, ph32.p_offset, ph64.p_offset);
468 const p_filesz = elfInt(is_64, need_bswap, ph32.p_filesz, ph64.p_filesz);
469 if (p_filesz > result.dynamic_linker.buffer.len) return error.NameTooLong;
470 const filesz = @as(usize, @intCast(p_filesz));
471 _ = try preadMin(file, result.dynamic_linker.buffer[0..filesz], p_offset, filesz);
472 // PT_INTERP includes a null byte in filesz.
473 const len = filesz - 1;
474 // dynamic_linker.max_byte is "max", not "len".
475 // We know it will fit in u8 because we check against dynamic_linker.buffer.len above.
476 result.dynamic_linker.max_byte = @as(u8, @intCast(len - 1));
477
478 // Use it to determine ABI.
479 const full_ld_path = result.dynamic_linker.buffer[0..len];
480 for (ld_info_list) |ld_info| {
481 const standard_ld_basename = fs.path.basename(ld_info.ld.get().?);
482 if (std.mem.endsWith(u8, full_ld_path, standard_ld_basename)) {
483 result.abi = ld_info.abi;
484 break;
485 }
486 }
487 },
488 // We only need this for detecting glibc version.
489 elf.PT_DYNAMIC => if (builtin.target.os.tag == .linux and result.isGnuLibC() and
490 query.glibc_version == null)
491 {
492 var dyn_off = elfInt(is_64, need_bswap, ph32.p_offset, ph64.p_offset);
493 const p_filesz = elfInt(is_64, need_bswap, ph32.p_filesz, ph64.p_filesz);
494 const dyn_size: usize = if (is_64) @sizeOf(elf.Elf64_Dyn) else @sizeOf(elf.Elf32_Dyn);
495 const dyn_num = p_filesz / dyn_size;
496 var dyn_buf: [16 * @sizeOf(elf.Elf64_Dyn)]u8 align(@alignOf(elf.Elf64_Dyn)) = undefined;
497 var dyn_i: usize = 0;
498 dyn: while (dyn_i < dyn_num) {
499 // Reserve some bytes so that we can deref the 64-bit struct fields
500 // even when the ELF file is 32-bits.
501 const dyn_reserve: usize = @sizeOf(elf.Elf64_Dyn) - @sizeOf(elf.Elf32_Dyn);
502 const dyn_read_byte_len = try preadMin(
503 file,
504 dyn_buf[0 .. dyn_buf.len - dyn_reserve],
505 dyn_off,
506 dyn_size,
507 );
508 var dyn_buf_i: usize = 0;
509 while (dyn_buf_i < dyn_read_byte_len and dyn_i < dyn_num) : ({
510 dyn_i += 1;
511 dyn_off += dyn_size;
512 dyn_buf_i += dyn_size;
513 }) {
514 const dyn32: *elf.Elf32_Dyn = @ptrCast(@alignCast(&dyn_buf[dyn_buf_i]));
515 const dyn64: *elf.Elf64_Dyn = @ptrCast(@alignCast(&dyn_buf[dyn_buf_i]));
516 const tag = elfInt(is_64, need_bswap, dyn32.d_tag, dyn64.d_tag);
517 const val = elfInt(is_64, need_bswap, dyn32.d_val, dyn64.d_val);
518 if (tag == elf.DT_RUNPATH) {
519 rpath_offset = val;
520 break :dyn;
521 }
522 }
523 }
524 },
525 else => continue,
526 }
527 }
528 }
529
530 if (builtin.target.os.tag == .linux and result.isGnuLibC() and
531 query.glibc_version == null)
532 {
533 const shstrndx = elfInt(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx);
534
535 var shoff = elfInt(is_64, need_bswap, hdr32.e_shoff, hdr64.e_shoff);
536 const shentsize = elfInt(is_64, need_bswap, hdr32.e_shentsize, hdr64.e_shentsize);
537 const str_section_off = shoff + @as(u64, shentsize) * @as(u64, shstrndx);
538
539 var sh_buf: [16 * @sizeOf(elf.Elf64_Shdr)]u8 align(@alignOf(elf.Elf64_Shdr)) = undefined;
540 if (sh_buf.len < shentsize) return error.InvalidElfFile;
541
542 _ = try preadMin(file, &sh_buf, str_section_off, shentsize);
543 const shstr32: *elf.Elf32_Shdr = @ptrCast(@alignCast(&sh_buf));
544 const shstr64: *elf.Elf64_Shdr = @ptrCast(@alignCast(&sh_buf));
545 const shstrtab_off = elfInt(is_64, need_bswap, shstr32.sh_offset, shstr64.sh_offset);
546 const shstrtab_size = elfInt(is_64, need_bswap, shstr32.sh_size, shstr64.sh_size);
547 var strtab_buf: [4096:0]u8 = undefined;
548 const shstrtab_len = @min(shstrtab_size, strtab_buf.len);
549 const shstrtab_read_len = try preadMin(file, &strtab_buf, shstrtab_off, shstrtab_len);
550 const shstrtab = strtab_buf[0..shstrtab_read_len];
551
552 const shnum = elfInt(is_64, need_bswap, hdr32.e_shnum, hdr64.e_shnum);
553 var sh_i: u16 = 0;
554 const dynstr: ?struct { offset: u64, size: u64 } = find_dyn_str: while (sh_i < shnum) {
555 // Reserve some bytes so that we can deref the 64-bit struct fields
556 // even when the ELF file is 32-bits.
557 const sh_reserve: usize = @sizeOf(elf.Elf64_Shdr) - @sizeOf(elf.Elf32_Shdr);
558 const sh_read_byte_len = try preadMin(
559 file,
560 sh_buf[0 .. sh_buf.len - sh_reserve],
561 shoff,
562 shentsize,
563 );
564 var sh_buf_i: usize = 0;
565 while (sh_buf_i < sh_read_byte_len and sh_i < shnum) : ({
566 sh_i += 1;
567 shoff += shentsize;
568 sh_buf_i += shentsize;
569 }) {
570 const sh32: *elf.Elf32_Shdr = @ptrCast(@alignCast(&sh_buf[sh_buf_i]));
571 const sh64: *elf.Elf64_Shdr = @ptrCast(@alignCast(&sh_buf[sh_buf_i]));
572 const sh_name_off = elfInt(is_64, need_bswap, sh32.sh_name, sh64.sh_name);
573 const sh_name = mem.sliceTo(shstrtab[sh_name_off..], 0);
574 if (mem.eql(u8, sh_name, ".dynstr")) {
575 break :find_dyn_str .{
576 .offset = elfInt(is_64, need_bswap, sh32.sh_offset, sh64.sh_offset),
577 .size = elfInt(is_64, need_bswap, sh32.sh_size, sh64.sh_size),
578 };
579 }
580 }
581 } else null;
582
583 if (dynstr) |ds| {
584 if (rpath_offset) |rpoff| {
585 if (rpoff > ds.size) return error.InvalidElfFile;
586 const rpoff_file = ds.offset + rpoff;
587 const rp_max_size = ds.size - rpoff;
588
589 const strtab_len = @min(rp_max_size, strtab_buf.len);
590 const strtab_read_len = try preadMin(file, &strtab_buf, rpoff_file, strtab_len);
591 const strtab = strtab_buf[0..strtab_read_len];
592
593 const rpath_list = mem.sliceTo(strtab, 0);
594 var it = mem.tokenizeScalar(u8, rpath_list, ':');
595 while (it.next()) |rpath| {
596 if (glibcVerFromRPath(rpath)) |ver| {
597 result.os.version_range.linux.glibc = ver;
598 return result;
599 } else |err| switch (err) {
600 error.GLibCNotFound => continue,
601 else => |e| return e,
602 }
603 }
604 }
605 }
606
607 if (result.dynamic_linker.get()) |dl_path| glibc_ver: {
608 // There is no DT_RUNPATH so we try to find libc.so.6 inside the same
609 // directory as the dynamic linker.
610 if (fs.path.dirname(dl_path)) |rpath| {
611 if (glibcVerFromRPath(rpath)) |ver| {
612 result.os.version_range.linux.glibc = ver;
613 return result;
614 } else |err| switch (err) {
615 error.GLibCNotFound => {},
616 else => |e| return e,
617 }
618 }
619
620 // So far, no luck. Next we try to see if the information is
621 // present in the symlink data for the dynamic linker path.
622 var link_buf: [std.os.PATH_MAX]u8 = undefined;
623 const link_name = std.os.readlink(dl_path, &link_buf) catch |err| switch (err) {
624 error.NameTooLong => unreachable,
625 error.InvalidUtf8 => unreachable, // Windows only
626 error.BadPathName => unreachable, // Windows only
627 error.UnsupportedReparsePointType => unreachable, // Windows only
628 error.NetworkNotFound => unreachable, // Windows only
629
630 error.AccessDenied,
631 error.FileNotFound,
632 error.NotLink,
633 error.NotDir,
634 => break :glibc_ver,
635
636 error.SystemResources,
637 error.FileSystem,
638 error.SymLinkLoop,
639 error.Unexpected,
640 => |e| return e,
641 };
642 result.os.version_range.linux.glibc = glibcVerFromLinkName(
643 fs.path.basename(link_name),
644 "ld-",
645 ) catch |err| switch (err) {
646 error.UnrecognizedGnuLibCFileName,
647 error.InvalidGnuLibCVersion,
648 => break :glibc_ver,
649 };
650 return result;
651 }
652
653 // Nothing worked so far. Finally we fall back to hard-coded search paths.
654 // Some distros such as Debian keep their libc.so.6 in `/lib/$triple/`.
655 var path_buf: [std.os.PATH_MAX]u8 = undefined;
656 var index: usize = 0;
657 const prefix = "/lib/";
658 const cpu_arch = @tagName(result.cpu.arch);
659 const os_tag = @tagName(result.os.tag);
660 const abi = @tagName(result.abi);
661 @memcpy(path_buf[index..][0..prefix.len], prefix);
662 index += prefix.len;
663 @memcpy(path_buf[index..][0..cpu_arch.len], cpu_arch);
664 index += cpu_arch.len;
665 path_buf[index] = '-';
666 index += 1;
667 @memcpy(path_buf[index..][0..os_tag.len], os_tag);
668 index += os_tag.len;
669 path_buf[index] = '-';
670 index += 1;
671 @memcpy(path_buf[index..][0..abi.len], abi);
672 index += abi.len;
673 const rpath = path_buf[0..index];
674 if (glibcVerFromRPath(rpath)) |ver| {
675 result.os.version_range.linux.glibc = ver;
676 return result;
677 } else |err| switch (err) {
678 error.GLibCNotFound => {},
679 else => |e| return e,
680 }
681 }
682
683 return result;
684}
685
686fn glibcVerFromLinkName(link_name: []const u8, prefix: []const u8) error{ UnrecognizedGnuLibCFileName, InvalidGnuLibCVersion }!std.SemanticVersion {
687 // example: "libc-2.3.4.so"
688 // example: "libc-2.27.so"
689 // example: "ld-2.33.so"
690 const suffix = ".so";
691 if (!mem.startsWith(u8, link_name, prefix) or !mem.endsWith(u8, link_name, suffix)) {
692 return error.UnrecognizedGnuLibCFileName;
693 }
694 // chop off "libc-" and ".so"
695 const link_name_chopped = link_name[prefix.len .. link_name.len - suffix.len];
696 return Target.Query.parseVersion(link_name_chopped) catch |err| switch (err) {
697 error.Overflow => return error.InvalidGnuLibCVersion,
698 error.InvalidVersion => return error.InvalidGnuLibCVersion,
699 };
700}
701
702test glibcVerFromLinkName {
703 try std.testing.expectError(error.UnrecognizedGnuLibCFileName, glibcVerFromLinkName("ld-2.37.so", "this-prefix-does-not-exist"));
704 try std.testing.expectError(error.UnrecognizedGnuLibCFileName, glibcVerFromLinkName("libc-2.37.so-is-not-end", "libc-"));
705
706 try std.testing.expectError(error.InvalidGnuLibCVersion, glibcVerFromLinkName("ld-2.so", "ld-"));
707 try std.testing.expectEqual(std.SemanticVersion{ .major = 2, .minor = 37, .patch = 0 }, try glibcVerFromLinkName("ld-2.37.so", "ld-"));
708 try std.testing.expectEqual(std.SemanticVersion{ .major = 2, .minor = 37, .patch = 0 }, try glibcVerFromLinkName("ld-2.37.0.so", "ld-"));
709 try std.testing.expectEqual(std.SemanticVersion{ .major = 2, .minor = 37, .patch = 1 }, try glibcVerFromLinkName("ld-2.37.1.so", "ld-"));
710 try std.testing.expectError(error.InvalidGnuLibCVersion, glibcVerFromLinkName("ld-2.37.4.5.so", "ld-"));
711}
712
713fn glibcVerFromRPath(rpath: []const u8) !std.SemanticVersion {
714 var dir = fs.cwd().openDir(rpath, .{}) catch |err| switch (err) {
715 error.NameTooLong => unreachable,
716 error.InvalidUtf8 => unreachable,
717 error.BadPathName => unreachable,
718 error.DeviceBusy => unreachable,
719 error.NetworkNotFound => unreachable, // Windows-only
720
721 error.FileNotFound,
722 error.NotDir,
723 error.InvalidHandle,
724 error.AccessDenied,
725 error.NoDevice,
726 => return error.GLibCNotFound,
727
728 error.ProcessFdQuotaExceeded,
729 error.SystemFdQuotaExceeded,
730 error.SystemResources,
731 error.SymLinkLoop,
732 error.Unexpected,
733 => |e| return e,
734 };
735 defer dir.close();
736
737 // Now we have a candidate for the path to libc shared object. In
738 // the past, we used readlink() here because the link name would
739 // reveal the glibc version. However, in more recent GNU/Linux
740 // installations, there is no symlink. Thus we instead use a more
741 // robust check of opening the libc shared object and looking at the
742 // .dynstr section, and finding the max version number of symbols
743 // that start with "GLIBC_2.".
744 const glibc_so_basename = "libc.so.6";
745 var f = dir.openFile(glibc_so_basename, .{}) catch |err| switch (err) {
746 error.NameTooLong => unreachable,
747 error.InvalidUtf8 => unreachable, // Windows only
748 error.BadPathName => unreachable, // Windows only
749 error.PipeBusy => unreachable, // Windows-only
750 error.SharingViolation => unreachable, // Windows-only
751 error.NetworkNotFound => unreachable, // Windows-only
752 error.FileLocksNotSupported => unreachable, // No lock requested.
753 error.NoSpaceLeft => unreachable, // read-only
754 error.PathAlreadyExists => unreachable, // read-only
755 error.DeviceBusy => unreachable, // read-only
756 error.FileBusy => unreachable, // read-only
757 error.InvalidHandle => unreachable, // should not be in the error set
758 error.WouldBlock => unreachable, // not using O_NONBLOCK
759 error.NoDevice => unreachable, // not asking for a special device
760
761 error.AccessDenied,
762 error.FileNotFound,
763 error.NotDir,
764 error.IsDir,
765 => return error.GLibCNotFound,
766
767 error.FileTooBig => return error.Unexpected,
768
769 error.ProcessFdQuotaExceeded,
770 error.SystemFdQuotaExceeded,
771 error.SystemResources,
772 error.SymLinkLoop,
773 error.Unexpected,
774 => |e| return e,
775 };
776 defer f.close();
777
778 return glibcVerFromSoFile(f) catch |err| switch (err) {
779 error.InvalidElfMagic,
780 error.InvalidElfEndian,
781 error.InvalidElfClass,
782 error.InvalidElfFile,
783 error.InvalidElfVersion,
784 error.InvalidGnuLibCVersion,
785 error.UnexpectedEndOfFile,
786 => return error.GLibCNotFound,
787
788 error.SystemResources,
789 error.UnableToReadElfFile,
790 error.Unexpected,
791 error.FileSystem,
792 => |e| return e,
793 };
794}
795
796fn glibcVerFromSoFile(file: fs.File) !std.SemanticVersion {
797 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 align(@alignOf(elf.Elf64_Ehdr)) = undefined;
798 _ = try preadMin(file, &hdr_buf, 0, hdr_buf.len);
799 const hdr32 = @as(*elf.Elf32_Ehdr, @ptrCast(&hdr_buf));
800 const hdr64 = @as(*elf.Elf64_Ehdr, @ptrCast(&hdr_buf));
801 if (!mem.eql(u8, hdr32.e_ident[0..4], elf.MAGIC)) return error.InvalidElfMagic;
802 const elf_endian: std.builtin.Endian = switch (hdr32.e_ident[elf.EI_DATA]) {
803 elf.ELFDATA2LSB => .little,
804 elf.ELFDATA2MSB => .big,
805 else => return error.InvalidElfEndian,
806 };
807 const need_bswap = elf_endian != native_endian;
808 if (hdr32.e_ident[elf.EI_VERSION] != 1) return error.InvalidElfVersion;
809
810 const is_64 = switch (hdr32.e_ident[elf.EI_CLASS]) {
811 elf.ELFCLASS32 => false,
812 elf.ELFCLASS64 => true,
813 else => return error.InvalidElfClass,
814 };
815 const shstrndx = elfInt(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx);
816 var shoff = elfInt(is_64, need_bswap, hdr32.e_shoff, hdr64.e_shoff);
817 const shentsize = elfInt(is_64, need_bswap, hdr32.e_shentsize, hdr64.e_shentsize);
818 const str_section_off = shoff + @as(u64, shentsize) * @as(u64, shstrndx);
819 var sh_buf: [16 * @sizeOf(elf.Elf64_Shdr)]u8 align(@alignOf(elf.Elf64_Shdr)) = undefined;
820 if (sh_buf.len < shentsize) return error.InvalidElfFile;
821
822 _ = try preadMin(file, &sh_buf, str_section_off, shentsize);
823 const shstr32: *elf.Elf32_Shdr = @ptrCast(@alignCast(&sh_buf));
824 const shstr64: *elf.Elf64_Shdr = @ptrCast(@alignCast(&sh_buf));
825 const shstrtab_off = elfInt(is_64, need_bswap, shstr32.sh_offset, shstr64.sh_offset);
826 const shstrtab_size = elfInt(is_64, need_bswap, shstr32.sh_size, shstr64.sh_size);
827 var strtab_buf: [4096:0]u8 = undefined;
828 const shstrtab_len = @min(shstrtab_size, strtab_buf.len);
829 const shstrtab_read_len = try preadMin(file, &strtab_buf, shstrtab_off, shstrtab_len);
830 const shstrtab = strtab_buf[0..shstrtab_read_len];
831 const shnum = elfInt(is_64, need_bswap, hdr32.e_shnum, hdr64.e_shnum);
832 var sh_i: u16 = 0;
833 const dynstr: struct { offset: u64, size: u64 } = find_dyn_str: while (sh_i < shnum) {
834 // Reserve some bytes so that we can deref the 64-bit struct fields
835 // even when the ELF file is 32-bits.
836 const sh_reserve: usize = @sizeOf(elf.Elf64_Shdr) - @sizeOf(elf.Elf32_Shdr);
837 const sh_read_byte_len = try preadMin(
838 file,
839 sh_buf[0 .. sh_buf.len - sh_reserve],
840 shoff,
841 shentsize,
842 );
843 var sh_buf_i: usize = 0;
844 while (sh_buf_i < sh_read_byte_len and sh_i < shnum) : ({
845 sh_i += 1;
846 shoff += shentsize;
847 sh_buf_i += shentsize;
848 }) {
849 const sh32: *elf.Elf32_Shdr = @ptrCast(@alignCast(&sh_buf[sh_buf_i]));
850 const sh64: *elf.Elf64_Shdr = @ptrCast(@alignCast(&sh_buf[sh_buf_i]));
851 const sh_name_off = elfInt(is_64, need_bswap, sh32.sh_name, sh64.sh_name);
852 const sh_name = mem.sliceTo(shstrtab[sh_name_off..], 0);
853 if (mem.eql(u8, sh_name, ".dynstr")) {
854 break :find_dyn_str .{
855 .offset = elfInt(is_64, need_bswap, sh32.sh_offset, sh64.sh_offset),
856 .size = elfInt(is_64, need_bswap, sh32.sh_size, sh64.sh_size),
857 };
858 }
859 }
860 } else return error.InvalidGnuLibCVersion;
861
862 // Here we loop over all the strings in the dynstr string table, assuming that any
863 // strings that start with "GLIBC_2." indicate the existence of such a glibc version,
864 // and furthermore, that the system-installed glibc is at minimum that version.
865
866 // Empirically, glibc 2.34 libc.so .dynstr section is 32441 bytes on my system.
867 // Here I use double this value plus some headroom. This makes it only need
868 // a single read syscall here.
869 var buf: [80000]u8 = undefined;
870 if (buf.len < dynstr.size) return error.InvalidGnuLibCVersion;
871
872 const dynstr_size: usize = @intCast(dynstr.size);
873 const dynstr_bytes = buf[0..dynstr_size];
874 _ = try preadMin(file, dynstr_bytes, dynstr.offset, dynstr_bytes.len);
875 var it = mem.splitScalar(u8, dynstr_bytes, 0);
876 var max_ver: std.SemanticVersion = .{ .major = 2, .minor = 2, .patch = 5 };
877 while (it.next()) |s| {
878 if (mem.startsWith(u8, s, "GLIBC_2.")) {
879 const chopped = s["GLIBC_".len..];
880 const ver = Target.Query.parseVersion(chopped) catch |err| switch (err) {
881 error.Overflow => return error.InvalidGnuLibCVersion,
882 error.InvalidVersion => return error.InvalidGnuLibCVersion,
883 };
884 switch (ver.order(max_ver)) {
885 .gt => max_ver = ver,
886 .lt, .eq => continue,
887 }
888 }
889 }
890 return max_ver;
891}
892
893/// In the past, this function attempted to use the executable's own binary if it was dynamically
894/// linked to answer both the C ABI question and the dynamic linker question. However, this
895/// could be problematic on a system that uses a RUNPATH for the compiler binary, locking
896/// it to an older glibc version, while system binaries such as /usr/bin/env use a newer glibc
897/// version. The problem is that libc.so.6 glibc version will match that of the system while
898/// the dynamic linker will match that of the compiler binary. Executables with these versions
899/// mismatching will fail to run.
900///
901/// Therefore, this function works the same regardless of whether the compiler binary is
902/// dynamically or statically linked. It inspects `/usr/bin/env` as an ELF file to find the
903/// answer to these questions, or if there is a shebang line, then it chases the referenced
904/// file recursively. If that does not provide the answer, then the function falls back to
905/// defaults.
906fn detectAbiAndDynamicLinker(
907 cpu: Target.Cpu,
908 os: Target.Os,
909 query: Target.Query,
910) DetectError!Target {
911 const native_target_has_ld = comptime builtin.target.hasDynamicLinker();
912 const is_linux = builtin.target.os.tag == .linux;
913 const is_solarish = builtin.target.os.tag.isSolarish();
914 const have_all_info = query.dynamic_linker.get() != null and
915 query.abi != null and (!is_linux or query.abi.?.isGnu());
916 const os_is_non_native = query.os_tag != null;
917 // The Solaris/illumos environment is always the same.
918 if (!native_target_has_ld or have_all_info or os_is_non_native or is_solarish) {
919 return defaultAbiAndDynamicLinker(cpu, os, query);
920 }
921 if (query.abi) |abi| {
922 if (abi.isMusl()) {
923 // musl implies static linking.
924 return defaultAbiAndDynamicLinker(cpu, os, query);
925 }
926 }
927 // The current target's ABI cannot be relied on for this. For example, we may build the zig
928 // compiler for target riscv64-linux-musl and provide a tarball for users to download.
929 // A user could then run that zig compiler on riscv64-linux-gnu. This use case is well-defined
930 // and supported by Zig. But that means that we must detect the system ABI here rather than
931 // relying on `builtin.target`.
932 const all_abis = comptime blk: {
933 assert(@intFromEnum(Target.Abi.none) == 0);
934 const fields = std.meta.fields(Target.Abi)[1..];
935 var array: [fields.len]Target.Abi = undefined;
936 for (fields, 0..) |field, i| {
937 array[i] = @field(Target.Abi, field.name);
938 }
939 break :blk array;
940 };
941 var ld_info_list_buffer: [all_abis.len]LdInfo = undefined;
942 var ld_info_list_len: usize = 0;
943 const ofmt = query.ofmt orelse Target.ObjectFormat.default(os.tag, cpu.arch);
944
945 for (all_abis) |abi| {
946 // This may be a nonsensical parameter. We detect this with
947 // error.UnknownDynamicLinkerPath and skip adding it to `ld_info_list`.
948 const target: Target = .{
949 .cpu = cpu,
950 .os = os,
951 .abi = abi,
952 .ofmt = ofmt,
953 };
954 const ld = target.standardDynamicLinkerPath();
955 if (ld.get() == null) continue;
956
957 ld_info_list_buffer[ld_info_list_len] = .{
958 .ld = ld,
959 .abi = abi,
960 };
961 ld_info_list_len += 1;
962 }
963 const ld_info_list = ld_info_list_buffer[0..ld_info_list_len];
964
965 // Best case scenario: the executable is dynamically linked, and we can iterate
966 // over our own shared objects and find a dynamic linker.
967 const elf_file = blk: {
968 // This block looks for a shebang line in /usr/bin/env,
969 // if it finds one, then instead of using /usr/bin/env as the ELF file to examine, it uses the file it references instead,
970 // doing the same logic recursively in case it finds another shebang line.
971
972 // Since /usr/bin/env is hard-coded into the shebang line of many portable scripts, it's a
973 // reasonably reliable path to start with.
974 var file_name: []const u8 = "/usr/bin/env";
975 // #! (2) + 255 (max length of shebang line since Linux 5.1) + \n (1)
976 var buffer: [258]u8 = undefined;
977 while (true) {
978 const file = fs.openFileAbsolute(file_name, .{}) catch |err| switch (err) {
979 error.NoSpaceLeft => unreachable,
980 error.NameTooLong => unreachable,
981 error.PathAlreadyExists => unreachable,
982 error.SharingViolation => unreachable,
983 error.InvalidUtf8 => unreachable,
984 error.BadPathName => unreachable,
985 error.PipeBusy => unreachable,
986 error.FileLocksNotSupported => unreachable,
987 error.WouldBlock => unreachable,
988 error.FileBusy => unreachable, // opened without write permissions
989
990 error.IsDir,
991 error.NotDir,
992 error.InvalidHandle,
993 error.AccessDenied,
994 error.NoDevice,
995 error.FileNotFound,
996 error.NetworkNotFound,
997 error.FileTooBig,
998 error.Unexpected,
999 => |e| {
1000 std.log.warn("Encountered error: {s}, falling back to default ABI and dynamic linker.\n", .{@errorName(e)});
1001 return defaultAbiAndDynamicLinker(cpu, os, query);
1002 },
1003
1004 else => |e| return e,
1005 };
1006 errdefer file.close();
1007
1008 const len = preadMin(file, &buffer, 0, buffer.len) catch |err| switch (err) {
1009 error.UnexpectedEndOfFile,
1010 error.UnableToReadElfFile,
1011 => break :blk file,
1012
1013 else => |e| return e,
1014 };
1015 const newline = mem.indexOfScalar(u8, buffer[0..len], '\n') orelse break :blk file;
1016 const line = buffer[0..newline];
1017 if (!mem.startsWith(u8, line, "#!")) break :blk file;
1018 var it = mem.tokenizeScalar(u8, line[2..], ' ');
1019 file_name = it.next() orelse return defaultAbiAndDynamicLinker(cpu, os, query);
1020 file.close();
1021 }
1022 };
1023 defer elf_file.close();
1024
1025 // If Zig is statically linked, such as via distributed binary static builds, the above
1026 // trick (block self_exe) won't work. The next thing we fall back to is the same thing, but for elf_file.
1027 // TODO: inline this function and combine the buffer we already read above to find
1028 // the possible shebang line with the buffer we use for the ELF header.
1029 return abiAndDynamicLinkerFromFile(elf_file, cpu, os, ld_info_list, query) catch |err| switch (err) {
1030 error.FileSystem,
1031 error.SystemResources,
1032 error.SymLinkLoop,
1033 error.ProcessFdQuotaExceeded,
1034 error.SystemFdQuotaExceeded,
1035 => |e| return e,
1036
1037 error.UnableToReadElfFile,
1038 error.InvalidElfClass,
1039 error.InvalidElfVersion,
1040 error.InvalidElfEndian,
1041 error.InvalidElfFile,
1042 error.InvalidElfMagic,
1043 error.Unexpected,
1044 error.UnexpectedEndOfFile,
1045 error.NameTooLong,
1046 // Finally, we fall back on the standard path.
1047 => |e| {
1048 std.log.warn("Encountered error: {s}, falling back to default ABI and dynamic linker.\n", .{@errorName(e)});
1049 return defaultAbiAndDynamicLinker(cpu, os, query);
1050 },
1051 };
1052}
1053
1054fn defaultAbiAndDynamicLinker(cpu: Target.Cpu, os: Target.Os, query: Target.Query) !Target {
1055 const abi = query.abi orelse Target.Abi.default(cpu.arch, os);
1056 return .{
1057 .cpu = cpu,
1058 .os = os,
1059 .abi = abi,
1060 .ofmt = query.ofmt orelse Target.ObjectFormat.default(os.tag, cpu.arch),
1061 .dynamic_linker = if (query.dynamic_linker.get() == null)
1062 Target.standardDynamicLinkerPath_cpu_os_abi(cpu, os.tag, abi)
1063 else
1064 query.dynamic_linker,
1065 };
1066}
1067
1068const LdInfo = struct {
1069 ld: Target.DynamicLinker,
1070 abi: Target.Abi,
1071};
1072
1073fn preadMin(file: fs.File, buf: []u8, offset: u64, min_read_len: usize) !usize {
1074 var i: usize = 0;
1075 while (i < min_read_len) {
1076 const len = file.pread(buf[i..], offset + i) catch |err| switch (err) {
1077 error.OperationAborted => unreachable, // Windows-only
1078 error.WouldBlock => unreachable, // Did not request blocking mode
1079 error.NotOpenForReading => unreachable,
1080 error.SystemResources => return error.SystemResources,
1081 error.IsDir => return error.UnableToReadElfFile,
1082 error.BrokenPipe => return error.UnableToReadElfFile,
1083 error.Unseekable => return error.UnableToReadElfFile,
1084 error.ConnectionResetByPeer => return error.UnableToReadElfFile,
1085 error.ConnectionTimedOut => return error.UnableToReadElfFile,
1086 error.SocketNotConnected => return error.UnableToReadElfFile,
1087 error.NetNameDeleted => return error.UnableToReadElfFile,
1088 error.Unexpected => return error.Unexpected,
1089 error.InputOutput => return error.FileSystem,
1090 error.AccessDenied => return error.Unexpected,
1091 };
1092 if (len == 0) return error.UnexpectedEndOfFile;
1093 i += len;
1094 }
1095 return i;
1096}
1097
1098fn elfInt(is_64: bool, need_bswap: bool, int_32: anytype, int_64: anytype) @TypeOf(int_64) {
1099 if (is_64) {
1100 if (need_bswap) {
1101 return @byteSwap(int_64);
1102 } else {
1103 return int_64;
1104 }
1105 } else {
1106 if (need_bswap) {
1107 return @byteSwap(int_32);
1108 } else {
1109 return int_32;
1110 }
1111 }
1112}
1113
1114const builtin = @import("builtin");
1115const std = @import("../std.zig");
1116const mem = std.mem;
1117const elf = std.elf;
1118const fs = std.fs;
1119const assert = std.debug.assert;
1120const Target = std.Target;
1121const native_endian = builtin.cpu.arch.endian();
1122
8test {1123test {
9 _ = NativePaths;1124 _ = NativePaths;
10 _ = NativeTargetInfo;
111125
12 _ = darwin;1126 _ = darwin;
13 _ = linux;1127 _ = linux;
lib/std/zig/system/NativePaths.zig+1-3
...@@ -5,7 +5,6 @@ const process = std.process;...@@ -5,7 +5,6 @@ const process = std.process;
5const mem = std.mem;5const mem = std.mem;
66
7const NativePaths = @This();7const NativePaths = @This();
8const NativeTargetInfo = std.zig.system.NativeTargetInfo;
98
10arena: Allocator,9arena: Allocator,
11include_dirs: std.ArrayListUnmanaged([]const u8) = .{},10include_dirs: std.ArrayListUnmanaged([]const u8) = .{},
...@@ -14,8 +13,7 @@ framework_dirs: std.ArrayListUnmanaged([]const u8) = .{},...@@ -14,8 +13,7 @@ framework_dirs: std.ArrayListUnmanaged([]const u8) = .{},
14rpaths: std.ArrayListUnmanaged([]const u8) = .{},13rpaths: std.ArrayListUnmanaged([]const u8) = .{},
15warnings: std.ArrayListUnmanaged([]const u8) = .{},14warnings: std.ArrayListUnmanaged([]const u8) = .{},
1615
17pub fn detect(arena: Allocator, native_info: NativeTargetInfo) !NativePaths {16pub fn detect(arena: Allocator, native_target: std.Target) !NativePaths {
18 const native_target = native_info.target;
19 var self: NativePaths = .{ .arena = arena };17 var self: NativePaths = .{ .arena = arena };
20 var is_nix = false;18 var is_nix = false;
21 if (process.getEnvVarOwned(arena, "NIX_CFLAGS_COMPILE")) |nix_cflags_compile| {19 if (process.getEnvVarOwned(arena, "NIX_CFLAGS_COMPILE")) |nix_cflags_compile| {
lib/std/zig/system/NativeTargetInfo.zig deleted-1130
...@@ -1,1130 +0,0 @@
1const std = @import("../../std.zig");
2const builtin = @import("builtin");
3const mem = std.mem;
4const assert = std.debug.assert;
5const fs = std.fs;
6const elf = std.elf;
7const native_endian = builtin.cpu.arch.endian();
8
9const NativeTargetInfo = @This();
10const Target = std.Target;
11const Allocator = std.mem.Allocator;
12const windows = std.zig.system.windows;
13const darwin = std.zig.system.darwin;
14const linux = std.zig.system.linux;
15
16target: Target,
17dynamic_linker: DynamicLinker = DynamicLinker{},
18
19pub const DynamicLinker = Target.DynamicLinker;
20
21pub const DetectError = error{
22 FileSystem,
23 SystemResources,
24 SymLinkLoop,
25 ProcessFdQuotaExceeded,
26 SystemFdQuotaExceeded,
27 DeviceBusy,
28 OSVersionDetectionFail,
29 Unexpected,
30};
31
32/// Given a `Target.Query`, which specifies in detail which parts of the
33/// target should be detected natively, which should be standard or default,
34/// and which are provided explicitly, this function resolves the native
35/// components by detecting the native system, and then resolves
36/// standard/default parts relative to that.
37pub fn detect(query: Target.Query) DetectError!NativeTargetInfo {
38 var os = query.getOsTag().defaultVersionRange(query.getCpuArch());
39 if (query.os_tag == null) {
40 switch (builtin.target.os.tag) {
41 .linux => {
42 const uts = std.os.uname();
43 const release = mem.sliceTo(&uts.release, 0);
44 // The release field sometimes has a weird format,
45 // `Version.parse` will attempt to find some meaningful interpretation.
46 if (std.SemanticVersion.parse(release)) |ver| {
47 os.version_range.linux.range.min = ver;
48 os.version_range.linux.range.max = ver;
49 } else |err| switch (err) {
50 error.Overflow => {},
51 error.InvalidVersion => {},
52 }
53 },
54 .solaris, .illumos => {
55 const uts = std.os.uname();
56 const release = mem.sliceTo(&uts.release, 0);
57 if (std.SemanticVersion.parse(release)) |ver| {
58 os.version_range.semver.min = ver;
59 os.version_range.semver.max = ver;
60 } else |err| switch (err) {
61 error.Overflow => {},
62 error.InvalidVersion => {},
63 }
64 },
65 .windows => {
66 const detected_version = windows.detectRuntimeVersion();
67 os.version_range.windows.min = detected_version;
68 os.version_range.windows.max = detected_version;
69 },
70 .macos => try darwin.macos.detect(&os),
71 .freebsd, .netbsd, .dragonfly => {
72 const key = switch (builtin.target.os.tag) {
73 .freebsd => "kern.osreldate",
74 .netbsd, .dragonfly => "kern.osrevision",
75 else => unreachable,
76 };
77 var value: u32 = undefined;
78 var len: usize = @sizeOf(@TypeOf(value));
79
80 std.os.sysctlbynameZ(key, &value, &len, null, 0) catch |err| switch (err) {
81 error.NameTooLong => unreachable, // constant, known good value
82 error.PermissionDenied => unreachable, // only when setting values,
83 error.SystemResources => unreachable, // memory already on the stack
84 error.UnknownName => unreachable, // constant, known good value
85 error.Unexpected => return error.OSVersionDetectionFail,
86 };
87
88 switch (builtin.target.os.tag) {
89 .freebsd => {
90 // https://www.freebsd.org/doc/en_US.ISO8859-1/books/porters-handbook/versions.html
91 // Major * 100,000 has been convention since FreeBSD 2.2 (1997)
92 // Minor * 1(0),000 summed has been convention since FreeBSD 2.2 (1997)
93 // e.g. 492101 = 4.11-STABLE = 4.(9+2)
94 const major = value / 100_000;
95 const minor1 = value % 100_000 / 10_000; // usually 0 since 5.1
96 const minor2 = value % 10_000 / 1_000; // 0 before 5.1, minor version since
97 const patch = value % 1_000;
98 os.version_range.semver.min = .{ .major = major, .minor = minor1 + minor2, .patch = patch };
99 os.version_range.semver.max = os.version_range.semver.min;
100 },
101 .netbsd => {
102 // #define __NetBSD_Version__ MMmmrrpp00
103 //
104 // M = major version
105 // m = minor version; a minor number of 99 indicates current.
106 // r = 0 (*)
107 // p = patchlevel
108 const major = value / 100_000_000;
109 const minor = value % 100_000_000 / 1_000_000;
110 const patch = value % 10_000 / 100;
111 os.version_range.semver.min = .{ .major = major, .minor = minor, .patch = patch };
112 os.version_range.semver.max = os.version_range.semver.min;
113 },
114 .dragonfly => {
115 // https://github.com/DragonFlyBSD/DragonFlyBSD/blob/cb2cde83771754aeef9bb3251ee48959138dec87/Makefile.inc1#L15-L17
116 // flat base10 format: Mmmmpp
117 // M = major
118 // m = minor; odd-numbers indicate current dev branch
119 // p = patch
120 const major = value / 100_000;
121 const minor = value % 100_000 / 100;
122 const patch = value % 100;
123 os.version_range.semver.min = .{ .major = major, .minor = minor, .patch = patch };
124 os.version_range.semver.max = os.version_range.semver.min;
125 },
126 else => unreachable,
127 }
128 },
129 .openbsd => {
130 const mib: [2]c_int = [_]c_int{
131 std.os.CTL.KERN,
132 std.os.KERN.OSRELEASE,
133 };
134 var buf: [64]u8 = undefined;
135 // consider that sysctl result includes null-termination
136 // reserve 1 byte to ensure we never overflow when appending ".0"
137 var len: usize = buf.len - 1;
138
139 std.os.sysctl(&mib, &buf, &len, null, 0) catch |err| switch (err) {
140 error.NameTooLong => unreachable, // constant, known good value
141 error.PermissionDenied => unreachable, // only when setting values,
142 error.SystemResources => unreachable, // memory already on the stack
143 error.UnknownName => unreachable, // constant, known good value
144 error.Unexpected => return error.OSVersionDetectionFail,
145 };
146
147 // append ".0" to satisfy semver
148 buf[len - 1] = '.';
149 buf[len] = '0';
150 len += 1;
151
152 if (std.SemanticVersion.parse(buf[0..len])) |ver| {
153 os.version_range.semver.min = ver;
154 os.version_range.semver.max = ver;
155 } else |_| {
156 return error.OSVersionDetectionFail;
157 }
158 },
159 else => {
160 // Unimplemented, fall back to default version range.
161 },
162 }
163 }
164
165 if (query.os_version_min) |min| switch (min) {
166 .none => {},
167 .semver => |semver| switch (query.getOsTag()) {
168 .linux => os.version_range.linux.range.min = semver,
169 else => os.version_range.semver.min = semver,
170 },
171 .windows => |win_ver| os.version_range.windows.min = win_ver,
172 };
173
174 if (query.os_version_max) |max| switch (max) {
175 .none => {},
176 .semver => |semver| switch (query.getOsTag()) {
177 .linux => os.version_range.linux.range.max = semver,
178 else => os.version_range.semver.max = semver,
179 },
180 .windows => |win_ver| os.version_range.windows.max = win_ver,
181 };
182
183 if (query.glibc_version) |glibc| {
184 assert(query.isGnuLibC());
185 os.version_range.linux.glibc = glibc;
186 }
187
188 // Until https://github.com/ziglang/zig/issues/4592 is implemented (support detecting the
189 // native CPU architecture as being different than the current target), we use this:
190 const cpu_arch = query.getCpuArch();
191
192 const cpu = switch (query.cpu_model) {
193 .native => detectNativeCpuAndFeatures(cpu_arch, os, query),
194 .baseline => Target.Cpu.baseline(cpu_arch),
195 .determined_by_cpu_arch => if (query.cpu_arch == null)
196 detectNativeCpuAndFeatures(cpu_arch, os, query)
197 else
198 Target.Cpu.baseline(cpu_arch),
199 .explicit => |model| model.toCpu(cpu_arch),
200 } orelse backup_cpu_detection: {
201 break :backup_cpu_detection Target.Cpu.baseline(cpu_arch);
202 };
203 var result = try detectAbiAndDynamicLinker(cpu, os, query);
204 // For x86, we need to populate some CPU feature flags depending on architecture
205 // and mode:
206 // * 16bit_mode => if the abi is code16
207 // * 32bit_mode => if the arch is x86
208 // However, the "mode" flags can be used as overrides, so if the user explicitly
209 // sets one of them, that takes precedence.
210 switch (cpu_arch) {
211 .x86 => {
212 if (!Target.x86.featureSetHasAny(query.cpu_features_add, .{
213 .@"16bit_mode", .@"32bit_mode",
214 })) {
215 switch (result.target.abi) {
216 .code16 => result.target.cpu.features.addFeature(
217 @intFromEnum(Target.x86.Feature.@"16bit_mode"),
218 ),
219 else => result.target.cpu.features.addFeature(
220 @intFromEnum(Target.x86.Feature.@"32bit_mode"),
221 ),
222 }
223 }
224 },
225 .arm, .armeb => {
226 // XXX What do we do if the target has the noarm feature?
227 // What do we do if the user specifies +thumb_mode?
228 },
229 .thumb, .thumbeb => {
230 result.target.cpu.features.addFeature(
231 @intFromEnum(Target.arm.Feature.thumb_mode),
232 );
233 },
234 else => {},
235 }
236 query.updateCpuFeatures(&result.target.cpu.features);
237 return result;
238}
239
240/// In the past, this function attempted to use the executable's own binary if it was dynamically
241/// linked to answer both the C ABI question and the dynamic linker question. However, this
242/// could be problematic on a system that uses a RUNPATH for the compiler binary, locking
243/// it to an older glibc version, while system binaries such as /usr/bin/env use a newer glibc
244/// version. The problem is that libc.so.6 glibc version will match that of the system while
245/// the dynamic linker will match that of the compiler binary. Executables with these versions
246/// mismatching will fail to run.
247///
248/// Therefore, this function works the same regardless of whether the compiler binary is
249/// dynamically or statically linked. It inspects `/usr/bin/env` as an ELF file to find the
250/// answer to these questions, or if there is a shebang line, then it chases the referenced
251/// file recursively. If that does not provide the answer, then the function falls back to
252/// defaults.
253fn detectAbiAndDynamicLinker(
254 cpu: Target.Cpu,
255 os: Target.Os,
256 query: Target.Query,
257) DetectError!NativeTargetInfo {
258 const native_target_has_ld = comptime builtin.target.hasDynamicLinker();
259 const is_linux = builtin.target.os.tag == .linux;
260 const is_solarish = builtin.target.os.tag.isSolarish();
261 const have_all_info = query.dynamic_linker.get() != null and
262 query.abi != null and (!is_linux or query.abi.?.isGnu());
263 const os_is_non_native = query.os_tag != null;
264 // The Solaris/illumos environment is always the same.
265 if (!native_target_has_ld or have_all_info or os_is_non_native or is_solarish) {
266 return defaultAbiAndDynamicLinker(cpu, os, query);
267 }
268 if (query.abi) |abi| {
269 if (abi.isMusl()) {
270 // musl implies static linking.
271 return defaultAbiAndDynamicLinker(cpu, os, query);
272 }
273 }
274 // The current target's ABI cannot be relied on for this. For example, we may build the zig
275 // compiler for target riscv64-linux-musl and provide a tarball for users to download.
276 // A user could then run that zig compiler on riscv64-linux-gnu. This use case is well-defined
277 // and supported by Zig. But that means that we must detect the system ABI here rather than
278 // relying on `builtin.target`.
279 const all_abis = comptime blk: {
280 assert(@intFromEnum(Target.Abi.none) == 0);
281 const fields = std.meta.fields(Target.Abi)[1..];
282 var array: [fields.len]Target.Abi = undefined;
283 for (fields, 0..) |field, i| {
284 array[i] = @field(Target.Abi, field.name);
285 }
286 break :blk array;
287 };
288 var ld_info_list_buffer: [all_abis.len]LdInfo = undefined;
289 var ld_info_list_len: usize = 0;
290 const ofmt = query.ofmt orelse Target.ObjectFormat.default(os.tag, cpu.arch);
291
292 for (all_abis) |abi| {
293 // This may be a nonsensical parameter. We detect this with
294 // error.UnknownDynamicLinkerPath and skip adding it to `ld_info_list`.
295 const target: Target = .{
296 .cpu = cpu,
297 .os = os,
298 .abi = abi,
299 .ofmt = ofmt,
300 };
301 const ld = target.standardDynamicLinkerPath();
302 if (ld.get() == null) continue;
303
304 ld_info_list_buffer[ld_info_list_len] = .{
305 .ld = ld,
306 .abi = abi,
307 };
308 ld_info_list_len += 1;
309 }
310 const ld_info_list = ld_info_list_buffer[0..ld_info_list_len];
311
312 // Best case scenario: the executable is dynamically linked, and we can iterate
313 // over our own shared objects and find a dynamic linker.
314 const elf_file = blk: {
315 // This block looks for a shebang line in /usr/bin/env,
316 // if it finds one, then instead of using /usr/bin/env as the ELF file to examine, it uses the file it references instead,
317 // doing the same logic recursively in case it finds another shebang line.
318
319 // Since /usr/bin/env is hard-coded into the shebang line of many portable scripts, it's a
320 // reasonably reliable path to start with.
321 var file_name: []const u8 = "/usr/bin/env";
322 // #! (2) + 255 (max length of shebang line since Linux 5.1) + \n (1)
323 var buffer: [258]u8 = undefined;
324 while (true) {
325 const file = fs.openFileAbsolute(file_name, .{}) catch |err| switch (err) {
326 error.NoSpaceLeft => unreachable,
327 error.NameTooLong => unreachable,
328 error.PathAlreadyExists => unreachable,
329 error.SharingViolation => unreachable,
330 error.InvalidUtf8 => unreachable,
331 error.BadPathName => unreachable,
332 error.PipeBusy => unreachable,
333 error.FileLocksNotSupported => unreachable,
334 error.WouldBlock => unreachable,
335 error.FileBusy => unreachable, // opened without write permissions
336
337 error.IsDir,
338 error.NotDir,
339 error.InvalidHandle,
340 error.AccessDenied,
341 error.NoDevice,
342 error.FileNotFound,
343 error.NetworkNotFound,
344 error.FileTooBig,
345 error.Unexpected,
346 => |e| {
347 std.log.warn("Encountered error: {s}, falling back to default ABI and dynamic linker.\n", .{@errorName(e)});
348 return defaultAbiAndDynamicLinker(cpu, os, query);
349 },
350
351 else => |e| return e,
352 };
353 errdefer file.close();
354
355 const len = preadMin(file, &buffer, 0, buffer.len) catch |err| switch (err) {
356 error.UnexpectedEndOfFile,
357 error.UnableToReadElfFile,
358 => break :blk file,
359
360 else => |e| return e,
361 };
362 const newline = mem.indexOfScalar(u8, buffer[0..len], '\n') orelse break :blk file;
363 const line = buffer[0..newline];
364 if (!mem.startsWith(u8, line, "#!")) break :blk file;
365 var it = mem.tokenizeScalar(u8, line[2..], ' ');
366 file_name = it.next() orelse return defaultAbiAndDynamicLinker(cpu, os, query);
367 file.close();
368 }
369 };
370 defer elf_file.close();
371
372 // If Zig is statically linked, such as via distributed binary static builds, the above
373 // trick (block self_exe) won't work. The next thing we fall back to is the same thing, but for elf_file.
374 // TODO: inline this function and combine the buffer we already read above to find
375 // the possible shebang line with the buffer we use for the ELF header.
376 return abiAndDynamicLinkerFromFile(elf_file, cpu, os, ld_info_list, query) catch |err| switch (err) {
377 error.FileSystem,
378 error.SystemResources,
379 error.SymLinkLoop,
380 error.ProcessFdQuotaExceeded,
381 error.SystemFdQuotaExceeded,
382 => |e| return e,
383
384 error.UnableToReadElfFile,
385 error.InvalidElfClass,
386 error.InvalidElfVersion,
387 error.InvalidElfEndian,
388 error.InvalidElfFile,
389 error.InvalidElfMagic,
390 error.Unexpected,
391 error.UnexpectedEndOfFile,
392 error.NameTooLong,
393 // Finally, we fall back on the standard path.
394 => |e| {
395 std.log.warn("Encountered error: {s}, falling back to default ABI and dynamic linker.\n", .{@errorName(e)});
396 return defaultAbiAndDynamicLinker(cpu, os, query);
397 },
398 };
399}
400
401fn glibcVerFromRPath(rpath: []const u8) !std.SemanticVersion {
402 var dir = fs.cwd().openDir(rpath, .{}) catch |err| switch (err) {
403 error.NameTooLong => unreachable,
404 error.InvalidUtf8 => unreachable,
405 error.BadPathName => unreachable,
406 error.DeviceBusy => unreachable,
407 error.NetworkNotFound => unreachable, // Windows-only
408
409 error.FileNotFound,
410 error.NotDir,
411 error.InvalidHandle,
412 error.AccessDenied,
413 error.NoDevice,
414 => return error.GLibCNotFound,
415
416 error.ProcessFdQuotaExceeded,
417 error.SystemFdQuotaExceeded,
418 error.SystemResources,
419 error.SymLinkLoop,
420 error.Unexpected,
421 => |e| return e,
422 };
423 defer dir.close();
424
425 // Now we have a candidate for the path to libc shared object. In
426 // the past, we used readlink() here because the link name would
427 // reveal the glibc version. However, in more recent GNU/Linux
428 // installations, there is no symlink. Thus we instead use a more
429 // robust check of opening the libc shared object and looking at the
430 // .dynstr section, and finding the max version number of symbols
431 // that start with "GLIBC_2.".
432 const glibc_so_basename = "libc.so.6";
433 var f = dir.openFile(glibc_so_basename, .{}) catch |err| switch (err) {
434 error.NameTooLong => unreachable,
435 error.InvalidUtf8 => unreachable, // Windows only
436 error.BadPathName => unreachable, // Windows only
437 error.PipeBusy => unreachable, // Windows-only
438 error.SharingViolation => unreachable, // Windows-only
439 error.NetworkNotFound => unreachable, // Windows-only
440 error.FileLocksNotSupported => unreachable, // No lock requested.
441 error.NoSpaceLeft => unreachable, // read-only
442 error.PathAlreadyExists => unreachable, // read-only
443 error.DeviceBusy => unreachable, // read-only
444 error.FileBusy => unreachable, // read-only
445 error.InvalidHandle => unreachable, // should not be in the error set
446 error.WouldBlock => unreachable, // not using O_NONBLOCK
447 error.NoDevice => unreachable, // not asking for a special device
448
449 error.AccessDenied,
450 error.FileNotFound,
451 error.NotDir,
452 error.IsDir,
453 => return error.GLibCNotFound,
454
455 error.FileTooBig => return error.Unexpected,
456
457 error.ProcessFdQuotaExceeded,
458 error.SystemFdQuotaExceeded,
459 error.SystemResources,
460 error.SymLinkLoop,
461 error.Unexpected,
462 => |e| return e,
463 };
464 defer f.close();
465
466 return glibcVerFromSoFile(f) catch |err| switch (err) {
467 error.InvalidElfMagic,
468 error.InvalidElfEndian,
469 error.InvalidElfClass,
470 error.InvalidElfFile,
471 error.InvalidElfVersion,
472 error.InvalidGnuLibCVersion,
473 error.UnexpectedEndOfFile,
474 => return error.GLibCNotFound,
475
476 error.SystemResources,
477 error.UnableToReadElfFile,
478 error.Unexpected,
479 error.FileSystem,
480 => |e| return e,
481 };
482}
483
484fn glibcVerFromSoFile(file: fs.File) !std.SemanticVersion {
485 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 align(@alignOf(elf.Elf64_Ehdr)) = undefined;
486 _ = try preadMin(file, &hdr_buf, 0, hdr_buf.len);
487 const hdr32 = @as(*elf.Elf32_Ehdr, @ptrCast(&hdr_buf));
488 const hdr64 = @as(*elf.Elf64_Ehdr, @ptrCast(&hdr_buf));
489 if (!mem.eql(u8, hdr32.e_ident[0..4], elf.MAGIC)) return error.InvalidElfMagic;
490 const elf_endian: std.builtin.Endian = switch (hdr32.e_ident[elf.EI_DATA]) {
491 elf.ELFDATA2LSB => .little,
492 elf.ELFDATA2MSB => .big,
493 else => return error.InvalidElfEndian,
494 };
495 const need_bswap = elf_endian != native_endian;
496 if (hdr32.e_ident[elf.EI_VERSION] != 1) return error.InvalidElfVersion;
497
498 const is_64 = switch (hdr32.e_ident[elf.EI_CLASS]) {
499 elf.ELFCLASS32 => false,
500 elf.ELFCLASS64 => true,
501 else => return error.InvalidElfClass,
502 };
503 const shstrndx = elfInt(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx);
504 var shoff = elfInt(is_64, need_bswap, hdr32.e_shoff, hdr64.e_shoff);
505 const shentsize = elfInt(is_64, need_bswap, hdr32.e_shentsize, hdr64.e_shentsize);
506 const str_section_off = shoff + @as(u64, shentsize) * @as(u64, shstrndx);
507 var sh_buf: [16 * @sizeOf(elf.Elf64_Shdr)]u8 align(@alignOf(elf.Elf64_Shdr)) = undefined;
508 if (sh_buf.len < shentsize) return error.InvalidElfFile;
509
510 _ = try preadMin(file, &sh_buf, str_section_off, shentsize);
511 const shstr32: *elf.Elf32_Shdr = @ptrCast(@alignCast(&sh_buf));
512 const shstr64: *elf.Elf64_Shdr = @ptrCast(@alignCast(&sh_buf));
513 const shstrtab_off = elfInt(is_64, need_bswap, shstr32.sh_offset, shstr64.sh_offset);
514 const shstrtab_size = elfInt(is_64, need_bswap, shstr32.sh_size, shstr64.sh_size);
515 var strtab_buf: [4096:0]u8 = undefined;
516 const shstrtab_len = @min(shstrtab_size, strtab_buf.len);
517 const shstrtab_read_len = try preadMin(file, &strtab_buf, shstrtab_off, shstrtab_len);
518 const shstrtab = strtab_buf[0..shstrtab_read_len];
519 const shnum = elfInt(is_64, need_bswap, hdr32.e_shnum, hdr64.e_shnum);
520 var sh_i: u16 = 0;
521 const dynstr: struct { offset: u64, size: u64 } = find_dyn_str: while (sh_i < shnum) {
522 // Reserve some bytes so that we can deref the 64-bit struct fields
523 // even when the ELF file is 32-bits.
524 const sh_reserve: usize = @sizeOf(elf.Elf64_Shdr) - @sizeOf(elf.Elf32_Shdr);
525 const sh_read_byte_len = try preadMin(
526 file,
527 sh_buf[0 .. sh_buf.len - sh_reserve],
528 shoff,
529 shentsize,
530 );
531 var sh_buf_i: usize = 0;
532 while (sh_buf_i < sh_read_byte_len and sh_i < shnum) : ({
533 sh_i += 1;
534 shoff += shentsize;
535 sh_buf_i += shentsize;
536 }) {
537 const sh32: *elf.Elf32_Shdr = @ptrCast(@alignCast(&sh_buf[sh_buf_i]));
538 const sh64: *elf.Elf64_Shdr = @ptrCast(@alignCast(&sh_buf[sh_buf_i]));
539 const sh_name_off = elfInt(is_64, need_bswap, sh32.sh_name, sh64.sh_name);
540 const sh_name = mem.sliceTo(shstrtab[sh_name_off..], 0);
541 if (mem.eql(u8, sh_name, ".dynstr")) {
542 break :find_dyn_str .{
543 .offset = elfInt(is_64, need_bswap, sh32.sh_offset, sh64.sh_offset),
544 .size = elfInt(is_64, need_bswap, sh32.sh_size, sh64.sh_size),
545 };
546 }
547 }
548 } else return error.InvalidGnuLibCVersion;
549
550 // Here we loop over all the strings in the dynstr string table, assuming that any
551 // strings that start with "GLIBC_2." indicate the existence of such a glibc version,
552 // and furthermore, that the system-installed glibc is at minimum that version.
553
554 // Empirically, glibc 2.34 libc.so .dynstr section is 32441 bytes on my system.
555 // Here I use double this value plus some headroom. This makes it only need
556 // a single read syscall here.
557 var buf: [80000]u8 = undefined;
558 if (buf.len < dynstr.size) return error.InvalidGnuLibCVersion;
559
560 const dynstr_size: usize = @intCast(dynstr.size);
561 const dynstr_bytes = buf[0..dynstr_size];
562 _ = try preadMin(file, dynstr_bytes, dynstr.offset, dynstr_bytes.len);
563 var it = mem.splitScalar(u8, dynstr_bytes, 0);
564 var max_ver: std.SemanticVersion = .{ .major = 2, .minor = 2, .patch = 5 };
565 while (it.next()) |s| {
566 if (mem.startsWith(u8, s, "GLIBC_2.")) {
567 const chopped = s["GLIBC_".len..];
568 const ver = Target.Query.parseVersion(chopped) catch |err| switch (err) {
569 error.Overflow => return error.InvalidGnuLibCVersion,
570 error.InvalidVersion => return error.InvalidGnuLibCVersion,
571 };
572 switch (ver.order(max_ver)) {
573 .gt => max_ver = ver,
574 .lt, .eq => continue,
575 }
576 }
577 }
578 return max_ver;
579}
580
581fn glibcVerFromLinkName(link_name: []const u8, prefix: []const u8) error{ UnrecognizedGnuLibCFileName, InvalidGnuLibCVersion }!std.SemanticVersion {
582 // example: "libc-2.3.4.so"
583 // example: "libc-2.27.so"
584 // example: "ld-2.33.so"
585 const suffix = ".so";
586 if (!mem.startsWith(u8, link_name, prefix) or !mem.endsWith(u8, link_name, suffix)) {
587 return error.UnrecognizedGnuLibCFileName;
588 }
589 // chop off "libc-" and ".so"
590 const link_name_chopped = link_name[prefix.len .. link_name.len - suffix.len];
591 return Target.Query.parseVersion(link_name_chopped) catch |err| switch (err) {
592 error.Overflow => return error.InvalidGnuLibCVersion,
593 error.InvalidVersion => return error.InvalidGnuLibCVersion,
594 };
595}
596
597test glibcVerFromLinkName {
598 try std.testing.expectError(error.UnrecognizedGnuLibCFileName, glibcVerFromLinkName("ld-2.37.so", "this-prefix-does-not-exist"));
599 try std.testing.expectError(error.UnrecognizedGnuLibCFileName, glibcVerFromLinkName("libc-2.37.so-is-not-end", "libc-"));
600
601 try std.testing.expectError(error.InvalidGnuLibCVersion, glibcVerFromLinkName("ld-2.so", "ld-"));
602 try std.testing.expectEqual(std.SemanticVersion{ .major = 2, .minor = 37, .patch = 0 }, try glibcVerFromLinkName("ld-2.37.so", "ld-"));
603 try std.testing.expectEqual(std.SemanticVersion{ .major = 2, .minor = 37, .patch = 0 }, try glibcVerFromLinkName("ld-2.37.0.so", "ld-"));
604 try std.testing.expectEqual(std.SemanticVersion{ .major = 2, .minor = 37, .patch = 1 }, try glibcVerFromLinkName("ld-2.37.1.so", "ld-"));
605 try std.testing.expectError(error.InvalidGnuLibCVersion, glibcVerFromLinkName("ld-2.37.4.5.so", "ld-"));
606}
607
608pub const AbiAndDynamicLinkerFromFileError = error{
609 FileSystem,
610 SystemResources,
611 SymLinkLoop,
612 ProcessFdQuotaExceeded,
613 SystemFdQuotaExceeded,
614 UnableToReadElfFile,
615 InvalidElfClass,
616 InvalidElfVersion,
617 InvalidElfEndian,
618 InvalidElfFile,
619 InvalidElfMagic,
620 Unexpected,
621 UnexpectedEndOfFile,
622 NameTooLong,
623};
624
625pub fn abiAndDynamicLinkerFromFile(
626 file: fs.File,
627 cpu: Target.Cpu,
628 os: Target.Os,
629 ld_info_list: []const LdInfo,
630 query: Target.Query,
631) AbiAndDynamicLinkerFromFileError!NativeTargetInfo {
632 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 align(@alignOf(elf.Elf64_Ehdr)) = undefined;
633 _ = try preadMin(file, &hdr_buf, 0, hdr_buf.len);
634 const hdr32 = @as(*elf.Elf32_Ehdr, @ptrCast(&hdr_buf));
635 const hdr64 = @as(*elf.Elf64_Ehdr, @ptrCast(&hdr_buf));
636 if (!mem.eql(u8, hdr32.e_ident[0..4], elf.MAGIC)) return error.InvalidElfMagic;
637 const elf_endian: std.builtin.Endian = switch (hdr32.e_ident[elf.EI_DATA]) {
638 elf.ELFDATA2LSB => .little,
639 elf.ELFDATA2MSB => .big,
640 else => return error.InvalidElfEndian,
641 };
642 const need_bswap = elf_endian != native_endian;
643 if (hdr32.e_ident[elf.EI_VERSION] != 1) return error.InvalidElfVersion;
644
645 const is_64 = switch (hdr32.e_ident[elf.EI_CLASS]) {
646 elf.ELFCLASS32 => false,
647 elf.ELFCLASS64 => true,
648 else => return error.InvalidElfClass,
649 };
650 var phoff = elfInt(is_64, need_bswap, hdr32.e_phoff, hdr64.e_phoff);
651 const phentsize = elfInt(is_64, need_bswap, hdr32.e_phentsize, hdr64.e_phentsize);
652 const phnum = elfInt(is_64, need_bswap, hdr32.e_phnum, hdr64.e_phnum);
653
654 var result: NativeTargetInfo = .{
655 .target = .{
656 .cpu = cpu,
657 .os = os,
658 .abi = query.abi orelse Target.Abi.default(cpu.arch, os),
659 .ofmt = query.ofmt orelse Target.ObjectFormat.default(os.tag, cpu.arch),
660 },
661 .dynamic_linker = query.dynamic_linker,
662 };
663 var rpath_offset: ?u64 = null; // Found inside PT_DYNAMIC
664 const look_for_ld = query.dynamic_linker.get() == null;
665
666 var ph_buf: [16 * @sizeOf(elf.Elf64_Phdr)]u8 align(@alignOf(elf.Elf64_Phdr)) = undefined;
667 if (phentsize > @sizeOf(elf.Elf64_Phdr)) return error.InvalidElfFile;
668
669 var ph_i: u16 = 0;
670 while (ph_i < phnum) {
671 // Reserve some bytes so that we can deref the 64-bit struct fields
672 // even when the ELF file is 32-bits.
673 const ph_reserve: usize = @sizeOf(elf.Elf64_Phdr) - @sizeOf(elf.Elf32_Phdr);
674 const ph_read_byte_len = try preadMin(file, ph_buf[0 .. ph_buf.len - ph_reserve], phoff, phentsize);
675 var ph_buf_i: usize = 0;
676 while (ph_buf_i < ph_read_byte_len and ph_i < phnum) : ({
677 ph_i += 1;
678 phoff += phentsize;
679 ph_buf_i += phentsize;
680 }) {
681 const ph32: *elf.Elf32_Phdr = @ptrCast(@alignCast(&ph_buf[ph_buf_i]));
682 const ph64: *elf.Elf64_Phdr = @ptrCast(@alignCast(&ph_buf[ph_buf_i]));
683 const p_type = elfInt(is_64, need_bswap, ph32.p_type, ph64.p_type);
684 switch (p_type) {
685 elf.PT_INTERP => if (look_for_ld) {
686 const p_offset = elfInt(is_64, need_bswap, ph32.p_offset, ph64.p_offset);
687 const p_filesz = elfInt(is_64, need_bswap, ph32.p_filesz, ph64.p_filesz);
688 if (p_filesz > result.dynamic_linker.buffer.len) return error.NameTooLong;
689 const filesz = @as(usize, @intCast(p_filesz));
690 _ = try preadMin(file, result.dynamic_linker.buffer[0..filesz], p_offset, filesz);
691 // PT_INTERP includes a null byte in filesz.
692 const len = filesz - 1;
693 // dynamic_linker.max_byte is "max", not "len".
694 // We know it will fit in u8 because we check against dynamic_linker.buffer.len above.
695 result.dynamic_linker.max_byte = @as(u8, @intCast(len - 1));
696
697 // Use it to determine ABI.
698 const full_ld_path = result.dynamic_linker.buffer[0..len];
699 for (ld_info_list) |ld_info| {
700 const standard_ld_basename = fs.path.basename(ld_info.ld.get().?);
701 if (std.mem.endsWith(u8, full_ld_path, standard_ld_basename)) {
702 result.target.abi = ld_info.abi;
703 break;
704 }
705 }
706 },
707 // We only need this for detecting glibc version.
708 elf.PT_DYNAMIC => if (builtin.target.os.tag == .linux and result.target.isGnuLibC() and
709 query.glibc_version == null)
710 {
711 var dyn_off = elfInt(is_64, need_bswap, ph32.p_offset, ph64.p_offset);
712 const p_filesz = elfInt(is_64, need_bswap, ph32.p_filesz, ph64.p_filesz);
713 const dyn_size: usize = if (is_64) @sizeOf(elf.Elf64_Dyn) else @sizeOf(elf.Elf32_Dyn);
714 const dyn_num = p_filesz / dyn_size;
715 var dyn_buf: [16 * @sizeOf(elf.Elf64_Dyn)]u8 align(@alignOf(elf.Elf64_Dyn)) = undefined;
716 var dyn_i: usize = 0;
717 dyn: while (dyn_i < dyn_num) {
718 // Reserve some bytes so that we can deref the 64-bit struct fields
719 // even when the ELF file is 32-bits.
720 const dyn_reserve: usize = @sizeOf(elf.Elf64_Dyn) - @sizeOf(elf.Elf32_Dyn);
721 const dyn_read_byte_len = try preadMin(
722 file,
723 dyn_buf[0 .. dyn_buf.len - dyn_reserve],
724 dyn_off,
725 dyn_size,
726 );
727 var dyn_buf_i: usize = 0;
728 while (dyn_buf_i < dyn_read_byte_len and dyn_i < dyn_num) : ({
729 dyn_i += 1;
730 dyn_off += dyn_size;
731 dyn_buf_i += dyn_size;
732 }) {
733 const dyn32: *elf.Elf32_Dyn = @ptrCast(@alignCast(&dyn_buf[dyn_buf_i]));
734 const dyn64: *elf.Elf64_Dyn = @ptrCast(@alignCast(&dyn_buf[dyn_buf_i]));
735 const tag = elfInt(is_64, need_bswap, dyn32.d_tag, dyn64.d_tag);
736 const val = elfInt(is_64, need_bswap, dyn32.d_val, dyn64.d_val);
737 if (tag == elf.DT_RUNPATH) {
738 rpath_offset = val;
739 break :dyn;
740 }
741 }
742 }
743 },
744 else => continue,
745 }
746 }
747 }
748
749 if (builtin.target.os.tag == .linux and result.target.isGnuLibC() and
750 query.glibc_version == null)
751 {
752 const shstrndx = elfInt(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx);
753
754 var shoff = elfInt(is_64, need_bswap, hdr32.e_shoff, hdr64.e_shoff);
755 const shentsize = elfInt(is_64, need_bswap, hdr32.e_shentsize, hdr64.e_shentsize);
756 const str_section_off = shoff + @as(u64, shentsize) * @as(u64, shstrndx);
757
758 var sh_buf: [16 * @sizeOf(elf.Elf64_Shdr)]u8 align(@alignOf(elf.Elf64_Shdr)) = undefined;
759 if (sh_buf.len < shentsize) return error.InvalidElfFile;
760
761 _ = try preadMin(file, &sh_buf, str_section_off, shentsize);
762 const shstr32: *elf.Elf32_Shdr = @ptrCast(@alignCast(&sh_buf));
763 const shstr64: *elf.Elf64_Shdr = @ptrCast(@alignCast(&sh_buf));
764 const shstrtab_off = elfInt(is_64, need_bswap, shstr32.sh_offset, shstr64.sh_offset);
765 const shstrtab_size = elfInt(is_64, need_bswap, shstr32.sh_size, shstr64.sh_size);
766 var strtab_buf: [4096:0]u8 = undefined;
767 const shstrtab_len = @min(shstrtab_size, strtab_buf.len);
768 const shstrtab_read_len = try preadMin(file, &strtab_buf, shstrtab_off, shstrtab_len);
769 const shstrtab = strtab_buf[0..shstrtab_read_len];
770
771 const shnum = elfInt(is_64, need_bswap, hdr32.e_shnum, hdr64.e_shnum);
772 var sh_i: u16 = 0;
773 const dynstr: ?struct { offset: u64, size: u64 } = find_dyn_str: while (sh_i < shnum) {
774 // Reserve some bytes so that we can deref the 64-bit struct fields
775 // even when the ELF file is 32-bits.
776 const sh_reserve: usize = @sizeOf(elf.Elf64_Shdr) - @sizeOf(elf.Elf32_Shdr);
777 const sh_read_byte_len = try preadMin(
778 file,
779 sh_buf[0 .. sh_buf.len - sh_reserve],
780 shoff,
781 shentsize,
782 );
783 var sh_buf_i: usize = 0;
784 while (sh_buf_i < sh_read_byte_len and sh_i < shnum) : ({
785 sh_i += 1;
786 shoff += shentsize;
787 sh_buf_i += shentsize;
788 }) {
789 const sh32: *elf.Elf32_Shdr = @ptrCast(@alignCast(&sh_buf[sh_buf_i]));
790 const sh64: *elf.Elf64_Shdr = @ptrCast(@alignCast(&sh_buf[sh_buf_i]));
791 const sh_name_off = elfInt(is_64, need_bswap, sh32.sh_name, sh64.sh_name);
792 const sh_name = mem.sliceTo(shstrtab[sh_name_off..], 0);
793 if (mem.eql(u8, sh_name, ".dynstr")) {
794 break :find_dyn_str .{
795 .offset = elfInt(is_64, need_bswap, sh32.sh_offset, sh64.sh_offset),
796 .size = elfInt(is_64, need_bswap, sh32.sh_size, sh64.sh_size),
797 };
798 }
799 }
800 } else null;
801
802 if (dynstr) |ds| {
803 if (rpath_offset) |rpoff| {
804 if (rpoff > ds.size) return error.InvalidElfFile;
805 const rpoff_file = ds.offset + rpoff;
806 const rp_max_size = ds.size - rpoff;
807
808 const strtab_len = @min(rp_max_size, strtab_buf.len);
809 const strtab_read_len = try preadMin(file, &strtab_buf, rpoff_file, strtab_len);
810 const strtab = strtab_buf[0..strtab_read_len];
811
812 const rpath_list = mem.sliceTo(strtab, 0);
813 var it = mem.tokenizeScalar(u8, rpath_list, ':');
814 while (it.next()) |rpath| {
815 if (glibcVerFromRPath(rpath)) |ver| {
816 result.target.os.version_range.linux.glibc = ver;
817 return result;
818 } else |err| switch (err) {
819 error.GLibCNotFound => continue,
820 else => |e| return e,
821 }
822 }
823 }
824 }
825
826 if (result.dynamic_linker.get()) |dl_path| glibc_ver: {
827 // There is no DT_RUNPATH so we try to find libc.so.6 inside the same
828 // directory as the dynamic linker.
829 if (fs.path.dirname(dl_path)) |rpath| {
830 if (glibcVerFromRPath(rpath)) |ver| {
831 result.target.os.version_range.linux.glibc = ver;
832 return result;
833 } else |err| switch (err) {
834 error.GLibCNotFound => {},
835 else => |e| return e,
836 }
837 }
838
839 // So far, no luck. Next we try to see if the information is
840 // present in the symlink data for the dynamic linker path.
841 var link_buf: [std.os.PATH_MAX]u8 = undefined;
842 const link_name = std.os.readlink(dl_path, &link_buf) catch |err| switch (err) {
843 error.NameTooLong => unreachable,
844 error.InvalidUtf8 => unreachable, // Windows only
845 error.BadPathName => unreachable, // Windows only
846 error.UnsupportedReparsePointType => unreachable, // Windows only
847 error.NetworkNotFound => unreachable, // Windows only
848
849 error.AccessDenied,
850 error.FileNotFound,
851 error.NotLink,
852 error.NotDir,
853 => break :glibc_ver,
854
855 error.SystemResources,
856 error.FileSystem,
857 error.SymLinkLoop,
858 error.Unexpected,
859 => |e| return e,
860 };
861 result.target.os.version_range.linux.glibc = glibcVerFromLinkName(
862 fs.path.basename(link_name),
863 "ld-",
864 ) catch |err| switch (err) {
865 error.UnrecognizedGnuLibCFileName,
866 error.InvalidGnuLibCVersion,
867 => break :glibc_ver,
868 };
869 return result;
870 }
871
872 // Nothing worked so far. Finally we fall back to hard-coded search paths.
873 // Some distros such as Debian keep their libc.so.6 in `/lib/$triple/`.
874 var path_buf: [std.os.PATH_MAX]u8 = undefined;
875 var index: usize = 0;
876 const prefix = "/lib/";
877 const cpu_arch = @tagName(result.target.cpu.arch);
878 const os_tag = @tagName(result.target.os.tag);
879 const abi = @tagName(result.target.abi);
880 @memcpy(path_buf[index..][0..prefix.len], prefix);
881 index += prefix.len;
882 @memcpy(path_buf[index..][0..cpu_arch.len], cpu_arch);
883 index += cpu_arch.len;
884 path_buf[index] = '-';
885 index += 1;
886 @memcpy(path_buf[index..][0..os_tag.len], os_tag);
887 index += os_tag.len;
888 path_buf[index] = '-';
889 index += 1;
890 @memcpy(path_buf[index..][0..abi.len], abi);
891 index += abi.len;
892 const rpath = path_buf[0..index];
893 if (glibcVerFromRPath(rpath)) |ver| {
894 result.target.os.version_range.linux.glibc = ver;
895 return result;
896 } else |err| switch (err) {
897 error.GLibCNotFound => {},
898 else => |e| return e,
899 }
900 }
901
902 return result;
903}
904
905fn preadMin(file: fs.File, buf: []u8, offset: u64, min_read_len: usize) !usize {
906 var i: usize = 0;
907 while (i < min_read_len) {
908 const len = file.pread(buf[i..], offset + i) catch |err| switch (err) {
909 error.OperationAborted => unreachable, // Windows-only
910 error.WouldBlock => unreachable, // Did not request blocking mode
911 error.NotOpenForReading => unreachable,
912 error.SystemResources => return error.SystemResources,
913 error.IsDir => return error.UnableToReadElfFile,
914 error.BrokenPipe => return error.UnableToReadElfFile,
915 error.Unseekable => return error.UnableToReadElfFile,
916 error.ConnectionResetByPeer => return error.UnableToReadElfFile,
917 error.ConnectionTimedOut => return error.UnableToReadElfFile,
918 error.SocketNotConnected => return error.UnableToReadElfFile,
919 error.NetNameDeleted => return error.UnableToReadElfFile,
920 error.Unexpected => return error.Unexpected,
921 error.InputOutput => return error.FileSystem,
922 error.AccessDenied => return error.Unexpected,
923 };
924 if (len == 0) return error.UnexpectedEndOfFile;
925 i += len;
926 }
927 return i;
928}
929
930fn defaultAbiAndDynamicLinker(cpu: Target.Cpu, os: Target.Os, query: Target.Query) !NativeTargetInfo {
931 const target: Target = .{
932 .cpu = cpu,
933 .os = os,
934 .abi = query.abi orelse Target.Abi.default(cpu.arch, os),
935 .ofmt = query.ofmt orelse Target.ObjectFormat.default(os.tag, cpu.arch),
936 };
937 return NativeTargetInfo{
938 .target = target,
939 .dynamic_linker = if (query.dynamic_linker.get() == null)
940 target.standardDynamicLinkerPath()
941 else
942 query.dynamic_linker,
943 };
944}
945
946pub const LdInfo = struct {
947 ld: DynamicLinker,
948 abi: Target.Abi,
949};
950
951pub fn elfInt(is_64: bool, need_bswap: bool, int_32: anytype, int_64: anytype) @TypeOf(int_64) {
952 if (is_64) {
953 if (need_bswap) {
954 return @byteSwap(int_64);
955 } else {
956 return int_64;
957 }
958 } else {
959 if (need_bswap) {
960 return @byteSwap(int_32);
961 } else {
962 return int_32;
963 }
964 }
965}
966
967fn detectNativeCpuAndFeatures(cpu_arch: Target.Cpu.Arch, os: Target.Os, query: Target.Query) ?Target.Cpu {
968 // Here we switch on a comptime value rather than `cpu_arch`. This is valid because `cpu_arch`,
969 // although it is a runtime value, is guaranteed to be one of the architectures in the set
970 // of the respective switch prong.
971 switch (builtin.cpu.arch) {
972 .x86_64, .x86 => {
973 return @import("x86.zig").detectNativeCpuAndFeatures(cpu_arch, os, query);
974 },
975 else => {},
976 }
977
978 switch (builtin.os.tag) {
979 .linux => return linux.detectNativeCpuAndFeatures(),
980 .macos => return darwin.macos.detectNativeCpuAndFeatures(),
981 .windows => return windows.detectNativeCpuAndFeatures(),
982 else => {},
983 }
984
985 // This architecture does not have CPU model & feature detection yet.
986 // See https://github.com/ziglang/zig/issues/4591
987 return null;
988}
989
990pub const Executor = union(enum) {
991 native,
992 rosetta,
993 qemu: []const u8,
994 wine: []const u8,
995 wasmtime: []const u8,
996 darling: []const u8,
997 bad_dl: []const u8,
998 bad_os_or_cpu,
999};
1000
1001pub const GetExternalExecutorOptions = struct {
1002 allow_darling: bool = true,
1003 allow_qemu: bool = true,
1004 allow_rosetta: bool = true,
1005 allow_wasmtime: bool = true,
1006 allow_wine: bool = true,
1007 qemu_fixes_dl: bool = false,
1008 link_libc: bool = false,
1009};
1010
1011/// Return whether or not the given host is capable of running executables of
1012/// the other target.
1013pub fn getExternalExecutor(
1014 host: NativeTargetInfo,
1015 candidate: *const NativeTargetInfo,
1016 options: GetExternalExecutorOptions,
1017) Executor {
1018 const os_match = host.target.os.tag == candidate.target.os.tag;
1019 const cpu_ok = cpu_ok: {
1020 if (host.target.cpu.arch == candidate.target.cpu.arch)
1021 break :cpu_ok true;
1022
1023 if (host.target.cpu.arch == .x86_64 and candidate.target.cpu.arch == .x86)
1024 break :cpu_ok true;
1025
1026 if (host.target.cpu.arch == .aarch64 and candidate.target.cpu.arch == .arm)
1027 break :cpu_ok true;
1028
1029 if (host.target.cpu.arch == .aarch64_be and candidate.target.cpu.arch == .armeb)
1030 break :cpu_ok true;
1031
1032 // TODO additionally detect incompatible CPU features.
1033 // Note that in some cases the OS kernel will emulate missing CPU features
1034 // when an illegal instruction is encountered.
1035
1036 break :cpu_ok false;
1037 };
1038
1039 var bad_result: Executor = .bad_os_or_cpu;
1040
1041 if (os_match and cpu_ok) native: {
1042 if (options.link_libc) {
1043 if (candidate.dynamic_linker.get()) |candidate_dl| {
1044 fs.cwd().access(candidate_dl, .{}) catch {
1045 bad_result = .{ .bad_dl = candidate_dl };
1046 break :native;
1047 };
1048 }
1049 }
1050 return .native;
1051 }
1052
1053 // If the OS match and OS is macOS and CPU is arm64, we can use Rosetta 2
1054 // to emulate the foreign architecture.
1055 if (options.allow_rosetta and os_match and
1056 host.target.os.tag == .macos and host.target.cpu.arch == .aarch64)
1057 {
1058 switch (candidate.target.cpu.arch) {
1059 .x86_64 => return .rosetta,
1060 else => return bad_result,
1061 }
1062 }
1063
1064 // If the OS matches, we can use QEMU to emulate a foreign architecture.
1065 if (options.allow_qemu and os_match and (!cpu_ok or options.qemu_fixes_dl)) {
1066 return switch (candidate.target.cpu.arch) {
1067 .aarch64 => Executor{ .qemu = "qemu-aarch64" },
1068 .aarch64_be => Executor{ .qemu = "qemu-aarch64_be" },
1069 .arm => Executor{ .qemu = "qemu-arm" },
1070 .armeb => Executor{ .qemu = "qemu-armeb" },
1071 .hexagon => Executor{ .qemu = "qemu-hexagon" },
1072 .x86 => Executor{ .qemu = "qemu-i386" },
1073 .m68k => Executor{ .qemu = "qemu-m68k" },
1074 .mips => Executor{ .qemu = "qemu-mips" },
1075 .mipsel => Executor{ .qemu = "qemu-mipsel" },
1076 .mips64 => Executor{ .qemu = "qemu-mips64" },
1077 .mips64el => Executor{ .qemu = "qemu-mips64el" },
1078 .powerpc => Executor{ .qemu = "qemu-ppc" },
1079 .powerpc64 => Executor{ .qemu = "qemu-ppc64" },
1080 .powerpc64le => Executor{ .qemu = "qemu-ppc64le" },
1081 .riscv32 => Executor{ .qemu = "qemu-riscv32" },
1082 .riscv64 => Executor{ .qemu = "qemu-riscv64" },
1083 .s390x => Executor{ .qemu = "qemu-s390x" },
1084 .sparc => Executor{ .qemu = "qemu-sparc" },
1085 .sparc64 => Executor{ .qemu = "qemu-sparc64" },
1086 .x86_64 => Executor{ .qemu = "qemu-x86_64" },
1087 else => return bad_result,
1088 };
1089 }
1090
1091 switch (candidate.target.os.tag) {
1092 .windows => {
1093 if (options.allow_wine) {
1094 // x86_64 wine does not support emulating aarch64-windows and
1095 // vice versa.
1096 if (candidate.target.cpu.arch != builtin.cpu.arch) {
1097 return bad_result;
1098 }
1099 switch (candidate.target.ptrBitWidth()) {
1100 32 => return Executor{ .wine = "wine" },
1101 64 => return Executor{ .wine = "wine64" },
1102 else => return bad_result,
1103 }
1104 }
1105 return bad_result;
1106 },
1107 .wasi => {
1108 if (options.allow_wasmtime) {
1109 switch (candidate.target.ptrBitWidth()) {
1110 32 => return Executor{ .wasmtime = "wasmtime" },
1111 else => return bad_result,
1112 }
1113 }
1114 return bad_result;
1115 },
1116 .macos => {
1117 if (options.allow_darling) {
1118 // This check can be loosened once darling adds a QEMU-based emulation
1119 // layer for non-host architectures:
1120 // https://github.com/darlinghq/darling/issues/863
1121 if (candidate.target.cpu.arch != builtin.cpu.arch) {
1122 return bad_result;
1123 }
1124 return Executor{ .darling = "darling" };
1125 }
1126 return bad_result;
1127 },
1128 else => return bad_result,
1129 }
1130}
src/Compilation.zig+21-6
...@@ -6527,7 +6527,6 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca...@@ -6527,7 +6527,6 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca
6527 try buffer.writer().print(" .{},\n", .{std.zig.fmtId(feature.name)});6527 try buffer.writer().print(" .{},\n", .{std.zig.fmtId(feature.name)});
6528 }6528 }
6529 }6529 }
6530
6531 try buffer.writer().print(6530 try buffer.writer().print(
6532 \\ }}),6531 \\ }}),
6533 \\}};6532 \\}};
...@@ -6607,15 +6606,31 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca...@@ -6607,15 +6606,31 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca
6607 .{ windows.min, windows.max },6606 .{ windows.min, windows.max },
6608 ),6607 ),
6609 }6608 }
6610 try buffer.appendSlice("};\n");6609 try buffer.appendSlice(
66116610 \\};
6612 try buffer.writer().print(6611 \\pub const target: std.Target = .{
6613 \\pub const target = std.Target{{
6614 \\ .cpu = cpu,6612 \\ .cpu = cpu,
6615 \\ .os = os,6613 \\ .os = os,
6616 \\ .abi = abi,6614 \\ .abi = abi,
6617 \\ .ofmt = object_format,6615 \\ .ofmt = object_format,
6618 \\}};6616 \\
6617 );
6618
6619 if (target.dynamic_linker.get()) |dl| {
6620 try buffer.writer().print(
6621 \\ .dynamic_linker = std.Target.DynamicLinker.init("{s}"),
6622 \\}};
6623 \\
6624 , .{dl});
6625 } else {
6626 try buffer.appendSlice(
6627 \\ .dynamic_linker = std.Target.DynamicLinker.none,
6628 \\};
6629 \\
6630 );
6631 }
6632
6633 try buffer.writer().print(
6619 \\pub const object_format = std.Target.ObjectFormat.{};6634 \\pub const object_format = std.Target.ObjectFormat.{};
6620 \\pub const mode = std.builtin.OptimizeMode.{};6635 \\pub const mode = std.builtin.OptimizeMode.{};
6621 \\pub const link_libc = {};6636 \\pub const link_libc = {};
src/main.zig+62-70
...@@ -321,13 +321,14 @@ pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi...@@ -321,13 +321,14 @@ pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
321 } else if (mem.eql(u8, cmd, "init")) {321 } else if (mem.eql(u8, cmd, "init")) {
322 return cmdInit(gpa, arena, cmd_args);322 return cmdInit(gpa, arena, cmd_args);
323 } else if (mem.eql(u8, cmd, "targets")) {323 } else if (mem.eql(u8, cmd, "targets")) {
324 const info = try detectNativeTargetInfo(.{});324 const host = try std.zig.system.resolveTargetQuery(.{});
325 const stdout = io.getStdOut().writer();325 const stdout = io.getStdOut().writer();
326 return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, info.target);326 return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, host);
327 } else if (mem.eql(u8, cmd, "version")) {327 } else if (mem.eql(u8, cmd, "version")) {
328 try std.io.getStdOut().writeAll(build_options.version ++ "\n");328 try std.io.getStdOut().writeAll(build_options.version ++ "\n");
329 // Check libc++ linkage to make sure Zig was built correctly, but only for "env" and "version"329 // Check libc++ linkage to make sure Zig was built correctly, but only
330 // to avoid affecting the startup time for build-critical commands (check takes about ~10 μs)330 // for "env" and "version" to avoid affecting the startup time for
331 // build-critical commands (check takes about ~10 μs)
331 return verifyLibcxxCorrectlyLinked();332 return verifyLibcxxCorrectlyLinked();
332 } else if (mem.eql(u8, cmd, "env")) {333 } else if (mem.eql(u8, cmd, "env")) {
333 verifyLibcxxCorrectlyLinked();334 verifyLibcxxCorrectlyLinked();
...@@ -2608,9 +2609,9 @@ fn buildOutputType(...@@ -2608,9 +2609,9 @@ fn buildOutputType(
2608 }2609 }
26092610
2610 const target_query = try parseTargetQueryOrReportFatalError(arena, target_parse_options);2611 const target_query = try parseTargetQueryOrReportFatalError(arena, target_parse_options);
2611 const target_info = try detectNativeTargetInfo(target_query);2612 const target = try std.zig.system.resolveTargetQuery(target_query);
26122613
2613 if (target_info.target.os.tag != .freestanding) {2614 if (target.os.tag != .freestanding) {
2614 if (ensure_libc_on_non_freestanding)2615 if (ensure_libc_on_non_freestanding)
2615 link_libc = true;2616 link_libc = true;
2616 if (ensure_libcpp_on_non_freestanding)2617 if (ensure_libcpp_on_non_freestanding)
...@@ -2621,7 +2622,7 @@ fn buildOutputType(...@@ -2621,7 +2622,7 @@ fn buildOutputType(
2621 if (!force) {2622 if (!force) {
2622 entry = null;2623 entry = null;
2623 } else if (entry == null and output_mode == .Exe) {2624 } else if (entry == null and output_mode == .Exe) {
2624 entry = switch (target_info.target.ofmt) {2625 entry = switch (target.ofmt) {
2625 .coff => "wWinMainCRTStartup",2626 .coff => "wWinMainCRTStartup",
2626 .macho => "_main",2627 .macho => "_main",
2627 .elf, .plan9 => "_start",2628 .elf, .plan9 => "_start",
...@@ -2629,12 +2630,12 @@ fn buildOutputType(...@@ -2629,12 +2630,12 @@ fn buildOutputType(
2629 else => |tag| fatal("No default entry point available for output format {s}", .{@tagName(tag)}),2630 else => |tag| fatal("No default entry point available for output format {s}", .{@tagName(tag)}),
2630 };2631 };
2631 }2632 }
2632 } else if (entry == null and target_info.target.isWasm() and output_mode == .Exe) {2633 } else if (entry == null and target.isWasm() and output_mode == .Exe) {
2633 // For WebAssembly the compiler defaults to setting the entry name when no flags are set.2634 // For WebAssembly the compiler defaults to setting the entry name when no flags are set.
2634 entry = defaultWasmEntryName(wasi_exec_model);2635 entry = defaultWasmEntryName(wasi_exec_model);
2635 }2636 }
26362637
2637 if (target_info.target.ofmt == .coff) {2638 if (target.ofmt == .coff) {
2638 // Now that we know the target supports resources,2639 // Now that we know the target supports resources,
2639 // we can add the res files as link objects.2640 // we can add the res files as link objects.
2640 for (res_files.items) |res_file| {2641 for (res_files.items) |res_file| {
...@@ -2652,7 +2653,7 @@ fn buildOutputType(...@@ -2652,7 +2653,7 @@ fn buildOutputType(
2652 }2653 }
2653 }2654 }
26542655
2655 if (target_info.target.cpu.arch.isWasm()) blk: {2656 if (target.cpu.arch.isWasm()) blk: {
2656 if (single_threaded == null) {2657 if (single_threaded == null) {
2657 single_threaded = true;2658 single_threaded = true;
2658 }2659 }
...@@ -2678,8 +2679,8 @@ fn buildOutputType(...@@ -2678,8 +2679,8 @@ fn buildOutputType(
2678 fatal("shared memory is not allowed in object files", .{});2679 fatal("shared memory is not allowed in object files", .{});
2679 }2680 }
26802681
2681 if (!target_info.target.cpu.features.isEnabled(@intFromEnum(std.Target.wasm.Feature.atomics)) or2682 if (!target.cpu.features.isEnabled(@intFromEnum(std.Target.wasm.Feature.atomics)) or
2682 !target_info.target.cpu.features.isEnabled(@intFromEnum(std.Target.wasm.Feature.bulk_memory)))2683 !target.cpu.features.isEnabled(@intFromEnum(std.Target.wasm.Feature.bulk_memory)))
2683 {2684 {
2684 fatal("'atomics' and 'bulk-memory' features must be enabled to use shared memory", .{});2685 fatal("'atomics' and 'bulk-memory' features must be enabled to use shared memory", .{});
2685 }2686 }
...@@ -2777,15 +2778,15 @@ fn buildOutputType(...@@ -2777,15 +2778,15 @@ fn buildOutputType(
2777 }2778 }
27782779
2779 for (system_libs.keys(), system_libs.values()) |lib_name, info| {2780 for (system_libs.keys(), system_libs.values()) |lib_name, info| {
2780 if (target_info.target.is_libc_lib_name(lib_name)) {2781 if (target.is_libc_lib_name(lib_name)) {
2781 link_libc = true;2782 link_libc = true;
2782 continue;2783 continue;
2783 }2784 }
2784 if (target_info.target.is_libcpp_lib_name(lib_name)) {2785 if (target.is_libcpp_lib_name(lib_name)) {
2785 link_libcpp = true;2786 link_libcpp = true;
2786 continue;2787 continue;
2787 }2788 }
2788 switch (target_util.classifyCompilerRtLibName(target_info.target, lib_name)) {2789 switch (target_util.classifyCompilerRtLibName(target, lib_name)) {
2789 .none => {},2790 .none => {},
2790 .only_libunwind, .both => {2791 .only_libunwind, .both => {
2791 link_libunwind = true;2792 link_libunwind = true;
...@@ -2797,8 +2798,8 @@ fn buildOutputType(...@@ -2797,8 +2798,8 @@ fn buildOutputType(
2797 },2798 },
2798 }2799 }
27992800
2800 if (target_info.target.isMinGW()) {2801 if (target.isMinGW()) {
2801 const exists = mingw.libExists(arena, target_info.target, zig_lib_directory, lib_name) catch |err| {2802 const exists = mingw.libExists(arena, target, zig_lib_directory, lib_name) catch |err| {
2802 fatal("failed to check zig installation for DLL import libs: {s}", .{2803 fatal("failed to check zig installation for DLL import libs: {s}", .{
2803 @errorName(err),2804 @errorName(err),
2804 });2805 });
...@@ -2820,7 +2821,7 @@ fn buildOutputType(...@@ -2820,7 +2821,7 @@ fn buildOutputType(
2820 fatal("cannot use absolute path as a system library: {s}", .{lib_name});2821 fatal("cannot use absolute path as a system library: {s}", .{lib_name});
2821 }2822 }
28222823
2823 if (target_info.target.os.tag == .wasi) {2824 if (target.os.tag == .wasi) {
2824 if (wasi_libc.getEmulatedLibCRTFile(lib_name)) |crt_file| {2825 if (wasi_libc.getEmulatedLibCRTFile(lib_name)) |crt_file| {
2825 try wasi_emulated_libs.append(crt_file);2826 try wasi_emulated_libs.append(crt_file);
2826 continue;2827 continue;
...@@ -2838,7 +2839,7 @@ fn buildOutputType(...@@ -2838,7 +2839,7 @@ fn buildOutputType(
2838 if (sysroot == null and target_query.isNativeOs() and target_query.isNativeAbi() and2839 if (sysroot == null and target_query.isNativeOs() and target_query.isNativeAbi() and
2839 (external_system_libs.len != 0 or want_native_include_dirs))2840 (external_system_libs.len != 0 or want_native_include_dirs))
2840 {2841 {
2841 const paths = std.zig.system.NativePaths.detect(arena, target_info) catch |err| {2842 const paths = std.zig.system.NativePaths.detect(arena, target) catch |err| {
2842 fatal("unable to detect native system paths: {s}", .{@errorName(err)});2843 fatal("unable to detect native system paths: {s}", .{@errorName(err)});
2843 };2844 };
2844 for (paths.warnings.items) |warning| {2845 for (paths.warnings.items) |warning| {
...@@ -2857,7 +2858,7 @@ fn buildOutputType(...@@ -2857,7 +2858,7 @@ fn buildOutputType(
2857 }2858 }
28582859
2859 if (builtin.target.os.tag == .windows and2860 if (builtin.target.os.tag == .windows and
2860 target_info.target.abi == .msvc and2861 target.abi == .msvc and
2861 external_system_libs.len != 0)2862 external_system_libs.len != 0)
2862 {2863 {
2863 if (libc_installation == null) {2864 if (libc_installation == null) {
...@@ -2902,7 +2903,7 @@ fn buildOutputType(...@@ -2902,7 +2903,7 @@ fn buildOutputType(
2902 &checked_paths,2903 &checked_paths,
2903 lib_dir_path,2904 lib_dir_path,
2904 lib_name,2905 lib_name,
2905 target_info.target,2906 target,
2906 info.preferred_mode,2907 info.preferred_mode,
2907 )) {2908 )) {
2908 const path = try arena.dupe(u8, test_path.items);2909 const path = try arena.dupe(u8, test_path.items);
...@@ -2936,7 +2937,7 @@ fn buildOutputType(...@@ -2936,7 +2937,7 @@ fn buildOutputType(
2936 &checked_paths,2937 &checked_paths,
2937 lib_dir_path,2938 lib_dir_path,
2938 lib_name,2939 lib_name,
2939 target_info.target,2940 target,
2940 info.fallbackMode(),2941 info.fallbackMode(),
2941 )) {2942 )) {
2942 const path = try arena.dupe(u8, test_path.items);2943 const path = try arena.dupe(u8, test_path.items);
...@@ -2970,7 +2971,7 @@ fn buildOutputType(...@@ -2970,7 +2971,7 @@ fn buildOutputType(
2970 &checked_paths,2971 &checked_paths,
2971 lib_dir_path,2972 lib_dir_path,
2972 lib_name,2973 lib_name,
2973 target_info.target,2974 target,
2974 info.preferred_mode,2975 info.preferred_mode,
2975 )) {2976 )) {
2976 const path = try arena.dupe(u8, test_path.items);2977 const path = try arena.dupe(u8, test_path.items);
...@@ -2994,7 +2995,7 @@ fn buildOutputType(...@@ -2994,7 +2995,7 @@ fn buildOutputType(
2994 &checked_paths,2995 &checked_paths,
2995 lib_dir_path,2996 lib_dir_path,
2996 lib_name,2997 lib_name,
2997 target_info.target,2998 target,
2998 info.fallbackMode(),2999 info.fallbackMode(),
2999 )) {3000 )) {
3000 const path = try arena.dupe(u8, test_path.items);3001 const path = try arena.dupe(u8, test_path.items);
...@@ -3089,15 +3090,13 @@ fn buildOutputType(...@@ -3089,15 +3090,13 @@ fn buildOutputType(
3089 }3090 }
3090 // After this point, resolved_frameworks is used instead of frameworks.3091 // After this point, resolved_frameworks is used instead of frameworks.
30913092
3092 const object_format = target_info.target.ofmt;3093 if (output_mode == .Obj and (target.ofmt == .coff or target.ofmt == .macho)) {
3093
3094 if (output_mode == .Obj and (object_format == .coff or object_format == .macho)) {
3095 const total_obj_count = c_source_files.items.len +3094 const total_obj_count = c_source_files.items.len +
3096 @intFromBool(root_src_file != null) +3095 @intFromBool(root_src_file != null) +
3097 rc_source_files.items.len +3096 rc_source_files.items.len +
3098 link_objects.items.len;3097 link_objects.items.len;
3099 if (total_obj_count > 1) {3098 if (total_obj_count > 1) {
3100 fatal("{s} does not support linking multiple objects into one", .{@tagName(object_format)});3099 fatal("{s} does not support linking multiple objects into one", .{@tagName(target.ofmt)});
3101 }3100 }
3102 }3101 }
31033102
...@@ -3110,7 +3109,7 @@ fn buildOutputType(...@@ -3110,7 +3109,7 @@ fn buildOutputType(
3110 const resolved_soname: ?[]const u8 = switch (soname) {3109 const resolved_soname: ?[]const u8 = switch (soname) {
3111 .yes => |explicit| explicit,3110 .yes => |explicit| explicit,
3112 .no => null,3111 .no => null,
3113 .yes_default_value => switch (object_format) {3112 .yes_default_value => switch (target.ofmt) {
3114 .elf => if (have_version)3113 .elf => if (have_version)
3115 try std.fmt.allocPrint(arena, "lib{s}.so.{d}", .{ root_name, version.major })3114 try std.fmt.allocPrint(arena, "lib{s}.so.{d}", .{ root_name, version.major })
3116 else3115 else
...@@ -3119,7 +3118,7 @@ fn buildOutputType(...@@ -3119,7 +3118,7 @@ fn buildOutputType(
3119 },3118 },
3120 };3119 };
31213120
3122 const a_out_basename = switch (object_format) {3121 const a_out_basename = switch (target.ofmt) {
3123 .coff => "a.exe",3122 .coff => "a.exe",
3124 else => "a.out",3123 else => "a.out",
3125 };3124 };
...@@ -3141,7 +3140,7 @@ fn buildOutputType(...@@ -3141,7 +3140,7 @@ fn buildOutputType(
3141 },3140 },
3142 .basename = try std.zig.binNameAlloc(arena, .{3141 .basename = try std.zig.binNameAlloc(arena, .{
3143 .root_name = root_name,3142 .root_name = root_name,
3144 .target = target_info.target,3143 .target = target,
3145 .output_mode = output_mode,3144 .output_mode = output_mode,
3146 .link_mode = link_mode,3145 .link_mode = link_mode,
3147 .version = optional_version,3146 .version = optional_version,
...@@ -3269,7 +3268,7 @@ fn buildOutputType(...@@ -3269,7 +3268,7 @@ fn buildOutputType(
3269 // Note that cmake when targeting Windows will try to execute3268 // Note that cmake when targeting Windows will try to execute
3270 // zig cc to make an executable and output an implib too.3269 // zig cc to make an executable and output an implib too.
3271 const implib_eligible = is_exe_or_dyn_lib and3270 const implib_eligible = is_exe_or_dyn_lib and
3272 emit_bin_loc != null and target_info.target.os.tag == .windows;3271 emit_bin_loc != null and target.os.tag == .windows;
3273 if (!implib_eligible) {3272 if (!implib_eligible) {
3274 if (!emit_implib_arg_provided) {3273 if (!emit_implib_arg_provided) {
3275 emit_implib = .no;3274 emit_implib = .no;
...@@ -3419,7 +3418,7 @@ fn buildOutputType(...@@ -3419,7 +3418,7 @@ fn buildOutputType(
3419 // "-" is stdin. Dump it to a real file.3418 // "-" is stdin. Dump it to a real file.
3420 const sep = fs.path.sep_str;3419 const sep = fs.path.sep_str;
3421 const sub_path = try std.fmt.allocPrint(arena, "tmp" ++ sep ++ "{x}-stdin{s}", .{3420 const sub_path = try std.fmt.allocPrint(arena, "tmp" ++ sep ++ "{x}-stdin{s}", .{
3422 std.crypto.random.int(u64), ext.canonicalName(target_info.target),3421 std.crypto.random.int(u64), ext.canonicalName(target),
3423 });3422 });
3424 try local_cache_directory.handle.makePath("tmp");3423 try local_cache_directory.handle.makePath("tmp");
3425 // Note that in one of the happy paths, execve() is used to switch3424 // Note that in one of the happy paths, execve() is used to switch
...@@ -3454,10 +3453,10 @@ fn buildOutputType(...@@ -3454,10 +3453,10 @@ fn buildOutputType(
3454 .local_cache_directory = local_cache_directory,3453 .local_cache_directory = local_cache_directory,
3455 .global_cache_directory = global_cache_directory,3454 .global_cache_directory = global_cache_directory,
3456 .root_name = root_name,3455 .root_name = root_name,
3457 .target = target_info.target,3456 .target = target,
3458 .is_native_os = target_query.isNativeOs(),3457 .is_native_os = target_query.isNativeOs(),
3459 .is_native_abi = target_query.isNativeAbi(),3458 .is_native_abi = target_query.isNativeAbi(),
3460 .dynamic_linker = target_info.dynamic_linker.get(),3459 .dynamic_linker = target.dynamic_linker.get(),
3461 .sysroot = sysroot,3460 .sysroot = sysroot,
3462 .output_mode = output_mode,3461 .output_mode = output_mode,
3463 .main_mod = main_mod,3462 .main_mod = main_mod,
...@@ -3603,7 +3602,6 @@ fn buildOutputType(...@@ -3603,7 +3602,6 @@ fn buildOutputType(
3603 .want_structured_cfg = want_structured_cfg,3602 .want_structured_cfg = want_structured_cfg,
3604 }) catch |err| switch (err) {3603 }) catch |err| switch (err) {
3605 error.LibCUnavailable => {3604 error.LibCUnavailable => {
3606 const target = target_info.target;
3607 const triple_name = try target.zigTriple(arena);3605 const triple_name = try target.zigTriple(arena);
3608 std.log.err("unable to find or provide libc for target '{s}'", .{triple_name});3606 std.log.err("unable to find or provide libc for target '{s}'", .{triple_name});
36093607
...@@ -3692,7 +3690,7 @@ fn buildOutputType(...@@ -3692,7 +3690,7 @@ fn buildOutputType(
3692 try comp.makeBinFileExecutable();3690 try comp.makeBinFileExecutable();
3693 saveState(comp, debug_incremental);3691 saveState(comp, debug_incremental);
36943692
3695 if (test_exec_args.items.len == 0 and object_format == .c) default_exec_args: {3693 if (test_exec_args.items.len == 0 and target.ofmt == .c) default_exec_args: {
3696 // Default to using `zig run` to execute the produced .c code from `zig test`.3694 // Default to using `zig run` to execute the produced .c code from `zig test`.
3697 const c_code_loc = emit_bin_loc orelse break :default_exec_args;3695 const c_code_loc = emit_bin_loc orelse break :default_exec_args;
3698 const c_code_directory = c_code_loc.directory orelse comp.bin_file.options.emit.?.directory;3696 const c_code_directory = c_code_loc.directory orelse comp.bin_file.options.emit.?.directory;
...@@ -3707,7 +3705,7 @@ fn buildOutputType(...@@ -3707,7 +3705,7 @@ fn buildOutputType(
37073705
3708 if (link_libc) {3706 if (link_libc) {
3709 try test_exec_args.append("-lc");3707 try test_exec_args.append("-lc");
3710 } else if (target_info.target.os.tag == .windows) {3708 } else if (target.os.tag == .windows) {
3711 try test_exec_args.appendSlice(&.{3709 try test_exec_args.appendSlice(&.{
3712 "--subsystem", "console",3710 "--subsystem", "console",
3713 "-lkernel32", "-lntdll",3711 "-lkernel32", "-lntdll",
...@@ -3741,7 +3739,7 @@ fn buildOutputType(...@@ -3741,7 +3739,7 @@ fn buildOutputType(
3741 test_exec_args.items,3739 test_exec_args.items,
3742 self_exe_path.?,3740 self_exe_path.?,
3743 arg_mode,3741 arg_mode,
3744 &target_info,3742 &target,
3745 &comp_destroyed,3743 &comp_destroyed,
3746 all_args,3744 all_args,
3747 runtime_args_start,3745 runtime_args_start,
...@@ -3861,7 +3859,7 @@ fn serve(...@@ -3861,7 +3859,7 @@ fn serve(
3861 // test_exec_args,3859 // test_exec_args,
3862 // self_exe_path.?,3860 // self_exe_path.?,
3863 // arg_mode,3861 // arg_mode,
3864 // target_info,3862 // target,
3865 // true,3863 // true,
3866 // &comp_destroyed,3864 // &comp_destroyed,
3867 // all_args,3865 // all_args,
...@@ -4071,7 +4069,7 @@ fn runOrTest(...@@ -4071,7 +4069,7 @@ fn runOrTest(
4071 test_exec_args: []const ?[]const u8,4069 test_exec_args: []const ?[]const u8,
4072 self_exe_path: []const u8,4070 self_exe_path: []const u8,
4073 arg_mode: ArgMode,4071 arg_mode: ArgMode,
4074 target_info: *const std.zig.system.NativeTargetInfo,4072 target: *const std.Target,
4075 comp_destroyed: *bool,4073 comp_destroyed: *bool,
4076 all_args: []const []const u8,4074 all_args: []const []const u8,
4077 runtime_args_start: ?usize,4075 runtime_args_start: ?usize,
...@@ -4105,7 +4103,7 @@ fn runOrTest(...@@ -4105,7 +4103,7 @@ fn runOrTest(
4105 if (process.can_execv and arg_mode == .run) {4103 if (process.can_execv and arg_mode == .run) {
4106 // execv releases the locks; no need to destroy the Compilation here.4104 // execv releases the locks; no need to destroy the Compilation here.
4107 const err = process.execve(gpa, argv.items, &env_map);4105 const err = process.execve(gpa, argv.items, &env_map);
4108 try warnAboutForeignBinaries(arena, arg_mode, target_info, link_libc);4106 try warnAboutForeignBinaries(arena, arg_mode, target, link_libc);
4109 const cmd = try std.mem.join(arena, " ", argv.items);4107 const cmd = try std.mem.join(arena, " ", argv.items);
4110 fatal("the following command failed to execve with '{s}':\n{s}", .{ @errorName(err), cmd });4108 fatal("the following command failed to execve with '{s}':\n{s}", .{ @errorName(err), cmd });
4111 } else if (process.can_spawn) {4109 } else if (process.can_spawn) {
...@@ -4121,7 +4119,7 @@ fn runOrTest(...@@ -4121,7 +4119,7 @@ fn runOrTest(
4121 comp_destroyed.* = true;4119 comp_destroyed.* = true;
41224120
4123 const term = child.spawnAndWait() catch |err| {4121 const term = child.spawnAndWait() catch |err| {
4124 try warnAboutForeignBinaries(arena, arg_mode, target_info, link_libc);4122 try warnAboutForeignBinaries(arena, arg_mode, target, link_libc);
4125 const cmd = try std.mem.join(arena, " ", argv.items);4123 const cmd = try std.mem.join(arena, " ", argv.items);
4126 fatal("the following command failed with '{s}':\n{s}", .{ @errorName(err), cmd });4124 fatal("the following command failed with '{s}':\n{s}", .{ @errorName(err), cmd });
4127 };4125 };
...@@ -4820,12 +4818,10 @@ pub fn cmdLibC(gpa: Allocator, args: []const []const u8) !void {...@@ -4820,12 +4818,10 @@ pub fn cmdLibC(gpa: Allocator, args: []const []const u8) !void {
4820 if (!target_query.isNative()) {4818 if (!target_query.isNative()) {
4821 fatal("unable to detect libc for non-native target", .{});4819 fatal("unable to detect libc for non-native target", .{});
4822 }4820 }
4823 const target_info = try detectNativeTargetInfo(target_query);
4824
4825 var libc = LibCInstallation.findNative(.{4821 var libc = LibCInstallation.findNative(.{
4826 .allocator = gpa,4822 .allocator = gpa,
4827 .verbose = true,4823 .verbose = true,
4828 .target = target_info.target,4824 .target = try std.zig.system.resolveTargetQuery(target_query),
4829 }) catch |err| {4825 }) catch |err| {
4830 fatal("unable to detect native libc: {s}", .{@errorName(err)});4826 fatal("unable to detect native libc: {s}", .{@errorName(err)});
4831 };4827 };
...@@ -5114,11 +5110,11 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi...@@ -5114,11 +5110,11 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
5114 gimmeMoreOfThoseSweetSweetFileDescriptors();5110 gimmeMoreOfThoseSweetSweetFileDescriptors();
51155111
5116 const target_query: std.Target.Query = .{};5112 const target_query: std.Target.Query = .{};
5117 const target_info = try detectNativeTargetInfo(target_query);5113 const target = try std.zig.system.resolveTargetQuery(target_query);
51185114
5119 const exe_basename = try std.zig.binNameAlloc(arena, .{5115 const exe_basename = try std.zig.binNameAlloc(arena, .{
5120 .root_name = "build",5116 .root_name = "build",
5121 .target = target_info.target,5117 .target = target,
5122 .output_mode = .Exe,5118 .output_mode = .Exe,
5123 });5119 });
5124 const emit_bin: Compilation.EmitLoc = .{5120 const emit_bin: Compilation.EmitLoc = .{
...@@ -5282,10 +5278,10 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi...@@ -5282,10 +5278,10 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
5282 .local_cache_directory = local_cache_directory,5278 .local_cache_directory = local_cache_directory,
5283 .global_cache_directory = global_cache_directory,5279 .global_cache_directory = global_cache_directory,
5284 .root_name = "build",5280 .root_name = "build",
5285 .target = target_info.target,5281 .target = target,
5286 .is_native_os = target_query.isNativeOs(),5282 .is_native_os = target_query.isNativeOs(),
5287 .is_native_abi = target_query.isNativeAbi(),5283 .is_native_abi = target_query.isNativeAbi(),
5288 .dynamic_linker = target_info.dynamic_linker.get(),5284 .dynamic_linker = target.dynamic_linker.get(),
5289 .output_mode = .Exe,5285 .output_mode = .Exe,
5290 .main_mod = &main_mod,5286 .main_mod = &main_mod,
5291 .emit_bin = emit_bin,5287 .emit_bin = emit_bin,
...@@ -6269,10 +6265,6 @@ test "fds" {...@@ -6269,10 +6265,6 @@ test "fds" {
6269 gimmeMoreOfThoseSweetSweetFileDescriptors();6265 gimmeMoreOfThoseSweetSweetFileDescriptors();
6270}6266}
62716267
6272fn detectNativeTargetInfo(target_query: std.Target.Query) !std.zig.system.NativeTargetInfo {
6273 return std.zig.system.NativeTargetInfo.detect(target_query);
6274}
6275
6276const usage_ast_check =6268const usage_ast_check =
6277 \\Usage: zig ast-check [file]6269 \\Usage: zig ast-check [file]
6278 \\6270 \\
...@@ -6669,24 +6661,24 @@ fn parseIntSuffix(arg: []const u8, prefix_len: usize) u64 {...@@ -6669,24 +6661,24 @@ fn parseIntSuffix(arg: []const u8, prefix_len: usize) u64 {
6669fn warnAboutForeignBinaries(6661fn warnAboutForeignBinaries(
6670 arena: Allocator,6662 arena: Allocator,
6671 arg_mode: ArgMode,6663 arg_mode: ArgMode,
6672 target_info: *const std.zig.system.NativeTargetInfo,6664 target: *const std.Target,
6673 link_libc: bool,6665 link_libc: bool,
6674) !void {6666) !void {
6675 const host_query: std.Target.Query = .{};6667 const host_query: std.Target.Query = .{};
6676 const host_target_info = try detectNativeTargetInfo(host_query);6668 const host_target = try std.zig.system.resolveTargetQuery(host_query);
66776669
6678 switch (host_target_info.getExternalExecutor(target_info, .{ .link_libc = link_libc })) {6670 switch (std.zig.system.getExternalExecutor(host_target, target, .{ .link_libc = link_libc })) {
6679 .native => return,6671 .native => return,
6680 .rosetta => {6672 .rosetta => {
6681 const host_name = try host_target_info.target.zigTriple(arena);6673 const host_name = try host_target.zigTriple(arena);
6682 const foreign_name = try target_info.target.zigTriple(arena);6674 const foreign_name = try target.zigTriple(arena);
6683 warn("the host system ({s}) does not appear to be capable of executing binaries from the target ({s}). Consider installing Rosetta.", .{6675 warn("the host system ({s}) does not appear to be capable of executing binaries from the target ({s}). Consider installing Rosetta.", .{
6684 host_name, foreign_name,6676 host_name, foreign_name,
6685 });6677 });
6686 },6678 },
6687 .qemu => |qemu| {6679 .qemu => |qemu| {
6688 const host_name = try host_target_info.target.zigTriple(arena);6680 const host_name = try host_target.zigTriple(arena);
6689 const foreign_name = try target_info.target.zigTriple(arena);6681 const foreign_name = try target.zigTriple(arena);
6690 switch (arg_mode) {6682 switch (arg_mode) {
6691 .zig_test => warn(6683 .zig_test => warn(
6692 "the host system ({s}) does not appear to be capable of executing binaries " ++6684 "the host system ({s}) does not appear to be capable of executing binaries " ++
...@@ -6702,8 +6694,8 @@ fn warnAboutForeignBinaries(...@@ -6702,8 +6694,8 @@ fn warnAboutForeignBinaries(
6702 }6694 }
6703 },6695 },
6704 .wine => |wine| {6696 .wine => |wine| {
6705 const host_name = try host_target_info.target.zigTriple(arena);6697 const host_name = try host_target.zigTriple(arena);
6706 const foreign_name = try target_info.target.zigTriple(arena);6698 const foreign_name = try target.zigTriple(arena);
6707 switch (arg_mode) {6699 switch (arg_mode) {
6708 .zig_test => warn(6700 .zig_test => warn(
6709 "the host system ({s}) does not appear to be capable of executing binaries " ++6701 "the host system ({s}) does not appear to be capable of executing binaries " ++
...@@ -6719,8 +6711,8 @@ fn warnAboutForeignBinaries(...@@ -6719,8 +6711,8 @@ fn warnAboutForeignBinaries(
6719 }6711 }
6720 },6712 },
6721 .wasmtime => |wasmtime| {6713 .wasmtime => |wasmtime| {
6722 const host_name = try host_target_info.target.zigTriple(arena);6714 const host_name = try host_target.zigTriple(arena);
6723 const foreign_name = try target_info.target.zigTriple(arena);6715 const foreign_name = try target.zigTriple(arena);
6724 switch (arg_mode) {6716 switch (arg_mode) {
6725 .zig_test => warn(6717 .zig_test => warn(
6726 "the host system ({s}) does not appear to be capable of executing binaries " ++6718 "the host system ({s}) does not appear to be capable of executing binaries " ++
...@@ -6736,8 +6728,8 @@ fn warnAboutForeignBinaries(...@@ -6736,8 +6728,8 @@ fn warnAboutForeignBinaries(
6736 }6728 }
6737 },6729 },
6738 .darling => |darling| {6730 .darling => |darling| {
6739 const host_name = try host_target_info.target.zigTriple(arena);6731 const host_name = try host_target.zigTriple(arena);
6740 const foreign_name = try target_info.target.zigTriple(arena);6732 const foreign_name = try target.zigTriple(arena);
6741 switch (arg_mode) {6733 switch (arg_mode) {
6742 .zig_test => warn(6734 .zig_test => warn(
6743 "the host system ({s}) does not appear to be capable of executing binaries " ++6735 "the host system ({s}) does not appear to be capable of executing binaries " ++
...@@ -6753,7 +6745,7 @@ fn warnAboutForeignBinaries(...@@ -6753,7 +6745,7 @@ fn warnAboutForeignBinaries(
6753 }6745 }
6754 },6746 },
6755 .bad_dl => |foreign_dl| {6747 .bad_dl => |foreign_dl| {
6756 const host_dl = host_target_info.dynamic_linker.get() orelse "(none)";6748 const host_dl = host_target.dynamic_linker.get() orelse "(none)";
6757 const tip_suffix = switch (arg_mode) {6749 const tip_suffix = switch (arg_mode) {
6758 .zig_test => ", '--test-no-exec', or '--test-cmd'",6750 .zig_test => ", '--test-no-exec', or '--test-cmd'",
6759 else => "",6751 else => "",
...@@ -6763,8 +6755,8 @@ fn warnAboutForeignBinaries(...@@ -6763,8 +6755,8 @@ fn warnAboutForeignBinaries(
6763 });6755 });
6764 },6756 },
6765 .bad_os_or_cpu => {6757 .bad_os_or_cpu => {
6766 const host_name = try host_target_info.target.zigTriple(arena);6758 const host_name = try host_target.zigTriple(arena);
6767 const foreign_name = try target_info.target.zigTriple(arena);6759 const foreign_name = try target.zigTriple(arena);
6768 const tip_suffix = switch (arg_mode) {6760 const tip_suffix = switch (arg_mode) {
6769 .zig_test => ". Consider using '--test-no-exec' or '--test-cmd'",6761 .zig_test => ". Consider using '--test-no-exec' or '--test-cmd'",
6770 else => "",6762 else => "",
src/print_env.zig+2-2
...@@ -17,8 +17,8 @@ pub fn cmdEnv(arena: Allocator, args: []const []const u8, stdout: std.fs.File.Wr...@@ -17,8 +17,8 @@ pub fn cmdEnv(arena: Allocator, args: []const []const u8, stdout: std.fs.File.Wr
1717
18 const global_cache_dir = try introspect.resolveGlobalCacheDir(arena);18 const global_cache_dir = try introspect.resolveGlobalCacheDir(arena);
1919
20 const info = try std.zig.system.NativeTargetInfo.detect(.{});20 const host = try std.zig.system.resolveTargetQuery(.{});
21 const triple = try info.target.zigTriple(arena);21 const triple = try host.zigTriple(arena);
2222
23 var bw = std.io.bufferedWriter(stdout);23 var bw = std.io.bufferedWriter(stdout);
24 const w = bw.writer();24 const w = bw.writer();
test/src/Cases.zig+13-17
...@@ -541,7 +541,7 @@ pub fn lowerToBuildSteps(...@@ -541,7 +541,7 @@ pub fn lowerToBuildSteps(
541 cases_dir_path: []const u8,541 cases_dir_path: []const u8,
542 incremental_exe: *std.Build.Step.Compile,542 incremental_exe: *std.Build.Step.Compile,
543) void {543) void {
544 const host = std.zig.system.NativeTargetInfo.detect(.{}) catch |err|544 const host = std.zig.system.resolveTargetQuery(.{}) catch |err|
545 std.debug.panic("unable to detect native host: {s}\n", .{@errorName(err)});545 std.debug.panic("unable to detect native host: {s}\n", .{@errorName(err)});
546546
547 for (self.incremental_cases.items) |incr_case| {547 for (self.incremental_cases.items) |incr_case| {
...@@ -648,8 +648,7 @@ pub fn lowerToBuildSteps(...@@ -648,8 +648,7 @@ pub fn lowerToBuildSteps(
648 },648 },
649 .Execution => |expected_stdout| no_exec: {649 .Execution => |expected_stdout| no_exec: {
650 const run = if (case.target.target.ofmt == .c) run_step: {650 const run = if (case.target.target.ofmt == .c) run_step: {
651 const target_info = case.target.toNativeTargetInfo();651 if (getExternalExecutor(host, &case.target.target, .{ .link_libc = true }) != .native) {
652 if (host.getExternalExecutor(&target_info, .{ .link_libc = true }) != .native) {
653 // We wouldn't be able to run the compiled C code.652 // We wouldn't be able to run the compiled C code.
654 break :no_exec;653 break :no_exec;
655 }654 }
...@@ -694,8 +693,7 @@ pub fn lowerToBuildSteps(...@@ -694,8 +693,7 @@ pub fn lowerToBuildSteps(
694 continue; // Pass test.693 continue; // Pass test.
695 }694 }
696695
697 const target_info = case.target.toNativeTargetInfo();696 if (getExternalExecutor(host, &case.target.target, .{ .link_libc = true }) != .native) {
698 if (host.getExternalExecutor(&target_info, .{ .link_libc = true }) != .native) {
699 // We wouldn't be able to run the compiled C code.697 // We wouldn't be able to run the compiled C code.
700 continue; // Pass test.698 continue; // Pass test.
701 }699 }
...@@ -1199,6 +1197,8 @@ const builtin = @import("builtin");...@@ -1199,6 +1197,8 @@ const builtin = @import("builtin");
1199const std = @import("std");1197const std = @import("std");
1200const assert = std.debug.assert;1198const assert = std.debug.assert;
1201const Allocator = std.mem.Allocator;1199const Allocator = std.mem.Allocator;
1200const getExternalExecutor = std.zig.system.getExternalExecutor;
1201
1202const Compilation = @import("../../src/Compilation.zig");1202const Compilation = @import("../../src/Compilation.zig");
1203const zig_h = @import("../../src/link.zig").File.C.zig_h;1203const zig_h = @import("../../src/link.zig").File.C.zig_h;
1204const introspect = @import("../../src/introspect.zig");1204const introspect = @import("../../src/introspect.zig");
...@@ -1386,18 +1386,15 @@ pub fn main() !void {...@@ -1386,18 +1386,15 @@ pub fn main() !void {
1386}1386}
13871387
1388fn resolveTargetQuery(query: std.Target.Query) std.Build.ResolvedTarget {1388fn resolveTargetQuery(query: std.Target.Query) std.Build.ResolvedTarget {
1389 const result = std.zig.system.NativeTargetInfo.detect(query) catch
1390 @panic("unable to resolve target query");
1391
1392 return .{1389 return .{
1393 .query = query,1390 .query = query,
1394 .target = result.target,1391 .target = std.zig.system.resolveTargetQuery(query) catch
1395 .dynamic_linker = result.dynamic_linker,1392 @panic("unable to resolve target query"),
1396 };1393 };
1397}1394}
13981395
1399fn runCases(self: *Cases, zig_exe_path: []const u8) !void {1396fn runCases(self: *Cases, zig_exe_path: []const u8) !void {
1400 const host = try std.zig.system.NativeTargetInfo.detect(.{});1397 const host = try std.zig.system.resolveTargetQuery(.{});
14011398
1402 var progress = std.Progress{};1399 var progress = std.Progress{};
1403 const root_node = progress.start("compiler", self.cases.items.len);1400 const root_node = progress.start("compiler", self.cases.items.len);
...@@ -1478,7 +1475,7 @@ fn runOneCase(...@@ -1478,7 +1475,7 @@ fn runOneCase(
1478 zig_exe_path: []const u8,1475 zig_exe_path: []const u8,
1479 thread_pool: *ThreadPool,1476 thread_pool: *ThreadPool,
1480 global_cache_directory: Compilation.Directory,1477 global_cache_directory: Compilation.Directory,
1481 host: std.zig.system.NativeTargetInfo,1478 host: std.Target,
1482) !void {1479) !void {
1483 const tmp_src_path = "tmp.zig";1480 const tmp_src_path = "tmp.zig";
1484 const enable_rosetta = build_options.enable_rosetta;1481 const enable_rosetta = build_options.enable_rosetta;
...@@ -1488,8 +1485,7 @@ fn runOneCase(...@@ -1488,8 +1485,7 @@ fn runOneCase(
1488 const enable_darling = build_options.enable_darling;1485 const enable_darling = build_options.enable_darling;
1489 const glibc_runtimes_dir: ?[]const u8 = build_options.glibc_runtimes_dir;1486 const glibc_runtimes_dir: ?[]const u8 = build_options.glibc_runtimes_dir;
14901487
1491 const target_info = try std.zig.system.NativeTargetInfo.detect(case.target);1488 const target = try std.zig.system.resolveTargetQuery(case.target);
1492 const target = target_info.target;
14931489
1494 var arena_allocator = std.heap.ArenaAllocator.init(allocator);1490 var arena_allocator = std.heap.ArenaAllocator.init(allocator);
1495 defer arena_allocator.deinit();1491 defer arena_allocator.deinit();
...@@ -1579,7 +1575,7 @@ fn runOneCase(...@@ -1579,7 +1575,7 @@ fn runOneCase(
1579 .keep_source_files_loaded = true,1575 .keep_source_files_loaded = true,
1580 .is_native_os = case.target.isNativeOs(),1576 .is_native_os = case.target.isNativeOs(),
1581 .is_native_abi = case.target.isNativeAbi(),1577 .is_native_abi = case.target.isNativeAbi(),
1582 .dynamic_linker = target_info.dynamic_linker.get(),1578 .dynamic_linker = target.dynamic_linker.get(),
1583 .link_libc = case.link_libc,1579 .link_libc = case.link_libc,
1584 .use_llvm = use_llvm,1580 .use_llvm = use_llvm,
1585 .self_exe_path = zig_exe_path,1581 .self_exe_path = zig_exe_path,
...@@ -1715,7 +1711,7 @@ fn runOneCase(...@@ -1715,7 +1711,7 @@ fn runOneCase(
1715 .{ &tmp.sub_path, bin_name },1711 .{ &tmp.sub_path, bin_name },
1716 );1712 );
1717 if (case.target.ofmt != null and case.target.ofmt.? == .c) {1713 if (case.target.ofmt != null and case.target.ofmt.? == .c) {
1718 if (host.getExternalExecutor(target_info, .{ .link_libc = true }) != .native) {1714 if (getExternalExecutor(host, &target, .{ .link_libc = true }) != .native) {
1719 // We wouldn't be able to run the compiled C code.1715 // We wouldn't be able to run the compiled C code.
1720 continue :update; // Pass test.1716 continue :update; // Pass test.
1721 }1717 }
...@@ -1734,7 +1730,7 @@ fn runOneCase(...@@ -1734,7 +1730,7 @@ fn runOneCase(
1734 if (zig_lib_directory.path) |p| {1730 if (zig_lib_directory.path) |p| {
1735 try argv.appendSlice(&.{ "-I", p });1731 try argv.appendSlice(&.{ "-I", p });
1736 }1732 }
1737 } else switch (host.getExternalExecutor(target_info, .{ .link_libc = case.link_libc })) {1733 } else switch (getExternalExecutor(host, &target, .{ .link_libc = case.link_libc })) {
1738 .native => {1734 .native => {
1739 if (case.backend == .stage2 and case.target.getCpuArch().isArmOrThumb()) {1735 if (case.backend == .stage2 and case.target.getCpuArch().isArmOrThumb()) {
1740 // https://github.com/ziglang/zig/issues/136231736 // https://github.com/ziglang/zig/issues/13623