| author | |
| committer | |
| log | dbdb87502d6936597424fe86842c15978ba86918 |
| tree | 8e9fc73fad1d855368a85b7bceab4a725494b47b |
| parent | 3179f58c414b5e4845b9bf3acdf276fe8e2b88a0 |
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 | }; |
| 48 | 48 | ||
| 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 | }; |
| 55 | 53 | ||
| 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 { |
| 2129 | pub const ResolvedTarget = struct { | 2129 | pub 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 | }; |
| 2141 | 2133 | ||
| 2142 | /// Converts a target query into a fully resolved target that can be passed to | 2134 | /// 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; |
| 2148 | 2140 | ||
| 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 | } |
| 2158 | 2147 |
lib/std/Build/Module.zig-1| ... | @@ -746,5 +746,4 @@ const Module = @This(); | ... | @@ -746,5 +746,4 @@ const Module = @This(); |
| 746 | const std = @import("std"); | 746 | const std = @import("std"); |
| 747 | const assert = std.debug.assert; | 747 | const assert = std.debug.assert; |
| 748 | const LazyPath = std.Build.LazyPath; | 748 | const LazyPath = std.Build.LazyPath; |
| 749 | const NativeTargetInfo = std.zig.system.NativeTargetInfo; | ||
| 750 | const Step = std.Build.Step; | 749 | const 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; |
| 9 | const Sha256 = std.crypto.hash.sha2.Sha256; | 9 | const Sha256 = std.crypto.hash.sha2.Sha256; |
| 10 | const Allocator = mem.Allocator; | 10 | const Allocator = mem.Allocator; |
| 11 | const Step = std.Build.Step; | 11 | const Step = std.Build.Step; |
| 12 | const NativeTargetInfo = std.zig.system.NativeTargetInfo; | ||
| 13 | const LazyPath = std.Build.LazyPath; | 12 | const LazyPath = std.Build.LazyPath; |
| 14 | const PkgConfigPkg = std.Build.PkgConfigPkg; | 13 | const PkgConfigPkg = std.Build.PkgConfigPkg; |
| 15 | const PkgConfigError = std.Build.PkgConfigError; | 14 | const 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(); |
| 296 | 296 | ||
| 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 | }; |
| 303 | 301 | ||
| 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( |
| 678 | 678 | ||
| 679 | const need_cross_glibc = exe.rootModuleTarget().isGnuLibC() and | 679 | 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; |
| 754 | 754 | ||
| 755 | const host_dl = b.host.dynamic_linker.get() orelse "(none)"; | 755 | const host_dl = b.host.target.dynamic_linker.get() orelse "(none)"; |
| 756 | 756 | ||
| 757 | return step.fail( | 757 | return step.fail( |
| 758 | \\the host system is unable to execute binaries from the target | 758 | \\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 | |||
| 1 | cpu: Cpu, | 6 | cpu: Cpu, |
| 2 | os: Os, | 7 | os: Os, |
| 3 | abi: Abi, | 8 | abi: Abi, |
| 4 | ofmt: ObjectFormat, | 9 | ofmt: ObjectFormat, |
| 10 | dynamic_linker: DynamicLinker = DynamicLinker.none, | ||
| 5 | 11 | ||
| 6 | pub const Query = @import("Target/Query.zig"); | 12 | pub const Query = @import("Target/Query.zig"); |
| 7 | 13 | ||
| ... | @@ -1529,13 +1535,19 @@ pub inline fn hasDynamicLinker(self: Target) bool { | ... | @@ -1529,13 +1535,19 @@ pub inline fn hasDynamicLinker(self: Target) bool { |
| 1529 | } | 1535 | } |
| 1530 | 1536 | ||
| 1531 | pub const DynamicLinker = struct { | 1537 | pub const DynamicLinker = struct { |
| 1532 | /// Contains the memory used to store the dynamic linker path. This field should | 1538 | /// 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, | ||
| 1535 | 1542 | ||
| 1536 | /// Used to construct the dynamic linker path. This field should not be used | 1543 | /// 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 | }; | ||
| 1539 | 1551 | ||
| 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 | }; |
| 1563 | 1575 | ||
| 1564 | pub fn standardDynamicLinkerPath(self: Target) DynamicLinker { | 1576 | pub fn standardDynamicLinkerPath(target: Target) DynamicLinker { |
| 1565 | var result: DynamicLinker = .{}; | 1577 | return standardDynamicLinkerPath_cpu_os_abi(target.cpu, target.os.tag, target.abi); |
| 1578 | } | ||
| 1579 | |||
| 1580 | pub 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; |
| 1579 | 1595 | ||
| 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 | } |
| 1584 | 1600 | ||
| 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 | } |
| 1598 | 1614 | ||
| 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 | } |
| 1864 | 1880 | ||
| 1865 | pub fn ptrBitWidth(target: Target) u16 { | 1881 | pub 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, |
| 1876 | 1892 | ||
| 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, |
| 1912 | 1928 | ||
| 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, |
| 1937 | 1953 | ||
| 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 | |||
| 1958 | pub fn ptrBitWidth(target: Target) u16 { | ||
| 1959 | return ptrBitWidth_cpu_abi(target.cpu, target.abi); | ||
| 1940 | } | 1960 | } |
| 1941 | 1961 | ||
| 1942 | pub fn stackAlignment(target: Target) u16 { | 1962 | pub 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, |
| 34 | 34 | ||
| 35 | /// When `os_tag` is `null`, then `null` means native. Otherwise it means the standard path | 35 | /// 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`. |
| 37 | dynamic_linker: DynamicLinker = DynamicLinker{}, | 37 | dynamic_linker: Target.DynamicLinker = Target.DynamicLinker.none, |
| 38 | 38 | ||
| 39 | /// `null` means default for the cpu/arch/os combo. | 39 | /// `null` means default for the cpu/arch/os combo. |
| 40 | ofmt: ?Target.ObjectFormat = null, | 40 | ofmt: ?Target.ObjectFormat = null, |
| ... | @@ -61,8 +61,6 @@ pub const OsVersion = union(enum) { | ... | @@ -61,8 +61,6 @@ pub const OsVersion = union(enum) { |
| 61 | 61 | ||
| 62 | pub const SemanticVersion = std.SemanticVersion; | 62 | pub const SemanticVersion = std.SemanticVersion; |
| 63 | 63 | ||
| 64 | pub const DynamicLinker = Target.DynamicLinker; | ||
| 65 | |||
| 66 | pub fn fromTarget(target: Target) Query { | 64 | pub 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 | } |
| 166 | 164 | ||
| 167 | /// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`. | 165 | /// TODO deprecated, use `std.zig.system.resolveTargetQuery`. |
| 168 | pub fn toTarget(self: Query) Target { | 166 | pub 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; |
| 233 | 231 | ||
| 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 | }; |
| 237 | 235 | ||
| 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 | } |
| 381 | 379 | ||
| 382 | /// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`. | 380 | /// TODO deprecated, use `std.zig.system.resolveTargetQuery`. |
| 383 | pub fn getCpu(self: Query) Target.Cpu { | 381 | pub 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 using | 384 | // 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, and | 385 | // 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 using | 395 | // 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, and | 396 | // 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 | } |
| 428 | 426 | ||
| 429 | /// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`. | 427 | /// TODO deprecated, use `std.zig.system.resolveTargetQuery`. |
| 430 | pub fn getOs(self: Query) Target.Os { | 428 | pub fn getOs(self: Query) Target.Os { |
| 431 | // `builtin.os` works when doing `zig build` because Zig generates a build executable using | 429 | // `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, and | 430 | // 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; |
| 435 | 433 | ||
| 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 | } |
| 465 | 463 | ||
| 466 | /// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`. | 464 | /// TODO deprecated, use `std.zig.system.resolveTargetQuery`. |
| 467 | pub fn getOsVersionMin(self: Query) OsVersion { | 465 | pub 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 | } |
| 473 | 471 | ||
| 474 | /// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`. | 472 | /// TODO deprecated, use `std.zig.system.resolveTargetQuery`. |
| 475 | pub fn getOsVersionMax(self: Query) OsVersion { | 473 | pub 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 | } |
| 481 | 479 | ||
| 482 | /// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`. | 480 | /// TODO deprecated, use `std.zig.system.resolveTargetQuery`. |
| 483 | pub fn getAbi(self: Query) Target.Abi { | 481 | pub fn getAbi(self: Query) Target.Abi { |
| 484 | if (self.abi) |abi| return abi; | 482 | if (self.abi) |abi| return abi; |
| 485 | 483 | ||
| 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 using | 485 | // 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, and | 486 | // 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 | } |
| 492 | 490 |
lib/std/zig/system.zig+1116-2| ... | @@ -1,13 +1,1127 @@ | ... | @@ -1,13 +1,1127 @@ |
| 1 | pub const NativePaths = @import("system/NativePaths.zig"); | 1 | pub const NativePaths = @import("system/NativePaths.zig"); |
| 2 | pub const NativeTargetInfo = @import("system/NativeTargetInfo.zig"); | ||
| 3 | 2 | ||
| 4 | pub const windows = @import("system/windows.zig"); | 3 | pub const windows = @import("system/windows.zig"); |
| 5 | pub const darwin = @import("system/darwin.zig"); | 4 | pub const darwin = @import("system/darwin.zig"); |
| 6 | pub const linux = @import("system/linux.zig"); | 5 | pub const linux = @import("system/linux.zig"); |
| 7 | 6 | ||
| 7 | pub 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 | |||
| 18 | pub 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. | ||
| 30 | pub 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 | |||
| 149 | pub 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. | ||
| 165 | pub 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 | |||
| 368 | fn 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 | |||
| 391 | pub 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 | |||
| 408 | pub 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 | |||
| 686 | fn 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 | |||
| 702 | test 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 | |||
| 713 | fn 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 | |||
| 796 | fn 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. | ||
| 906 | fn 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 | |||
| 1054 | fn 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 | |||
| 1068 | const LdInfo = struct { | ||
| 1069 | ld: Target.DynamicLinker, | ||
| 1070 | abi: Target.Abi, | ||
| 1071 | }; | ||
| 1072 | |||
| 1073 | fn 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 | |||
| 1098 | fn 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 | |||
| 1114 | const builtin = @import("builtin"); | ||
| 1115 | const std = @import("../std.zig"); | ||
| 1116 | const mem = std.mem; | ||
| 1117 | const elf = std.elf; | ||
| 1118 | const fs = std.fs; | ||
| 1119 | const assert = std.debug.assert; | ||
| 1120 | const Target = std.Target; | ||
| 1121 | const native_endian = builtin.cpu.arch.endian(); | ||
| 1122 | |||
| 8 | test { | 1123 | test { |
| 9 | _ = NativePaths; | 1124 | _ = NativePaths; |
| 10 | _ = NativeTargetInfo; | ||
| 11 | 1125 | ||
| 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; |
| 5 | const mem = std.mem; | 5 | const mem = std.mem; |
| 6 | 6 | ||
| 7 | const NativePaths = @This(); | 7 | const NativePaths = @This(); |
| 8 | const NativeTargetInfo = std.zig.system.NativeTargetInfo; | ||
| 9 | 8 | ||
| 10 | arena: Allocator, | 9 | arena: Allocator, |
| 11 | include_dirs: std.ArrayListUnmanaged([]const u8) = .{}, | 10 | include_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) = .{}, |
| 14 | rpaths: std.ArrayListUnmanaged([]const u8) = .{}, | 13 | rpaths: std.ArrayListUnmanaged([]const u8) = .{}, |
| 15 | warnings: std.ArrayListUnmanaged([]const u8) = .{}, | 14 | warnings: std.ArrayListUnmanaged([]const u8) = .{}, |
| 16 | 15 | ||
| 17 | pub fn detect(arena: Allocator, native_info: NativeTargetInfo) !NativePaths { | 16 | pub 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 @@ | ||
| 1 | const std = @import("../../std.zig"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const mem = std.mem; | ||
| 4 | const assert = std.debug.assert; | ||
| 5 | const fs = std.fs; | ||
| 6 | const elf = std.elf; | ||
| 7 | const native_endian = builtin.cpu.arch.endian(); | ||
| 8 | |||
| 9 | const NativeTargetInfo = @This(); | ||
| 10 | const Target = std.Target; | ||
| 11 | const Allocator = std.mem.Allocator; | ||
| 12 | const windows = std.zig.system.windows; | ||
| 13 | const darwin = std.zig.system.darwin; | ||
| 14 | const linux = std.zig.system.linux; | ||
| 15 | |||
| 16 | target: Target, | ||
| 17 | dynamic_linker: DynamicLinker = DynamicLinker{}, | ||
| 18 | |||
| 19 | pub const DynamicLinker = Target.DynamicLinker; | ||
| 20 | |||
| 21 | pub 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. | ||
| 37 | pub 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. | ||
| 253 | fn 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 | |||
| 401 | fn 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 | |||
| 484 | fn 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 | |||
| 581 | fn 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 | |||
| 597 | test 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 | |||
| 608 | pub 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 | |||
| 625 | pub 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 | |||
| 905 | fn 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 | |||
| 930 | fn 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 | |||
| 946 | pub const LdInfo = struct { | ||
| 947 | ld: DynamicLinker, | ||
| 948 | abi: Target.Abi, | ||
| 949 | }; | ||
| 950 | |||
| 951 | pub 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 | |||
| 967 | fn 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 | |||
| 990 | pub 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 | |||
| 1001 | pub 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. | ||
| 1013 | pub 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( |
| 6611 | 6610 | \\}; | |
| 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 | } |
| 2609 | 2610 | ||
| 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); |
| 2612 | 2613 | ||
| 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 | } |
| 2636 | 2637 | ||
| 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 | } |
| 2654 | 2655 | ||
| 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 | } |
| 2680 | 2681 | ||
| 2681 | if (!target_info.target.cpu.features.isEnabled(@intFromEnum(std.Target.wasm.Feature.atomics)) or | 2682 | 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 | } |
| 2778 | 2779 | ||
| 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 | } |
| 2799 | 2800 | ||
| 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 | } |
| 2822 | 2823 | ||
| 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() and | 2839 | 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 | } |
| 2858 | 2859 | ||
| 2859 | if (builtin.target.os.tag == .windows and | 2860 | if (builtin.target.os.tag == .windows and |
| 2860 | target_info.target.abi == .msvc and | 2861 | 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. |
| 3091 | 3092 | ||
| 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 | } |
| 3103 | 3102 | ||
| ... | @@ -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 | else | 3115 | else |
| ... | @@ -3119,7 +3118,7 @@ fn buildOutputType( | ... | @@ -3119,7 +3118,7 @@ fn buildOutputType( |
| 3119 | }, | 3118 | }, |
| 3120 | }; | 3119 | }; |
| 3121 | 3120 | ||
| 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 execute | 3268 | // 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 and | 3270 | 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 switch | 3424 | // 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}); |
| 3609 | 3607 | ||
| ... | @@ -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); |
| 3694 | 3692 | ||
| 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( |
| 3707 | 3705 | ||
| 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; |
| 4122 | 4120 | ||
| 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(); |
| 5115 | 5111 | ||
| 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); |
| 5118 | 5114 | ||
| 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 | } |
| 6271 | 6267 | ||
| 6272 | fn detectNativeTargetInfo(target_query: std.Target.Query) !std.zig.system.NativeTargetInfo { | ||
| 6273 | return std.zig.system.NativeTargetInfo.detect(target_query); | ||
| 6274 | } | ||
| 6275 | |||
| 6276 | const usage_ast_check = | 6268 | const 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 { |
| 6669 | fn warnAboutForeignBinaries( | 6661 | fn 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); |
| 6677 | 6669 | ||
| 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 |
| 17 | 17 | ||
| 18 | const global_cache_dir = try introspect.resolveGlobalCacheDir(arena); | 18 | const global_cache_dir = try introspect.resolveGlobalCacheDir(arena); |
| 19 | 19 | ||
| 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); |
| 22 | 22 | ||
| 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)}); |
| 546 | 546 | ||
| 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 | } |
| 696 | 695 | ||
| 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"); |
| 1199 | const std = @import("std"); | 1197 | const std = @import("std"); |
| 1200 | const assert = std.debug.assert; | 1198 | const assert = std.debug.assert; |
| 1201 | const Allocator = std.mem.Allocator; | 1199 | const Allocator = std.mem.Allocator; |
| 1200 | const getExternalExecutor = std.zig.system.getExternalExecutor; | ||
| 1201 | |||
| 1202 | const Compilation = @import("../../src/Compilation.zig"); | 1202 | const Compilation = @import("../../src/Compilation.zig"); |
| 1203 | const zig_h = @import("../../src/link.zig").File.C.zig_h; | 1203 | const zig_h = @import("../../src/link.zig").File.C.zig_h; |
| 1204 | const introspect = @import("../../src/introspect.zig"); | 1204 | const introspect = @import("../../src/introspect.zig"); |
| ... | @@ -1386,18 +1386,15 @@ pub fn main() !void { | ... | @@ -1386,18 +1386,15 @@ pub fn main() !void { |
| 1386 | } | 1386 | } |
| 1387 | 1387 | ||
| 1388 | fn resolveTargetQuery(query: std.Target.Query) std.Build.ResolvedTarget { | 1388 | fn 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 | } |
| 1398 | 1395 | ||
| 1399 | fn runCases(self: *Cases, zig_exe_path: []const u8) !void { | 1396 | fn 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(.{}); |
| 1401 | 1398 | ||
| 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; |
| 1490 | 1487 | ||
| 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; | ||
| 1493 | 1489 | ||
| 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/13623 | 1736 | // https://github.com/ziglang/zig/issues/13623 |