authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-06 18:34:51-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-29 06:20:48-07:00
log066864a0bf59bc1a926412b3c6e4d2d0c65e5642
treef9336243c45e2209205baa3bf48ae9836e04ee8b
parentb428612a202a76f7a0aee18bde00c104753f3e60

std.zig.system: upgrade to std.Io.Reader


12 files changed, 433 insertions(+), 518 deletions(-)

lib/compiler/build_runner.zig+5
...@@ -38,6 +38,10 @@ pub fn main() !void {...@@ -38,6 +38,10 @@ pub fn main() !void {
3838
39 const args = try process.argsAlloc(arena);39 const args = try process.argsAlloc(arena);
4040
41 var threaded: std.Io.Threaded = .init(gpa);
42 defer threaded.deinit();
43 const io = threaded.io();
44
41 // skip my own exe name45 // skip my own exe name
42 var arg_idx: usize = 1;46 var arg_idx: usize = 1;
4347
...@@ -68,6 +72,7 @@ pub fn main() !void {...@@ -68,6 +72,7 @@ pub fn main() !void {
68 };72 };
6973
70 var graph: std.Build.Graph = .{74 var graph: std.Build.Graph = .{
75 .io = io,
71 .arena = arena,76 .arena = arena,
72 .cache = .{77 .cache = .{
73 .gpa = arena,78 .gpa = arena,
lib/std/Build.zig+6-2
...@@ -1,5 +1,7 @@...@@ -1,5 +1,7 @@
1const std = @import("std.zig");
2const builtin = @import("builtin");1const builtin = @import("builtin");
2
3const std = @import("std.zig");
4const Io = std.Io;
3const fs = std.fs;5const fs = std.fs;
4const mem = std.mem;6const mem = std.mem;
5const debug = std.debug;7const debug = std.debug;
...@@ -110,6 +112,7 @@ pub const ReleaseMode = enum {...@@ -110,6 +112,7 @@ pub const ReleaseMode = enum {
110/// Shared state among all Build instances.112/// Shared state among all Build instances.
111/// Settings that are here rather than in Build are not configurable per-package.113/// Settings that are here rather than in Build are not configurable per-package.
112pub const Graph = struct {114pub const Graph = struct {
115 io: Io,
113 arena: Allocator,116 arena: Allocator,
114 system_library_options: std.StringArrayHashMapUnmanaged(SystemLibraryMode) = .empty,117 system_library_options: std.StringArrayHashMapUnmanaged(SystemLibraryMode) = .empty,
115 system_package_mode: bool = false,118 system_package_mode: bool = false,
...@@ -2666,9 +2669,10 @@ pub fn resolveTargetQuery(b: *Build, query: Target.Query) ResolvedTarget {...@@ -2666,9 +2669,10 @@ pub fn resolveTargetQuery(b: *Build, query: Target.Query) ResolvedTarget {
2666 // Hot path. This is faster than querying the native CPU and OS again.2669 // Hot path. This is faster than querying the native CPU and OS again.
2667 return b.graph.host;2670 return b.graph.host;
2668 }2671 }
2672 const io = b.graph.io;
2669 return .{2673 return .{
2670 .query = query,2674 .query = query,
2671 .result = std.zig.system.resolveTargetQuery(query) catch2675 .result = std.zig.system.resolveTargetQuery(io, query) catch
2672 @panic("unable to resolve target query"),2676 @panic("unable to resolve target query"),
2673 };2677 };
2674}2678}
lib/std/Build/Step/Options.zig+3-1
...@@ -532,6 +532,8 @@ const Arg = struct {...@@ -532,6 +532,8 @@ const Arg = struct {
532test Options {532test Options {
533 if (builtin.os.tag == .wasi) return error.SkipZigTest;533 if (builtin.os.tag == .wasi) return error.SkipZigTest;
534534
535 const io = std.testing.io;
536
535 var arena = std.heap.ArenaAllocator.init(std.testing.allocator);537 var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
536 defer arena.deinit();538 defer arena.deinit();
537539
...@@ -546,7 +548,7 @@ test Options {...@@ -546,7 +548,7 @@ test Options {
546 .global_cache_root = .{ .path = "test", .handle = std.fs.cwd() },548 .global_cache_root = .{ .path = "test", .handle = std.fs.cwd() },
547 .host = .{549 .host = .{
548 .query = .{},550 .query = .{},
549 .result = try std.zig.system.resolveTargetQuery(.{}),551 .result = try std.zig.system.resolveTargetQuery(io, .{}),
550 },552 },
551 .zig_lib_directory = std.Build.Cache.Directory.cwd(),553 .zig_lib_directory = std.Build.Cache.Directory.cwd(),
552 .time_report = false,554 .time_report = false,
lib/std/Build/WebServer.zig+2-1
...@@ -516,6 +516,7 @@ pub fn serveTarFile(...@@ -516,6 +516,7 @@ pub fn serveTarFile(
516}516}
517517
518fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.OptimizeMode) !Cache.Path {518fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.OptimizeMode) !Cache.Path {
519 const io = ws.graph.io;
519 const root_name = "build-web";520 const root_name = "build-web";
520 const arch_os_abi = "wasm32-freestanding";521 const arch_os_abi = "wasm32-freestanding";
521 const cpu_features = "baseline+atomics+bulk_memory+multivalue+mutable_globals+nontrapping_fptoint+reference_types+sign_ext";522 const cpu_features = "baseline+atomics+bulk_memory+multivalue+mutable_globals+nontrapping_fptoint+reference_types+sign_ext";
...@@ -659,7 +660,7 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim...@@ -659,7 +660,7 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim
659 };660 };
660 const bin_name = try std.zig.binNameAlloc(arena, .{661 const bin_name = try std.zig.binNameAlloc(arena, .{
661 .root_name = root_name,662 .root_name = root_name,
662 .target = &(std.zig.system.resolveTargetQuery(std.Build.parseTargetQuery(.{663 .target = &(std.zig.system.resolveTargetQuery(io, std.Build.parseTargetQuery(.{
663 .arch_os_abi = arch_os_abi,664 .arch_os_abi = arch_os_abi,
664 .cpu_features = cpu_features,665 .cpu_features = cpu_features,
665 }) catch unreachable) catch unreachable),666 }) catch unreachable) catch unreachable),
lib/std/Io.zig+3-3
...@@ -738,9 +738,9 @@ pub const Timestamp = struct {...@@ -738,9 +738,9 @@ pub const Timestamp = struct {
738 /// * On Linux, corresponds `CLOCK_MONOTONIC`.738 /// * On Linux, corresponds `CLOCK_MONOTONIC`.
739 /// * On macOS, corresponds to `CLOCK_UPTIME_RAW`.739 /// * On macOS, corresponds to `CLOCK_UPTIME_RAW`.
740 awake,740 awake,
741 /// Identical to `awake` except it expresses intent to include time741 /// Identical to `awake` except it expresses intent to **include time
742 /// that the system is suspended, however, it may be implemented742 /// that the system is suspended**, however, due to limitations it may
743 /// identically to `awake`.743 /// behave identically to `awake`.
744 ///744 ///
745 /// * On Linux, corresponds `CLOCK_BOOTTIME`.745 /// * On Linux, corresponds `CLOCK_BOOTTIME`.
746 /// * On macOS, corresponds to `CLOCK_MONOTONIC_RAW`.746 /// * On macOS, corresponds to `CLOCK_MONOTONIC_RAW`.
lib/std/Io/Threaded.zig+5-8
...@@ -1054,7 +1054,7 @@ fn nowWasi(userdata: ?*anyopaque, clock: Io.Timestamp.Clock) Io.Timestamp.Error!...@@ -1054,7 +1054,7 @@ fn nowWasi(userdata: ?*anyopaque, clock: Io.Timestamp.Clock) Io.Timestamp.Error!
1054fn sleepLinux(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {1054fn sleepLinux(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {
1055 const pool: *Pool = @ptrCast(@alignCast(userdata));1055 const pool: *Pool = @ptrCast(@alignCast(userdata));
1056 const clock_id: posix.clockid_t = clockToPosix(switch (timeout) {1056 const clock_id: posix.clockid_t = clockToPosix(switch (timeout) {
1057 .none => .monotonic,1057 .none => .awake,
1058 .duration => |d| d.clock,1058 .duration => |d| d.clock,
1059 .deadline => |d| d.clock,1059 .deadline => |d| d.clock,
1060 });1060 });
...@@ -1087,7 +1087,6 @@ fn sleepWindows(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {...@@ -1087,7 +1087,6 @@ fn sleepWindows(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {
1087 const ms = ms: {1087 const ms = ms: {
1088 const duration_and_clock = (try timeout.toDurationFromNow(pool.io())) orelse1088 const duration_and_clock = (try timeout.toDurationFromNow(pool.io())) orelse
1089 break :ms std.math.maxInt(windows.DWORD);1089 break :ms std.math.maxInt(windows.DWORD);
1090 if (duration_and_clock.clock != .monotonic) return error.UnsupportedClock;
1091 break :ms std.math.lossyCast(windows.DWORD, duration_and_clock.duration.toMilliseconds());1090 break :ms std.math.lossyCast(windows.DWORD, duration_and_clock.duration.toMilliseconds());
1092 };1091 };
1093 windows.kernel32.Sleep(ms);1092 windows.kernel32.Sleep(ms);
...@@ -1132,8 +1131,6 @@ fn sleepPosix(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {...@@ -1132,8 +1131,6 @@ fn sleepPosix(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {
1132 .sec = std.math.maxInt(sec_type),1131 .sec = std.math.maxInt(sec_type),
1133 .nsec = std.math.maxInt(nsec_type),1132 .nsec = std.math.maxInt(nsec_type),
1134 };1133 };
1135 // TODO check which clock nanosleep uses on this host
1136 // and return error.UnsupportedClock if it does not match
1137 const ns = d.duration.nanoseconds;1134 const ns = d.duration.nanoseconds;
1138 break :t .{1135 break :t .{
1139 .sec = @intCast(@divFloor(ns, std.time.ns_per_s)),1136 .sec = @intCast(@divFloor(ns, std.time.ns_per_s)),
...@@ -2046,9 +2043,9 @@ fn clockToPosix(clock: Io.Timestamp.Clock) posix.clockid_t {...@@ -2046,9 +2043,9 @@ fn clockToPosix(clock: Io.Timestamp.Clock) posix.clockid_t {
2046fn clockToWasi(clock: Io.Timestamp.Clock) std.os.wasi.clockid_t {2043fn clockToWasi(clock: Io.Timestamp.Clock) std.os.wasi.clockid_t {
2047 return switch (clock) {2044 return switch (clock) {
2048 .realtime => .REALTIME,2045 .realtime => .REALTIME,
2049 .monotonic => .MONOTONIC,2046 .awake => .MONOTONIC,
2050 .uptime => .MONOTONIC,2047 .boot => .MONOTONIC,
2051 .process_cputime_id => .PROCESS_CPUTIME_ID,2048 .cpu_process => .PROCESS_CPUTIME_ID,
2052 .thread_cputime_id => .THREAD_CPUTIME_ID,2049 .cpu_thread => .THREAD_CPUTIME_ID,
2053 };2050 };
2054}2051}
lib/std/Io/net.zig+1-1
...@@ -228,7 +228,7 @@ pub const IpAddress = union(enum) {...@@ -228,7 +228,7 @@ pub const IpAddress = union(enum) {
228 ///228 ///
229 /// One bound `Socket` can be used to receive messages from multiple229 /// One bound `Socket` can be used to receive messages from multiple
230 /// different addresses.230 /// different addresses.
231 pub fn bind(address: IpAddress, io: Io, options: BindOptions) BindError!Socket {231 pub fn bind(address: *const IpAddress, io: Io, options: BindOptions) BindError!Socket {
232 return io.vtable.ipBind(io.userdata, address, options);232 return io.vtable.ipBind(io.userdata, address, options);
233 }233 }
234234
lib/std/Target/Query.zig+7-5
...@@ -612,6 +612,8 @@ fn versionEqualOpt(a: ?SemanticVersion, b: ?SemanticVersion) bool {...@@ -612,6 +612,8 @@ fn versionEqualOpt(a: ?SemanticVersion, b: ?SemanticVersion) bool {
612}612}
613613
614test parse {614test parse {
615 const io = std.testing.io;
616
615 if (builtin.target.isGnuLibC()) {617 if (builtin.target.isGnuLibC()) {
616 var query = try Query.parse(.{});618 var query = try Query.parse(.{});
617 query.setGnuLibCVersion(2, 1, 1);619 query.setGnuLibCVersion(2, 1, 1);
...@@ -654,7 +656,7 @@ test parse {...@@ -654,7 +656,7 @@ test parse {
654 .arch_os_abi = "x86_64-linux-gnu",656 .arch_os_abi = "x86_64-linux-gnu",
655 .cpu_features = "x86_64-sse-sse2-avx-cx8",657 .cpu_features = "x86_64-sse-sse2-avx-cx8",
656 });658 });
657 const target = try std.zig.system.resolveTargetQuery(query);659 const target = try std.zig.system.resolveTargetQuery(io, query);
658660
659 try std.testing.expect(target.os.tag == .linux);661 try std.testing.expect(target.os.tag == .linux);
660 try std.testing.expect(target.abi == .gnu);662 try std.testing.expect(target.abi == .gnu);
...@@ -679,7 +681,7 @@ test parse {...@@ -679,7 +681,7 @@ test parse {
679 .arch_os_abi = "arm-linux-musleabihf",681 .arch_os_abi = "arm-linux-musleabihf",
680 .cpu_features = "generic+v8a",682 .cpu_features = "generic+v8a",
681 });683 });
682 const target = try std.zig.system.resolveTargetQuery(query);684 const target = try std.zig.system.resolveTargetQuery(io, query);
683685
684 try std.testing.expect(target.os.tag == .linux);686 try std.testing.expect(target.os.tag == .linux);
685 try std.testing.expect(target.abi == .musleabihf);687 try std.testing.expect(target.abi == .musleabihf);
...@@ -696,7 +698,7 @@ test parse {...@@ -696,7 +698,7 @@ test parse {
696 .arch_os_abi = "aarch64-linux.3.10...4.4.1-gnu.2.27",698 .arch_os_abi = "aarch64-linux.3.10...4.4.1-gnu.2.27",
697 .cpu_features = "generic+v8a",699 .cpu_features = "generic+v8a",
698 });700 });
699 const target = try std.zig.system.resolveTargetQuery(query);701 const target = try std.zig.system.resolveTargetQuery(io, query);
700702
701 try std.testing.expect(target.cpu.arch == .aarch64);703 try std.testing.expect(target.cpu.arch == .aarch64);
702 try std.testing.expect(target.os.tag == .linux);704 try std.testing.expect(target.os.tag == .linux);
...@@ -719,7 +721,7 @@ test parse {...@@ -719,7 +721,7 @@ test parse {
719 const query = try Query.parse(.{721 const query = try Query.parse(.{
720 .arch_os_abi = "aarch64-linux.3.10...4.4.1-android.30",722 .arch_os_abi = "aarch64-linux.3.10...4.4.1-android.30",
721 });723 });
722 const target = try std.zig.system.resolveTargetQuery(query);724 const target = try std.zig.system.resolveTargetQuery(io, query);
723725
724 try std.testing.expect(target.cpu.arch == .aarch64);726 try std.testing.expect(target.cpu.arch == .aarch64);
725 try std.testing.expect(target.os.tag == .linux);727 try std.testing.expect(target.os.tag == .linux);
...@@ -740,7 +742,7 @@ test parse {...@@ -740,7 +742,7 @@ test parse {
740 const query = try Query.parse(.{742 const query = try Query.parse(.{
741 .arch_os_abi = "x86-windows.xp...win8-msvc",743 .arch_os_abi = "x86-windows.xp...win8-msvc",
742 });744 });
743 const target = try std.zig.system.resolveTargetQuery(query);745 const target = try std.zig.system.resolveTargetQuery(io, query);
744746
745 try std.testing.expect(target.cpu.arch == .x86);747 try std.testing.expect(target.cpu.arch == .x86);
746 try std.testing.expect(target.os.tag == .windows);748 try std.testing.expect(target.os.tag == .windows);
lib/std/elf.zig+121-45
...@@ -1,9 +1,11 @@...@@ -1,9 +1,11 @@
1//! Executable and Linkable Format.1//! Executable and Linkable Format.
22
3const std = @import("std.zig");3const std = @import("std.zig");
4const Io = std.Io;
4const math = std.math;5const math = std.math;
5const mem = std.mem;6const mem = std.mem;
6const assert = std.debug.assert;7const assert = std.debug.assert;
8const Endian = std.builtin.Endian;
7const native_endian = @import("builtin").target.cpu.arch.endian();9const native_endian = @import("builtin").target.cpu.arch.endian();
810
9pub const AT_NULL = 0;11pub const AT_NULL = 0;
...@@ -568,7 +570,7 @@ pub const ET = enum(u16) {...@@ -568,7 +570,7 @@ pub const ET = enum(u16) {
568/// All integers are native endian.570/// All integers are native endian.
569pub const Header = struct {571pub const Header = struct {
570 is_64: bool,572 is_64: bool,
571 endian: std.builtin.Endian,573 endian: Endian,
572 os_abi: OSABI,574 os_abi: OSABI,
573 /// The meaning of this value depends on `os_abi`.575 /// The meaning of this value depends on `os_abi`.
574 abi_version: u8,576 abi_version: u8,
...@@ -583,48 +585,76 @@ pub const Header = struct {...@@ -583,48 +585,76 @@ pub const Header = struct {
583 shnum: u16,585 shnum: u16,
584 shstrndx: u16,586 shstrndx: u16,
585587
586 pub fn iterateProgramHeaders(h: Header, file_reader: *std.fs.File.Reader) ProgramHeaderIterator {588 pub fn iterateProgramHeaders(h: *const Header, file_reader: *Io.File.Reader) ProgramHeaderIterator {
587 return .{589 return .{
588 .elf_header = h,590 .is_64 = h.is_64,
591 .endian = h.endian,
592 .phnum = h.phnum,
593 .phoff = h.phoff,
589 .file_reader = file_reader,594 .file_reader = file_reader,
590 };595 };
591 }596 }
592597
593 pub fn iterateProgramHeadersBuffer(h: Header, buf: []const u8) ProgramHeaderBufferIterator {598 pub fn iterateProgramHeadersBuffer(h: *const Header, buf: []const u8) ProgramHeaderBufferIterator {
594 return .{599 return .{
595 .elf_header = h,600 .is_64 = h.is_64,
601 .endian = h.endian,
602 .phnum = h.phnum,
603 .phoff = h.phoff,
596 .buf = buf,604 .buf = buf,
597 };605 };
598 }606 }
599607
600 pub fn iterateSectionHeaders(h: Header, file_reader: *std.fs.File.Reader) SectionHeaderIterator {608 pub fn iterateSectionHeaders(h: *const Header, file_reader: *Io.File.Reader) SectionHeaderIterator {
601 return .{609 return .{
602 .elf_header = h,610 .is_64 = h.is_64,
611 .endian = h.endian,
612 .shnum = h.shnum,
613 .shoff = h.shoff,
603 .file_reader = file_reader,614 .file_reader = file_reader,
604 };615 };
605 }616 }
606617
607 pub fn iterateSectionHeadersBuffer(h: Header, buf: []const u8) SectionHeaderBufferIterator {618 pub fn iterateSectionHeadersBuffer(h: *const Header, buf: []const u8) SectionHeaderBufferIterator {
608 return .{619 return .{
609 .elf_header = h,620 .is_64 = h.is_64,
621 .endian = h.endian,
622 .shnum = h.shnum,
623 .shoff = h.shoff,
610 .buf = buf,624 .buf = buf,
611 };625 };
612 }626 }
613627
614 pub const ReadError = std.Io.Reader.Error || error{628 pub fn iterateDynamicSection(
629 h: *const Header,
630 file_reader: *Io.File.Reader,
631 offset: u64,
632 size: u64,
633 ) DynamicSectionIterator {
634 return .{
635 .is_64 = h.is_64,
636 .endian = h.endian,
637 .offset = offset,
638 .end_offset = offset + size,
639 .file_reader = file_reader,
640 };
641 }
642
643 pub const ReadError = Io.Reader.Error || error{
615 InvalidElfMagic,644 InvalidElfMagic,
616 InvalidElfVersion,645 InvalidElfVersion,
617 InvalidElfClass,646 InvalidElfClass,
618 InvalidElfEndian,647 InvalidElfEndian,
619 };648 };
620649
621 pub fn read(r: *std.Io.Reader) ReadError!Header {650 /// If this function fails, seek position of `r` is unchanged.
651 pub fn read(r: *Io.Reader) ReadError!Header {
622 const buf = try r.peek(@sizeOf(Elf64_Ehdr));652 const buf = try r.peek(@sizeOf(Elf64_Ehdr));
623653
624 if (!mem.eql(u8, buf[0..4], MAGIC)) return error.InvalidElfMagic;654 if (!mem.eql(u8, buf[0..4], MAGIC)) return error.InvalidElfMagic;
625 if (buf[EI.VERSION] != 1) return error.InvalidElfVersion;655 if (buf[EI.VERSION] != 1) return error.InvalidElfVersion;
626656
627 const endian: std.builtin.Endian = switch (buf[EI.DATA]) {657 const endian: Endian = switch (buf[EI.DATA]) {
628 ELFDATA2LSB => .little,658 ELFDATA2LSB => .little,
629 ELFDATA2MSB => .big,659 ELFDATA2MSB => .big,
630 else => return error.InvalidElfEndian,660 else => return error.InvalidElfEndian,
...@@ -637,7 +667,7 @@ pub const Header = struct {...@@ -637,7 +667,7 @@ pub const Header = struct {
637 };667 };
638 }668 }
639669
640 pub fn init(hdr: anytype, endian: std.builtin.Endian) Header {670 pub fn init(hdr: anytype, endian: Endian) Header {
641 // Converting integers to exhaustive enums using `@enumFromInt` could cause a panic.671 // Converting integers to exhaustive enums using `@enumFromInt` could cause a panic.
642 comptime assert(!@typeInfo(OSABI).@"enum".is_exhaustive);672 comptime assert(!@typeInfo(OSABI).@"enum".is_exhaustive);
643 return .{673 return .{
...@@ -664,46 +694,54 @@ pub const Header = struct {...@@ -664,46 +694,54 @@ pub const Header = struct {
664};694};
665695
666pub const ProgramHeaderIterator = struct {696pub const ProgramHeaderIterator = struct {
667 elf_header: Header,697 is_64: bool,
668 file_reader: *std.fs.File.Reader,698 endian: Endian,
699 phnum: u16,
700 phoff: u64,
701
702 file_reader: *Io.File.Reader,
669 index: usize = 0,703 index: usize = 0,
670704
671 pub fn next(it: *ProgramHeaderIterator) !?Elf64_Phdr {705 pub fn next(it: *ProgramHeaderIterator) !?Elf64_Phdr {
672 if (it.index >= it.elf_header.phnum) return null;706 if (it.index >= it.phnum) return null;
673 defer it.index += 1;707 defer it.index += 1;
674708
675 const size: u64 = if (it.elf_header.is_64) @sizeOf(Elf64_Phdr) else @sizeOf(Elf32_Phdr);709 const size: u64 = if (it.is_64) @sizeOf(Elf64_Phdr) else @sizeOf(Elf32_Phdr);
676 const offset = it.elf_header.phoff + size * it.index;710 const offset = it.phoff + size * it.index;
677 try it.file_reader.seekTo(offset);711 try it.file_reader.seekTo(offset);
678712
679 return takePhdr(&it.file_reader.interface, it.elf_header);713 return takeProgramHeader(&it.file_reader.interface, it.is_64, it.endian);
680 }714 }
681};715};
682716
683pub const ProgramHeaderBufferIterator = struct {717pub const ProgramHeaderBufferIterator = struct {
684 elf_header: Header,718 is_64: bool,
719 endian: Endian,
720 phnum: u16,
721 phoff: u64,
722
685 buf: []const u8,723 buf: []const u8,
686 index: usize = 0,724 index: usize = 0,
687725
688 pub fn next(it: *ProgramHeaderBufferIterator) !?Elf64_Phdr {726 pub fn next(it: *ProgramHeaderBufferIterator) !?Elf64_Phdr {
689 if (it.index >= it.elf_header.phnum) return null;727 if (it.index >= it.phnum) return null;
690 defer it.index += 1;728 defer it.index += 1;
691729
692 const size: u64 = if (it.elf_header.is_64) @sizeOf(Elf64_Phdr) else @sizeOf(Elf32_Phdr);730 const size: u64 = if (it.is_64) @sizeOf(Elf64_Phdr) else @sizeOf(Elf32_Phdr);
693 const offset = it.elf_header.phoff + size * it.index;731 const offset = it.phoff + size * it.index;
694 var reader = std.Io.Reader.fixed(it.buf[offset..]);732 var reader = Io.Reader.fixed(it.buf[offset..]);
695733
696 return takePhdr(&reader, it.elf_header);734 return takeProgramHeader(&reader, it.is_64, it.endian);
697 }735 }
698};736};
699737
700fn takePhdr(reader: *std.Io.Reader, elf_header: Header) !?Elf64_Phdr {738pub fn takeProgramHeader(reader: *Io.Reader, is_64: bool, endian: Endian) !Elf64_Phdr {
701 if (elf_header.is_64) {739 if (is_64) {
702 const phdr = try reader.takeStruct(Elf64_Phdr, elf_header.endian);740 const phdr = try reader.takeStruct(Elf64_Phdr, endian);
703 return phdr;741 return phdr;
704 }742 }
705743
706 const phdr = try reader.takeStruct(Elf32_Phdr, elf_header.endian);744 const phdr = try reader.takeStruct(Elf32_Phdr, endian);
707 return .{745 return .{
708 .p_type = phdr.p_type,746 .p_type = phdr.p_type,
709 .p_offset = phdr.p_offset,747 .p_offset = phdr.p_offset,
...@@ -717,47 +755,55 @@ fn takePhdr(reader: *std.Io.Reader, elf_header: Header) !?Elf64_Phdr {...@@ -717,47 +755,55 @@ fn takePhdr(reader: *std.Io.Reader, elf_header: Header) !?Elf64_Phdr {
717}755}
718756
719pub const SectionHeaderIterator = struct {757pub const SectionHeaderIterator = struct {
720 elf_header: Header,758 is_64: bool,
721 file_reader: *std.fs.File.Reader,759 endian: Endian,
760 shnum: u16,
761 shoff: u64,
762
763 file_reader: *Io.File.Reader,
722 index: usize = 0,764 index: usize = 0,
723765
724 pub fn next(it: *SectionHeaderIterator) !?Elf64_Shdr {766 pub fn next(it: *SectionHeaderIterator) !?Elf64_Shdr {
725 if (it.index >= it.elf_header.shnum) return null;767 if (it.index >= it.shnum) return null;
726 defer it.index += 1;768 defer it.index += 1;
727769
728 const size: u64 = if (it.elf_header.is_64) @sizeOf(Elf64_Shdr) else @sizeOf(Elf32_Shdr);770 const size: u64 = if (it.is_64) @sizeOf(Elf64_Shdr) else @sizeOf(Elf32_Shdr);
729 const offset = it.elf_header.shoff + size * it.index;771 const offset = it.shoff + size * it.index;
730 try it.file_reader.seekTo(offset);772 try it.file_reader.seekTo(offset);
731773
732 return takeShdr(&it.file_reader.interface, it.elf_header);774 return takeSectionHeader(&it.file_reader.interface, it.is_64, it.endian);
733 }775 }
734};776};
735777
736pub const SectionHeaderBufferIterator = struct {778pub const SectionHeaderBufferIterator = struct {
737 elf_header: Header,779 is_64: bool,
780 endian: Endian,
781 shnum: u16,
782 shoff: u64,
783
738 buf: []const u8,784 buf: []const u8,
739 index: usize = 0,785 index: usize = 0,
740786
741 pub fn next(it: *SectionHeaderBufferIterator) !?Elf64_Shdr {787 pub fn next(it: *SectionHeaderBufferIterator) !?Elf64_Shdr {
742 if (it.index >= it.elf_header.shnum) return null;788 if (it.index >= it.shnum) return null;
743 defer it.index += 1;789 defer it.index += 1;
744790
745 const size: u64 = if (it.elf_header.is_64) @sizeOf(Elf64_Shdr) else @sizeOf(Elf32_Shdr);791 const size: u64 = if (it.is_64) @sizeOf(Elf64_Shdr) else @sizeOf(Elf32_Shdr);
746 const offset = it.elf_header.shoff + size * it.index;792 const offset = it.shoff + size * it.index;
747 if (offset > it.buf.len) return error.EndOfStream;793 if (offset > it.buf.len) return error.EndOfStream;
748 var reader = std.Io.Reader.fixed(it.buf[@intCast(offset)..]);794 var reader = Io.Reader.fixed(it.buf[@intCast(offset)..]);
749795
750 return takeShdr(&reader, it.elf_header);796 return takeSectionHeader(&reader, it.is_64, it.endian);
751 }797 }
752};798};
753799
754fn takeShdr(reader: *std.Io.Reader, elf_header: Header) !?Elf64_Shdr {800pub fn takeSectionHeader(reader: *Io.Reader, is_64: bool, endian: Endian) !Elf64_Shdr {
755 if (elf_header.is_64) {801 if (is_64) {
756 const shdr = try reader.takeStruct(Elf64_Shdr, elf_header.endian);802 const shdr = try reader.takeStruct(Elf64_Shdr, endian);
757 return shdr;803 return shdr;
758 }804 }
759805
760 const shdr = try reader.takeStruct(Elf32_Shdr, elf_header.endian);806 const shdr = try reader.takeStruct(Elf32_Shdr, endian);
761 return .{807 return .{
762 .sh_name = shdr.sh_name,808 .sh_name = shdr.sh_name,
763 .sh_type = shdr.sh_type,809 .sh_type = shdr.sh_type,
...@@ -772,6 +818,36 @@ fn takeShdr(reader: *std.Io.Reader, elf_header: Header) !?Elf64_Shdr {...@@ -772,6 +818,36 @@ fn takeShdr(reader: *std.Io.Reader, elf_header: Header) !?Elf64_Shdr {
772 };818 };
773}819}
774820
821pub const DynamicSectionIterator = struct {
822 is_64: bool,
823 endian: Endian,
824 offset: u64,
825 end_offset: u64,
826
827 file_reader: *Io.File.Reader,
828
829 pub fn next(it: *SectionHeaderIterator) !?Elf64_Dyn {
830 if (it.offset >= it.end_offset) return null;
831 const size: u64 = if (it.is_64) @sizeOf(Elf64_Dyn) else @sizeOf(Elf32_Dyn);
832 defer it.offset += size;
833 try it.file_reader.seekTo(it.offset);
834 return takeDynamicSection(&it.file_reader.interface, it.is_64, it.endian);
835 }
836};
837
838pub fn takeDynamicSection(reader: *Io.Reader, is_64: bool, endian: Endian) !Elf64_Dyn {
839 if (is_64) {
840 const dyn = try reader.takeStruct(Elf64_Dyn, endian);
841 return dyn;
842 }
843
844 const dyn = try reader.takeStruct(Elf32_Dyn, endian);
845 return .{
846 .d_tag = dyn.d_tag,
847 .d_val = dyn.d_val,
848 };
849}
850
775pub const EI = struct {851pub const EI = struct {
776 pub const CLASS = 4;852 pub const CLASS = 4;
777 pub const DATA = 5;853 pub const DATA = 5;
lib/std/zig.zig+6-5
...@@ -6,6 +6,7 @@ const std = @import("std.zig");...@@ -6,6 +6,7 @@ const std = @import("std.zig");
6const tokenizer = @import("zig/tokenizer.zig");6const tokenizer = @import("zig/tokenizer.zig");
7const assert = std.debug.assert;7const assert = std.debug.assert;
8const Allocator = std.mem.Allocator;8const Allocator = std.mem.Allocator;
9const Io = std.Io;
9const Writer = std.Io.Writer;10const Writer = std.Io.Writer;
1011
11pub const ErrorBundle = @import("zig/ErrorBundle.zig");12pub const ErrorBundle = @import("zig/ErrorBundle.zig");
...@@ -52,9 +53,9 @@ pub const Color = enum {...@@ -52,9 +53,9 @@ pub const Color = enum {
52 /// Assume stderr is a terminal.53 /// Assume stderr is a terminal.
53 on,54 on,
5455
55 pub fn get_tty_conf(color: Color) std.Io.tty.Config {56 pub fn get_tty_conf(color: Color) Io.tty.Config {
56 return switch (color) {57 return switch (color) {
57 .auto => std.Io.tty.detectConfig(std.fs.File.stderr()),58 .auto => Io.tty.detectConfig(std.fs.File.stderr()),
58 .on => .escape_codes,59 .on => .escape_codes,
59 .off => .no_color,60 .off => .no_color,
60 };61 };
...@@ -323,7 +324,7 @@ pub const BuildId = union(enum) {...@@ -323,7 +324,7 @@ pub const BuildId = union(enum) {
323 try std.testing.expectError(error.InvalidBuildIdStyle, parse("yaddaxxx"));324 try std.testing.expectError(error.InvalidBuildIdStyle, parse("yaddaxxx"));
324 }325 }
325326
326 pub fn format(id: BuildId, writer: *std.Io.Writer) std.Io.Writer.Error!void {327 pub fn format(id: BuildId, writer: *Writer) Writer.Error!void {
327 switch (id) {328 switch (id) {
328 .none, .fast, .uuid, .sha1, .md5 => {329 .none, .fast, .uuid, .sha1, .md5 => {
329 try writer.writeAll(@tagName(id));330 try writer.writeAll(@tagName(id));
...@@ -620,8 +621,8 @@ pub fn putAstErrorsIntoBundle(...@@ -620,8 +621,8 @@ pub fn putAstErrorsIntoBundle(
620 try wip_errors.addZirErrorMessages(zir, tree, tree.source, path);621 try wip_errors.addZirErrorMessages(zir, tree, tree.source, path);
621}622}
622623
623pub fn resolveTargetQueryOrFatal(target_query: std.Target.Query) std.Target {624pub fn resolveTargetQueryOrFatal(io: Io, target_query: std.Target.Query) std.Target {
624 return std.zig.system.resolveTargetQuery(target_query) catch |err|625 return std.zig.system.resolveTargetQuery(io, target_query) catch |err|
625 std.process.fatal("unable to resolve target: {s}", .{@errorName(err)});626 std.process.fatal("unable to resolve target: {s}", .{@errorName(err)});
626}627}
627628
lib/std/zig/system.zig+221-411
...@@ -1,3 +1,14 @@...@@ -1,3 +1,14 @@
1const builtin = @import("builtin");
2const std = @import("../std.zig");
3const mem = std.mem;
4const elf = std.elf;
5const fs = std.fs;
6const assert = std.debug.assert;
7const Target = std.Target;
8const native_endian = builtin.cpu.arch.endian();
9const posix = std.posix;
10const Io = std.Io;
11
1pub const NativePaths = @import("system/NativePaths.zig");12pub const NativePaths = @import("system/NativePaths.zig");
213
3pub const windows = @import("system/windows.zig");14pub const windows = @import("system/windows.zig");
...@@ -199,14 +210,14 @@ pub const DetectError = error{...@@ -199,14 +210,14 @@ pub const DetectError = error{
199 OSVersionDetectionFail,210 OSVersionDetectionFail,
200 Unexpected,211 Unexpected,
201 ProcessNotFound,212 ProcessNotFound,
202};213} || Io.Cancelable;
203214
204/// Given a `Target.Query`, which specifies in detail which parts of the215/// Given a `Target.Query`, which specifies in detail which parts of the
205/// target should be detected natively, which should be standard or default,216/// target should be detected natively, which should be standard or default,
206/// and which are provided explicitly, this function resolves the native217/// and which are provided explicitly, this function resolves the native
207/// components by detecting the native system, and then resolves218/// components by detecting the native system, and then resolves
208/// standard/default parts relative to that.219/// standard/default parts relative to that.
209pub fn resolveTargetQuery(query: Target.Query) DetectError!Target {220pub fn resolveTargetQuery(io: Io, query: Target.Query) DetectError!Target {
210 // Until https://github.com/ziglang/zig/issues/4592 is implemented (support detecting the221 // Until https://github.com/ziglang/zig/issues/4592 is implemented (support detecting the
211 // native CPU architecture as being different than the current target), we use this:222 // native CPU architecture as being different than the current target), we use this:
212 const query_cpu_arch = query.cpu_arch orelse builtin.cpu.arch;223 const query_cpu_arch = query.cpu_arch orelse builtin.cpu.arch;
...@@ -411,7 +422,33 @@ pub fn resolveTargetQuery(query: Target.Query) DetectError!Target {...@@ -411,7 +422,33 @@ pub fn resolveTargetQuery(query: Target.Query) DetectError!Target {
411 query.cpu_features_sub,422 query.cpu_features_sub,
412 );423 );
413424
414 var result = try detectAbiAndDynamicLinker(cpu, os, query);425 var result = detectAbiAndDynamicLinker(io, cpu, os, query) catch |err| switch (err) {
426 error.Canceled => |e| return e,
427 error.Unexpected => |e| return e,
428 error.WouldBlock => return error.Unexpected,
429 error.BrokenPipe => return error.Unexpected,
430 error.ConnectionResetByPeer => return error.Unexpected,
431 error.ConnectionTimedOut => return error.Unexpected,
432 error.NotOpenForReading => return error.Unexpected,
433 error.SocketUnconnected => return error.Unexpected,
434
435 error.AccessDenied,
436 error.ProcessNotFound,
437 error.SymLinkLoop,
438 error.ProcessFdQuotaExceeded,
439 error.SystemFdQuotaExceeded,
440 error.SystemResources,
441 error.IsDir,
442 error.DeviceBusy,
443 error.InputOutput,
444 error.LockViolation,
445
446 error.UnableToOpenElfFile,
447 error.UnhelpfulFile,
448 error.InvalidElfFile,
449 error.RelativeShebang,
450 => return defaultAbiAndDynamicLinker(cpu, os, query),
451 };
415452
416 // These CPU feature hacks have to come after ABI detection.453 // These CPU feature hacks have to come after ABI detection.
417 {454 {
...@@ -505,54 +542,16 @@ fn detectNativeCpuAndFeatures(cpu_arch: Target.Cpu.Arch, os: Target.Os, query: T...@@ -505,54 +542,16 @@ fn detectNativeCpuAndFeatures(cpu_arch: Target.Cpu.Arch, os: Target.Os, query: T
505 return null;542 return null;
506}543}
507544
508pub const AbiAndDynamicLinkerFromFileError = error{545pub const AbiAndDynamicLinkerFromFileError = error{};
509 FileSystem,
510 SystemResources,
511 SymLinkLoop,
512 ProcessFdQuotaExceeded,
513 SystemFdQuotaExceeded,
514 UnableToReadElfFile,
515 InvalidElfClass,
516 InvalidElfVersion,
517 InvalidElfEndian,
518 InvalidElfFile,
519 InvalidElfMagic,
520 Unexpected,
521 UnexpectedEndOfFile,
522 NameTooLong,
523 ProcessNotFound,
524 StaticElfFile,
525};
526546
527pub fn abiAndDynamicLinkerFromFile(547pub fn abiAndDynamicLinkerFromFile(
528 file: fs.File,548 file_reader: *Io.File.Reader,
549 header: *const elf.Header,
529 cpu: Target.Cpu,550 cpu: Target.Cpu,
530 os: Target.Os,551 os: Target.Os,
531 ld_info_list: []const LdInfo,552 ld_info_list: []const LdInfo,
532 query: Target.Query,553 query: Target.Query,
533) AbiAndDynamicLinkerFromFileError!Target {554) AbiAndDynamicLinkerFromFileError!Target {
534 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 align(@alignOf(elf.Elf64_Ehdr)) = undefined;
535 _ = try preadAtLeast(file, &hdr_buf, 0, hdr_buf.len);
536 const hdr32: *elf.Elf32_Ehdr = @ptrCast(&hdr_buf);
537 const hdr64: *elf.Elf64_Ehdr = @ptrCast(&hdr_buf);
538 if (!mem.eql(u8, hdr32.e_ident[0..4], elf.MAGIC)) return error.InvalidElfMagic;
539 const elf_endian: std.builtin.Endian = switch (hdr32.e_ident[elf.EI.DATA]) {
540 elf.ELFDATA2LSB => .little,
541 elf.ELFDATA2MSB => .big,
542 else => return error.InvalidElfEndian,
543 };
544 const need_bswap = elf_endian != native_endian;
545 if (hdr32.e_ident[elf.EI.VERSION] != 1) return error.InvalidElfVersion;
546
547 const is_64 = switch (hdr32.e_ident[elf.EI.CLASS]) {
548 elf.ELFCLASS32 => false,
549 elf.ELFCLASS64 => true,
550 else => return error.InvalidElfClass,
551 };
552 var phoff = elfInt(is_64, need_bswap, hdr32.e_phoff, hdr64.e_phoff);
553 const phentsize = elfInt(is_64, need_bswap, hdr32.e_phentsize, hdr64.e_phentsize);
554 const phnum = elfInt(is_64, need_bswap, hdr32.e_phnum, hdr64.e_phnum);
555
556 var result: Target = .{555 var result: Target = .{
557 .cpu = cpu,556 .cpu = cpu,
558 .os = os,557 .os = os,
...@@ -563,167 +562,87 @@ pub fn abiAndDynamicLinkerFromFile(...@@ -563,167 +562,87 @@ pub fn abiAndDynamicLinkerFromFile(
563 var rpath_offset: ?u64 = null; // Found inside PT_DYNAMIC562 var rpath_offset: ?u64 = null; // Found inside PT_DYNAMIC
564 const look_for_ld = query.dynamic_linker.get() == null;563 const look_for_ld = query.dynamic_linker.get() == null;
565564
566 var ph_buf: [16 * @sizeOf(elf.Elf64_Phdr)]u8 align(@alignOf(elf.Elf64_Phdr)) = undefined;
567 if (phentsize > @sizeOf(elf.Elf64_Phdr)) return error.InvalidElfFile;
568
569 var ph_i: u16 = 0;
570 var got_dyn_section: bool = false;565 var got_dyn_section: bool = false;
571566 {
572 while (ph_i < phnum) {567 var it = header.iterateProgramHeaders(file_reader);
573 // Reserve some bytes so that we can deref the 64-bit struct fields568 while (try it.next()) |phdr| switch (phdr.p_type) {
574 // even when the ELF file is 32-bits.569 elf.PT_INTERP => {
575 const ph_reserve: usize = @sizeOf(elf.Elf64_Phdr) - @sizeOf(elf.Elf32_Phdr);570 got_dyn_section = true;
576 const ph_read_byte_len = try preadAtLeast(file, ph_buf[0 .. ph_buf.len - ph_reserve], phoff, phentsize);571
577 var ph_buf_i: usize = 0;572 if (look_for_ld) {
578 while (ph_buf_i < ph_read_byte_len and ph_i < phnum) : ({573 const p_filesz = phdr.p_filesz;
579 ph_i += 1;574 if (p_filesz > result.dynamic_linker.buffer.len) return error.NameTooLong;
580 phoff += phentsize;575 const filesz: usize = @intCast(p_filesz);
581 ph_buf_i += phentsize;576 try file_reader.seekTo(phdr.p_offset);
582 }) {577 try file_reader.interface.readSliceAll(result.dynamic_linker.buffer[0..filesz]);
583 const ph32: *elf.Elf32_Phdr = @ptrCast(@alignCast(&ph_buf[ph_buf_i]));578 // PT_INTERP includes a null byte in filesz.
584 const ph64: *elf.Elf64_Phdr = @ptrCast(@alignCast(&ph_buf[ph_buf_i]));579 const len = filesz - 1;
585 const p_type = elfInt(is_64, need_bswap, ph32.p_type, ph64.p_type);580 // dynamic_linker.max_byte is "max", not "len".
586 switch (p_type) {581 // We know it will fit in u8 because we check against dynamic_linker.buffer.len above.
587 elf.PT_INTERP => {582 result.dynamic_linker.len = @intCast(len);
588 got_dyn_section = true;583
589584 // Use it to determine ABI.
590 if (look_for_ld) {585 const full_ld_path = result.dynamic_linker.buffer[0..len];
591 const p_offset = elfInt(is_64, need_bswap, ph32.p_offset, ph64.p_offset);586 for (ld_info_list) |ld_info| {
592 const p_filesz = elfInt(is_64, need_bswap, ph32.p_filesz, ph64.p_filesz);587 const standard_ld_basename = fs.path.basename(ld_info.ld.get().?);
593 if (p_filesz > result.dynamic_linker.buffer.len) return error.NameTooLong;588 if (std.mem.endsWith(u8, full_ld_path, standard_ld_basename)) {
594 const filesz: usize = @intCast(p_filesz);589 result.abi = ld_info.abi;
595 _ = try preadAtLeast(file, result.dynamic_linker.buffer[0..filesz], p_offset, filesz);590 break;
596 // PT_INTERP includes a null byte in filesz.
597 const len = filesz - 1;
598 // dynamic_linker.max_byte is "max", not "len".
599 // We know it will fit in u8 because we check against dynamic_linker.buffer.len above.
600 result.dynamic_linker.len = @intCast(len);
601
602 // Use it to determine ABI.
603 const full_ld_path = result.dynamic_linker.buffer[0..len];
604 for (ld_info_list) |ld_info| {
605 const standard_ld_basename = fs.path.basename(ld_info.ld.get().?);
606 if (std.mem.endsWith(u8, full_ld_path, standard_ld_basename)) {
607 result.abi = ld_info.abi;
608 break;
609 }
610 }591 }
611 }592 }
612 },593 }
613 // We only need this for detecting glibc version.594 },
614 elf.PT_DYNAMIC => {595 // We only need this for detecting glibc version.
615 got_dyn_section = true;596 elf.PT_DYNAMIC => {
616597 got_dyn_section = true;
617 if (builtin.target.os.tag == .linux and result.isGnuLibC() and598
618 query.glibc_version == null)599 if (builtin.target.os.tag == .linux and result.isGnuLibC() and query.glibc_version == null) {
619 {600 var dyn_it = header.iterateDynamicSection(file_reader, phdr.p_offset, phdr.p_filesz);
620 var dyn_off = elfInt(is_64, need_bswap, ph32.p_offset, ph64.p_offset);601 while (try dyn_it.next()) |dyn| {
621 const p_filesz = elfInt(is_64, need_bswap, ph32.p_filesz, ph64.p_filesz);602 if (dyn.d_tag == elf.DT_RUNPATH) {
622 const dyn_size: usize = if (is_64) @sizeOf(elf.Elf64_Dyn) else @sizeOf(elf.Elf32_Dyn);603 rpath_offset = dyn.d_val;
623 const dyn_num = p_filesz / dyn_size;604 break;
624 var dyn_buf: [16 * @sizeOf(elf.Elf64_Dyn)]u8 align(@alignOf(elf.Elf64_Dyn)) = undefined;
625 var dyn_i: usize = 0;
626 dyn: while (dyn_i < dyn_num) {
627 // Reserve some bytes so that we can deref the 64-bit struct fields
628 // even when the ELF file is 32-bits.
629 const dyn_reserve: usize = @sizeOf(elf.Elf64_Dyn) - @sizeOf(elf.Elf32_Dyn);
630 const dyn_read_byte_len = try preadAtLeast(
631 file,
632 dyn_buf[0 .. dyn_buf.len - dyn_reserve],
633 dyn_off,
634 dyn_size,
635 );
636 var dyn_buf_i: usize = 0;
637 while (dyn_buf_i < dyn_read_byte_len and dyn_i < dyn_num) : ({
638 dyn_i += 1;
639 dyn_off += dyn_size;
640 dyn_buf_i += dyn_size;
641 }) {
642 const dyn32: *elf.Elf32_Dyn = @ptrCast(@alignCast(&dyn_buf[dyn_buf_i]));
643 const dyn64: *elf.Elf64_Dyn = @ptrCast(@alignCast(&dyn_buf[dyn_buf_i]));
644 const tag = elfInt(is_64, need_bswap, dyn32.d_tag, dyn64.d_tag);
645 const val = elfInt(is_64, need_bswap, dyn32.d_val, dyn64.d_val);
646 if (tag == elf.DT_RUNPATH) {
647 rpath_offset = val;
648 break :dyn;
649 }
650 }
651 }605 }
652 }606 }
653 },607 }
654 else => continue,608 },
655 }609 else => continue,
656 }610 };
657 }611 }
658612
659 if (!got_dyn_section) {613 if (!got_dyn_section) {
660 return error.StaticElfFile;614 return error.StaticElfFile;
661 }615 }
662616
663 if (builtin.target.os.tag == .linux and result.isGnuLibC() and617 if (builtin.target.os.tag == .linux and result.isGnuLibC() and query.glibc_version == null) {
664 query.glibc_version == null)618 const str_section_off = header.shoff + @as(u64, header.shentsize) * @as(u64, header.shstrndx);
665 {619 try file_reader.seekTo(str_section_off);
666 const shstrndx = elfInt(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx);620 const shstr = try elf.takeSectionHeader(&file_reader.interface, header.is_64, header.endian);
667621 var strtab_buf: [4096]u8 = undefined;
668 var shoff = elfInt(is_64, need_bswap, hdr32.e_shoff, hdr64.e_shoff);622 const shstrtab = strtab_buf[0..@min(shstr.sh_size, strtab_buf.len)];
669 const shentsize = elfInt(is_64, need_bswap, hdr32.e_shentsize, hdr64.e_shentsize);623 try file_reader.seekTo(shstr.sh_offset);
670 const str_section_off = shoff + @as(u64, shentsize) * @as(u64, shstrndx);624 try file_reader.interface.readSliceAll(shstrtab);
671625 const dynstr: ?struct { offset: u64, size: u64 } = find_dyn_str: {
672 var sh_buf: [16 * @sizeOf(elf.Elf64_Shdr)]u8 align(@alignOf(elf.Elf64_Shdr)) = undefined;626 var it = header.iterateSectionHeaders(&file_reader.interface);
673 if (sh_buf.len < shentsize) return error.InvalidElfFile;627 while (it.next()) |shdr| {
674628 const end = mem.findScalarPos(u8, shstrtab, shdr.sh_name, 0) orelse continue;
675 _ = try preadAtLeast(file, &sh_buf, str_section_off, shentsize);629 const sh_name = shstrtab[shdr.sh_name..end :0];
676 const shstr32: *elf.Elf32_Shdr = @ptrCast(@alignCast(&sh_buf));630 if (mem.eql(u8, sh_name, ".dynstr")) break :find_dyn_str .{
677 const shstr64: *elf.Elf64_Shdr = @ptrCast(@alignCast(&sh_buf));631 .offset = shdr.sh_offset,
678 const shstrtab_off = elfInt(is_64, need_bswap, shstr32.sh_offset, shstr64.sh_offset);632 .size = shdr.sh_size,
679 const shstrtab_size = elfInt(is_64, need_bswap, shstr32.sh_size, shstr64.sh_size);633 };
680 var strtab_buf: [4096:0]u8 = undefined;634 } else break :find_dyn_str null;
681 const shstrtab_len = @min(shstrtab_size, strtab_buf.len);635 };
682 const shstrtab_read_len = try preadAtLeast(file, &strtab_buf, shstrtab_off, shstrtab_len);
683 const shstrtab = strtab_buf[0..shstrtab_read_len];
684
685 const shnum = elfInt(is_64, need_bswap, hdr32.e_shnum, hdr64.e_shnum);
686 var sh_i: u16 = 0;
687 const dynstr: ?struct { offset: u64, size: u64 } = find_dyn_str: while (sh_i < shnum) {
688 // Reserve some bytes so that we can deref the 64-bit struct fields
689 // even when the ELF file is 32-bits.
690 const sh_reserve: usize = @sizeOf(elf.Elf64_Shdr) - @sizeOf(elf.Elf32_Shdr);
691 const sh_read_byte_len = try preadAtLeast(
692 file,
693 sh_buf[0 .. sh_buf.len - sh_reserve],
694 shoff,
695 shentsize,
696 );
697 var sh_buf_i: usize = 0;
698 while (sh_buf_i < sh_read_byte_len and sh_i < shnum) : ({
699 sh_i += 1;
700 shoff += shentsize;
701 sh_buf_i += shentsize;
702 }) {
703 const sh32: *elf.Elf32_Shdr = @ptrCast(@alignCast(&sh_buf[sh_buf_i]));
704 const sh64: *elf.Elf64_Shdr = @ptrCast(@alignCast(&sh_buf[sh_buf_i]));
705 const sh_name_off = elfInt(is_64, need_bswap, sh32.sh_name, sh64.sh_name);
706 const sh_name = mem.sliceTo(shstrtab[sh_name_off..], 0);
707 if (mem.eql(u8, sh_name, ".dynstr")) {
708 break :find_dyn_str .{
709 .offset = elfInt(is_64, need_bswap, sh32.sh_offset, sh64.sh_offset),
710 .size = elfInt(is_64, need_bswap, sh32.sh_size, sh64.sh_size),
711 };
712 }
713 }
714 } else null;
715
716 if (dynstr) |ds| {636 if (dynstr) |ds| {
717 if (rpath_offset) |rpoff| {637 if (rpath_offset) |rpoff| {
718 if (rpoff > ds.size) return error.InvalidElfFile;638 if (rpoff > ds.size) return error.InvalidElfFile;
719 const rpoff_file = ds.offset + rpoff;639 const rpoff_file = ds.offset + rpoff;
720 const rp_max_size = ds.size - rpoff;640 const rp_max_size = ds.size - rpoff;
721641
722 const strtab_len = @min(rp_max_size, strtab_buf.len);642 try file_reader.seekTo(rpoff_file);
723 const strtab_read_len = try preadAtLeast(file, &strtab_buf, rpoff_file, strtab_len);643 const rpath_list = try file_reader.interface.takeSentinel(0);
724 const strtab = strtab_buf[0..strtab_read_len];644 if (rpath_list.len > rp_max_size) return error.StreamTooLong;
725645
726 const rpath_list = mem.sliceTo(strtab, 0);
727 var it = mem.tokenizeScalar(u8, rpath_list, ':');646 var it = mem.tokenizeScalar(u8, rpath_list, ':');
728 while (it.next()) |rpath| {647 while (it.next()) |rpath| {
729 if (glibcVerFromRPath(rpath)) |ver| {648 if (glibcVerFromRPath(rpath)) |ver| {
...@@ -845,7 +764,7 @@ test glibcVerFromLinkName {...@@ -845,7 +764,7 @@ test glibcVerFromLinkName {
845 try std.testing.expectError(error.InvalidGnuLibCVersion, glibcVerFromLinkName("ld-2.37.4.5.so", "ld-"));764 try std.testing.expectError(error.InvalidGnuLibCVersion, glibcVerFromLinkName("ld-2.37.4.5.so", "ld-"));
846}765}
847766
848fn glibcVerFromRPath(rpath: []const u8) !std.SemanticVersion {767fn glibcVerFromRPath(io: Io, rpath: []const u8) !std.SemanticVersion {
849 var dir = fs.cwd().openDir(rpath, .{}) catch |err| switch (err) {768 var dir = fs.cwd().openDir(rpath, .{}) catch |err| switch (err) {
850 error.NameTooLong => unreachable,769 error.NameTooLong => unreachable,
851 error.InvalidUtf8 => unreachable, // WASI only770 error.InvalidUtf8 => unreachable, // WASI only
...@@ -879,7 +798,7 @@ fn glibcVerFromRPath(rpath: []const u8) !std.SemanticVersion {...@@ -879,7 +798,7 @@ fn glibcVerFromRPath(rpath: []const u8) !std.SemanticVersion {
879 // .dynstr section, and finding the max version number of symbols798 // .dynstr section, and finding the max version number of symbols
880 // that start with "GLIBC_2.".799 // that start with "GLIBC_2.".
881 const glibc_so_basename = "libc.so.6";800 const glibc_so_basename = "libc.so.6";
882 var f = dir.openFile(glibc_so_basename, .{}) catch |err| switch (err) {801 var file = dir.openFile(glibc_so_basename, .{}) catch |err| switch (err) {
883 error.NameTooLong => unreachable,802 error.NameTooLong => unreachable,
884 error.InvalidUtf8 => unreachable, // WASI only803 error.InvalidUtf8 => unreachable, // WASI only
885 error.InvalidWtf8 => unreachable, // Windows only804 error.InvalidWtf8 => unreachable, // Windows only
...@@ -913,16 +832,20 @@ fn glibcVerFromRPath(rpath: []const u8) !std.SemanticVersion {...@@ -913,16 +832,20 @@ fn glibcVerFromRPath(rpath: []const u8) !std.SemanticVersion {
913 error.Unexpected,832 error.Unexpected,
914 => |e| return e,833 => |e| return e,
915 };834 };
916 defer f.close();835 defer file.close();
917836
918 return glibcVerFromSoFile(f) catch |err| switch (err) {837 // Empirically, glibc 2.34 libc.so .dynstr section is 32441 bytes on my system.
838 var buffer: [8000]u8 = undefined;
839 var file_reader: Io.File.Reader = .initAdapted(file, io, &buffer);
840
841 return glibcVerFromSoFile(&file_reader) catch |err| switch (err) {
919 error.InvalidElfMagic,842 error.InvalidElfMagic,
920 error.InvalidElfEndian,843 error.InvalidElfEndian,
921 error.InvalidElfClass,844 error.InvalidElfClass,
922 error.InvalidElfFile,845 error.InvalidElfFile,
923 error.InvalidElfVersion,846 error.InvalidElfVersion,
924 error.InvalidGnuLibCVersion,847 error.InvalidGnuLibCVersion,
925 error.UnexpectedEndOfFile,848 error.EndOfStream,
926 => return error.GLibCNotFound,849 => return error.GLibCNotFound,
927850
928 error.SystemResources,851 error.SystemResources,
...@@ -934,88 +857,34 @@ fn glibcVerFromRPath(rpath: []const u8) !std.SemanticVersion {...@@ -934,88 +857,34 @@ fn glibcVerFromRPath(rpath: []const u8) !std.SemanticVersion {
934 };857 };
935}858}
936859
937fn glibcVerFromSoFile(file: fs.File) !std.SemanticVersion {860fn glibcVerFromSoFile(file_reader: *Io.File.Reader) !std.SemanticVersion {
938 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 align(@alignOf(elf.Elf64_Ehdr)) = undefined;861 const header = try elf.Header.read(&file_reader.interface);
939 _ = try preadAtLeast(file, &hdr_buf, 0, hdr_buf.len);862 const str_section_off = header.shoff + @as(u64, header.shentsize) * @as(u64, header.shstrndx);
940 const hdr32: *elf.Elf32_Ehdr = @ptrCast(&hdr_buf);863 try file_reader.seekTo(str_section_off);
941 const hdr64: *elf.Elf64_Ehdr = @ptrCast(&hdr_buf);864 const shstr = try elf.takeSectionHeader(&file_reader.interface, header.is_64, header.endian);
942 if (!mem.eql(u8, hdr32.e_ident[0..4], elf.MAGIC)) return error.InvalidElfMagic;865 var strtab_buf: [4096]u8 = undefined;
943 const elf_endian: std.builtin.Endian = switch (hdr32.e_ident[elf.EI.DATA]) {866 const shstrtab = strtab_buf[0..@min(shstr.sh_size, strtab_buf.len)];
944 elf.ELFDATA2LSB => .little,867 try file_reader.seekTo(shstr.sh_offset);
945 elf.ELFDATA2MSB => .big,868 try file_reader.interface.readSliceAll(shstrtab);
946 else => return error.InvalidElfEndian,869 const dynstr: struct { offset: u64, size: u64 } = find_dyn_str: {
947 };870 var it = header.iterateSectionHeaders(&file_reader.interface);
948 const need_bswap = elf_endian != native_endian;871 while (it.next()) |shdr| {
949 if (hdr32.e_ident[elf.EI.VERSION] != 1) return error.InvalidElfVersion;872 const end = mem.findScalarPos(u8, shstrtab, shdr.sh_name, 0) orelse continue;
950873 const sh_name = shstrtab[shdr.sh_name..end :0];
951 const is_64 = switch (hdr32.e_ident[elf.EI.CLASS]) {874 if (mem.eql(u8, sh_name, ".dynstr")) break :find_dyn_str .{
952 elf.ELFCLASS32 => false,875 .offset = shdr.sh_offset,
953 elf.ELFCLASS64 => true,876 .size = shdr.sh_size,
954 else => return error.InvalidElfClass,877 };
878 } else return error.InvalidGnuLibCVersion;
955 };879 };
956 const shstrndx = elfInt(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx);
957 var shoff = elfInt(is_64, need_bswap, hdr32.e_shoff, hdr64.e_shoff);
958 const shentsize = elfInt(is_64, need_bswap, hdr32.e_shentsize, hdr64.e_shentsize);
959 const str_section_off = shoff + @as(u64, shentsize) * @as(u64, shstrndx);
960 var sh_buf: [16 * @sizeOf(elf.Elf64_Shdr)]u8 align(@alignOf(elf.Elf64_Shdr)) = undefined;
961 if (sh_buf.len < shentsize) return error.InvalidElfFile;
962
963 _ = try preadAtLeast(file, &sh_buf, str_section_off, shentsize);
964 const shstr32: *elf.Elf32_Shdr = @ptrCast(@alignCast(&sh_buf));
965 const shstr64: *elf.Elf64_Shdr = @ptrCast(@alignCast(&sh_buf));
966 const shstrtab_off = elfInt(is_64, need_bswap, shstr32.sh_offset, shstr64.sh_offset);
967 const shstrtab_size = elfInt(is_64, need_bswap, shstr32.sh_size, shstr64.sh_size);
968 var strtab_buf: [4096:0]u8 = undefined;
969 const shstrtab_len = @min(shstrtab_size, strtab_buf.len);
970 const shstrtab_read_len = try preadAtLeast(file, &strtab_buf, shstrtab_off, shstrtab_len);
971 const shstrtab = strtab_buf[0..shstrtab_read_len];
972 const shnum = elfInt(is_64, need_bswap, hdr32.e_shnum, hdr64.e_shnum);
973 var sh_i: u16 = 0;
974 const dynstr: struct { offset: u64, size: u64 } = find_dyn_str: while (sh_i < shnum) {
975 // Reserve some bytes so that we can deref the 64-bit struct fields
976 // even when the ELF file is 32-bits.
977 const sh_reserve: usize = @sizeOf(elf.Elf64_Shdr) - @sizeOf(elf.Elf32_Shdr);
978 const sh_read_byte_len = try preadAtLeast(
979 file,
980 sh_buf[0 .. sh_buf.len - sh_reserve],
981 shoff,
982 shentsize,
983 );
984 var sh_buf_i: usize = 0;
985 while (sh_buf_i < sh_read_byte_len and sh_i < shnum) : ({
986 sh_i += 1;
987 shoff += shentsize;
988 sh_buf_i += shentsize;
989 }) {
990 const sh32: *elf.Elf32_Shdr = @ptrCast(@alignCast(&sh_buf[sh_buf_i]));
991 const sh64: *elf.Elf64_Shdr = @ptrCast(@alignCast(&sh_buf[sh_buf_i]));
992 const sh_name_off = elfInt(is_64, need_bswap, sh32.sh_name, sh64.sh_name);
993 const sh_name = mem.sliceTo(shstrtab[sh_name_off..], 0);
994 if (mem.eql(u8, sh_name, ".dynstr")) {
995 break :find_dyn_str .{
996 .offset = elfInt(is_64, need_bswap, sh32.sh_offset, sh64.sh_offset),
997 .size = elfInt(is_64, need_bswap, sh32.sh_size, sh64.sh_size),
998 };
999 }
1000 }
1001 } else return error.InvalidGnuLibCVersion;
1002880
1003 // Here we loop over all the strings in the dynstr string table, assuming that any881 // Here we loop over all the strings in the dynstr string table, assuming that any
1004 // strings that start with "GLIBC_2." indicate the existence of such a glibc version,882 // strings that start with "GLIBC_2." indicate the existence of such a glibc version,
1005 // and furthermore, that the system-installed glibc is at minimum that version.883 // and furthermore, that the system-installed glibc is at minimum that version.
1006
1007 // Empirically, glibc 2.34 libc.so .dynstr section is 32441 bytes on my system.
1008 // Here I use double this value plus some headroom. This makes it only need
1009 // a single read syscall here.
1010 var buf: [80000]u8 = undefined;
1011 if (buf.len < dynstr.size) return error.InvalidGnuLibCVersion;
1012
1013 const dynstr_size: usize = @intCast(dynstr.size);
1014 const dynstr_bytes = buf[0..dynstr_size];
1015 _ = try preadAtLeast(file, dynstr_bytes, dynstr.offset, dynstr_bytes.len);
1016 var it = mem.splitScalar(u8, dynstr_bytes, 0);
1017 var max_ver: std.SemanticVersion = .{ .major = 2, .minor = 2, .patch = 5 };884 var max_ver: std.SemanticVersion = .{ .major = 2, .minor = 2, .patch = 5 };
1018 while (it.next()) |s| {885
886 try file_reader.seekTo(dynstr.offset);
887 while (file_reader.interface.takeSentinel(0)) |s| {
1019 if (mem.startsWith(u8, s, "GLIBC_2.")) {888 if (mem.startsWith(u8, s, "GLIBC_2.")) {
1020 const chopped = s["GLIBC_".len..];889 const chopped = s["GLIBC_".len..];
1021 const ver = Target.Query.parseVersion(chopped) catch |err| switch (err) {890 const ver = Target.Query.parseVersion(chopped) catch |err| switch (err) {
...@@ -1028,6 +897,7 @@ fn glibcVerFromSoFile(file: fs.File) !std.SemanticVersion {...@@ -1028,6 +897,7 @@ fn glibcVerFromSoFile(file: fs.File) !std.SemanticVersion {
1028 }897 }
1029 }898 }
1030 }899 }
900
1031 return max_ver;901 return max_ver;
1032}902}
1033903
...@@ -1044,11 +914,7 @@ fn glibcVerFromSoFile(file: fs.File) !std.SemanticVersion {...@@ -1044,11 +914,7 @@ fn glibcVerFromSoFile(file: fs.File) !std.SemanticVersion {
1044/// answer to these questions, or if there is a shebang line, then it chases the referenced914/// answer to these questions, or if there is a shebang line, then it chases the referenced
1045/// file recursively. If that does not provide the answer, then the function falls back to915/// file recursively. If that does not provide the answer, then the function falls back to
1046/// defaults.916/// defaults.
1047fn detectAbiAndDynamicLinker(917fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Target.Query) !Target {
1048 cpu: Target.Cpu,
1049 os: Target.Os,
1050 query: Target.Query,
1051) DetectError!Target {
1052 const native_target_has_ld = comptime Target.DynamicLinker.kind(builtin.os.tag) != .none;918 const native_target_has_ld = comptime Target.DynamicLinker.kind(builtin.os.tag) != .none;
1053 const is_linux = builtin.target.os.tag == .linux;919 const is_linux = builtin.target.os.tag == .linux;
1054 const is_illumos = builtin.target.os.tag == .illumos;920 const is_illumos = builtin.target.os.tag == .illumos;
...@@ -1111,49 +977,52 @@ fn detectAbiAndDynamicLinker(...@@ -1111,49 +977,52 @@ fn detectAbiAndDynamicLinker(
1111977
1112 const ld_info_list = ld_info_list_buffer[0..ld_info_list_len];978 const ld_info_list = ld_info_list_buffer[0..ld_info_list_len];
1113979
980 var file_reader: Io.File.Reader = undefined;
981 // According to `man 2 execve`:
982 //
983 // The kernel imposes a maximum length on the text
984 // that follows the "#!" characters at the start of a script;
985 // characters beyond the limit are ignored.
986 // Before Linux 5.1, the limit is 127 characters.
987 // Since Linux 5.1, the limit is 255 characters.
988 //
989 // Tests show that bash and zsh consider 255 as total limit,
990 // *including* "#!" characters and ignoring newline.
991 // For safety, we set max length as 255 + \n (1).
992 const max_shebang_line_size = 256;
993 var file_reader_buffer: [4096]u8 = undefined;
994 comptime assert(file_reader_buffer.len >= max_shebang_line_size);
995
1114 // Best case scenario: the executable is dynamically linked, and we can iterate996 // Best case scenario: the executable is dynamically linked, and we can iterate
1115 // over our own shared objects and find a dynamic linker.997 // over our own shared objects and find a dynamic linker.
1116 const elf_file = elf_file: {998 const header = elf_file: {
1117 // This block looks for a shebang line in /usr/bin/env,999 // This block looks for a shebang line in "/usr/bin/env". If it finds
1118 // if it finds one, then instead of using /usr/bin/env as the ELF file to examine, it uses the file it references instead,1000 // one, then instead of using "/usr/bin/env" as the ELF file to examine,
1119 // doing the same logic recursively in case it finds another shebang line.1001 // it uses the file it references instead, doing the same logic
1002 // recursively in case it finds another shebang line.
11201003
1121 var file_name: []const u8 = switch (os.tag) {1004 var file_name: []const u8 = switch (os.tag) {
1122 // Since /usr/bin/env is hard-coded into the shebang line of many portable scripts, it's a1005 // Since /usr/bin/env is hard-coded into the shebang line of many
1123 // reasonably reliable path to start with.1006 // portable scripts, it's a reasonably reliable path to start with.
1124 else => "/usr/bin/env",1007 else => "/usr/bin/env",
1125 // Haiku does not have a /usr root directory.1008 // Haiku does not have a /usr root directory.
1126 .haiku => "/bin/env",1009 .haiku => "/bin/env",
1127 };1010 };
11281011
1129 // According to `man 2 execve`:
1130 //
1131 // The kernel imposes a maximum length on the text
1132 // that follows the "#!" characters at the start of a script;
1133 // characters beyond the limit are ignored.
1134 // Before Linux 5.1, the limit is 127 characters.
1135 // Since Linux 5.1, the limit is 255 characters.
1136 //
1137 // Tests show that bash and zsh consider 255 as total limit,
1138 // *including* "#!" characters and ignoring newline.
1139 // For safety, we set max length as 255 + \n (1).
1140 var buffer: [255 + 1]u8 = undefined;
1141 while (true) {1012 while (true) {
1142 // Interpreter path can be relative on Linux, but
1143 // for simplicity we are asserting it is an absolute path.
1144 const file = fs.openFileAbsolute(file_name, .{}) catch |err| switch (err) {1013 const file = fs.openFileAbsolute(file_name, .{}) catch |err| switch (err) {
1145 error.NoSpaceLeft => unreachable,1014 error.NoSpaceLeft => return error.Unexpected,
1146 error.NameTooLong => unreachable,1015 error.NameTooLong => return error.Unexpected,
1147 error.PathAlreadyExists => unreachable,1016 error.PathAlreadyExists => return error.Unexpected,
1148 error.SharingViolation => unreachable,1017 error.SharingViolation => return error.Unexpected,
1149 error.InvalidUtf8 => unreachable, // WASI only1018 error.InvalidUtf8 => return error.Unexpected, // WASI only
1150 error.InvalidWtf8 => unreachable, // Windows only1019 error.InvalidWtf8 => return error.Unexpected, // Windows only
1151 error.BadPathName => unreachable,1020 error.BadPathName => return error.Unexpected,
1152 error.PipeBusy => unreachable,1021 error.PipeBusy => return error.Unexpected,
1153 error.FileLocksNotSupported => unreachable,1022 error.FileLocksNotSupported => return error.Unexpected,
1154 error.WouldBlock => unreachable,1023 error.WouldBlock => return error.Unexpected,
1155 error.FileBusy => unreachable, // opened without write permissions1024 error.FileBusy => return error.Unexpected, // opened without write permissions
1156 error.AntivirusInterference => unreachable, // Windows-only error1025 error.AntivirusInterference => return error.Unexpected, // Windows-only error
11571026
1158 error.IsDir,1027 error.IsDir,
1159 error.NotDir,1028 error.NotDir,
...@@ -1164,66 +1033,58 @@ fn detectAbiAndDynamicLinker(...@@ -1164,66 +1033,58 @@ fn detectAbiAndDynamicLinker(
1164 error.NetworkNotFound,1033 error.NetworkNotFound,
1165 error.FileTooBig,1034 error.FileTooBig,
1166 error.Unexpected,1035 error.Unexpected,
1167 => |e| {1036 => return error.UnableToOpenElfFile,
1168 std.log.warn("Encountered error: {s}, falling back to default ABI and dynamic linker.", .{@errorName(e)});
1169 return defaultAbiAndDynamicLinker(cpu, os, query);
1170 },
11711037
1172 else => |e| return e,1038 else => |e| return e,
1173 };1039 };
1174 var is_elf_file = false;1040 var is_elf_file = false;
1175 defer if (is_elf_file == false) file.close();1041 defer if (!is_elf_file) file.close();
11761042
1177 // Shortest working interpreter path is "#!/i" (4)1043 file_reader = .initAdapted(file, io, &file_reader_buffer);
1178 // (interpreter is "/i", assuming all paths are absolute, like in above comment).1044 file_name = undefined; // it aliases file_reader_buffer
1179 // ELF magic number length is also 4.1045
1180 //1046 const header = elf.Header.read(&file_reader.interface) catch |hdr_err| switch (hdr_err) {
1181 // If file is shorter than that, it is definitely not ELF file1047 error.EndOfStream,
1182 // nor file with "shebang" line.1048 error.InvalidElfMagic,
1183 const min_len: usize = 4;1049 => {
11841050 const shebang_line = file_reader.interface.takeSentinel('\n') catch |err| switch (err) {
1185 const len = preadAtLeast(file, &buffer, 0, min_len) catch |err| switch (err) {1051 error.ReadFailed => return file_reader.err.?,
1186 error.UnexpectedEndOfFile,1052 // It's neither an ELF file nor file with shebang line.
1187 error.UnableToReadElfFile,1053 error.EndOfStream, error.StreamTooLong => return error.UnhelpfulFile,
1188 error.ProcessNotFound,1054 };
1189 => return defaultAbiAndDynamicLinker(cpu, os, query),1055 if (!mem.startsWith(u8, shebang_line, "#!")) return error.UnhelpfulFile;
1056 // We detected shebang, now parse entire line.
1057
1058 // Trim leading "#!", spaces and tabs.
1059 const trimmed_line = mem.trimStart(u8, shebang_line[2..], &.{ ' ', '\t' });
1060
1061 // This line can have:
1062 // * Interpreter path only,
1063 // * Interpreter path and arguments, all separated by space, tab or NUL character.
1064 // And optionally newline at the end.
1065 const path_maybe_args = mem.trimEnd(u8, trimmed_line, "\n");
1066
1067 // Separate path and args.
1068 const path_end = mem.indexOfAny(u8, path_maybe_args, &.{ ' ', '\t', 0 }) orelse path_maybe_args.len;
1069 const unvalidated_path = path_maybe_args[0..path_end];
1070 file_name = if (fs.path.isAbsolute(unvalidated_path)) unvalidated_path else return error.RelativeShebang;
1071 continue;
1072 },
11901073
1191 else => |e| return e,1074 error.InvalidElfVersion,
1075 error.InvalidElfClass,
1076 error.InvalidElfEndian,
1077 => return error.InvalidElfFile,
1078
1079 error.ReadFailed => return file_reader.err.?,
1192 };1080 };
1193 const content = buffer[0..len];1081 is_elf_file = true;
11941082 break :elf_file header;
1195 if (mem.eql(u8, content[0..4], std.elf.MAGIC)) {
1196 // It is very likely ELF file!
1197 is_elf_file = true;
1198 break :elf_file file;
1199 } else if (mem.eql(u8, content[0..2], "#!")) {
1200 // We detected shebang, now parse entire line.
1201
1202 // Trim leading "#!", spaces and tabs.
1203 const trimmed_line = mem.trimStart(u8, content[2..], &.{ ' ', '\t' });
1204
1205 // This line can have:
1206 // * Interpreter path only,
1207 // * Interpreter path and arguments, all separated by space, tab or NUL character.
1208 // And optionally newline at the end.
1209 const path_maybe_args = mem.trimEnd(u8, trimmed_line, "\n");
1210
1211 // Separate path and args.
1212 const path_end = mem.indexOfAny(u8, path_maybe_args, &.{ ' ', '\t', 0 }) orelse path_maybe_args.len;
1213
1214 file_name = path_maybe_args[0..path_end];
1215 continue;
1216 } else {
1217 // Not a ELF file, not a shell script with "shebang line", invalid duck.
1218 return defaultAbiAndDynamicLinker(cpu, os, query);
1219 }
1220 }1083 }
1221 };1084 };
1222 defer elf_file.close();1085 defer file_reader.file.close(io);
12231086
1224 // TODO: inline this function and combine the buffer we already read above to find1087 return abiAndDynamicLinkerFromFile(&file_reader, &header, cpu, os, ld_info_list, query) catch |err| switch (err) {
1225 // the possible shebang line with the buffer we use for the ELF header.
1226 return abiAndDynamicLinkerFromFile(elf_file, cpu, os, ld_info_list, query) catch |err| switch (err) {
1227 error.FileSystem,1088 error.FileSystem,
1228 error.SystemResources,1089 error.SystemResources,
1229 error.SymLinkLoop,1090 error.SymLinkLoop,
...@@ -1232,6 +1093,8 @@ fn detectAbiAndDynamicLinker(...@@ -1232,6 +1093,8 @@ fn detectAbiAndDynamicLinker(
1232 error.ProcessNotFound,1093 error.ProcessNotFound,
1233 => |e| return e,1094 => |e| return e,
12341095
1096 error.ReadFailed => return file_reader.err.?,
1097
1235 error.UnableToReadElfFile,1098 error.UnableToReadElfFile,
1236 error.InvalidElfClass,1099 error.InvalidElfClass,
1237 error.InvalidElfVersion,1100 error.InvalidElfVersion,
...@@ -1239,12 +1102,12 @@ fn detectAbiAndDynamicLinker(...@@ -1239,12 +1102,12 @@ fn detectAbiAndDynamicLinker(
1239 error.InvalidElfFile,1102 error.InvalidElfFile,
1240 error.InvalidElfMagic,1103 error.InvalidElfMagic,
1241 error.Unexpected,1104 error.Unexpected,
1242 error.UnexpectedEndOfFile,1105 error.EndOfStream,
1243 error.NameTooLong,1106 error.NameTooLong,
1244 error.StaticElfFile,1107 error.StaticElfFile,
1245 // Finally, we fall back on the standard path.1108 // Finally, we fall back on the standard path.
1246 => |e| {1109 => |e| {
1247 std.log.warn("Encountered error: {s}, falling back to default ABI and dynamic linker.", .{@errorName(e)});1110 std.log.warn("encountered {t}; falling back to default ABI and dynamic linker", .{e});
1248 return defaultAbiAndDynamicLinker(cpu, os, query);1111 return defaultAbiAndDynamicLinker(cpu, os, query);
1249 },1112 },
1250 };1113 };
...@@ -1269,59 +1132,6 @@ const LdInfo = struct {...@@ -1269,59 +1132,6 @@ const LdInfo = struct {
1269 abi: Target.Abi,1132 abi: Target.Abi,
1270};1133};
12711134
1272fn preadAtLeast(file: fs.File, buf: []u8, offset: u64, min_read_len: usize) !usize {
1273 var i: usize = 0;
1274 while (i < min_read_len) {
1275 const len = file.pread(buf[i..], offset + i) catch |err| switch (err) {
1276 error.OperationAborted => unreachable, // Windows-only
1277 error.WouldBlock => unreachable, // Did not request blocking mode
1278 error.Canceled => unreachable, // timerfd is unseekable
1279 error.NotOpenForReading => unreachable,
1280 error.SystemResources => return error.SystemResources,
1281 error.IsDir => return error.UnableToReadElfFile,
1282 error.BrokenPipe => return error.UnableToReadElfFile,
1283 error.Unseekable => return error.UnableToReadElfFile,
1284 error.ConnectionResetByPeer => return error.UnableToReadElfFile,
1285 error.ConnectionTimedOut => return error.UnableToReadElfFile,
1286 error.SocketUnconnected => return error.UnableToReadElfFile,
1287 error.Unexpected => return error.Unexpected,
1288 error.InputOutput => return error.FileSystem,
1289 error.AccessDenied => return error.Unexpected,
1290 error.ProcessNotFound => return error.ProcessNotFound,
1291 error.LockViolation => return error.UnableToReadElfFile,
1292 };
1293 if (len == 0) return error.UnexpectedEndOfFile;
1294 i += len;
1295 }
1296 return i;
1297}
1298
1299fn elfInt(is_64: bool, need_bswap: bool, int_32: anytype, int_64: anytype) @TypeOf(int_64) {
1300 if (is_64) {
1301 if (need_bswap) {
1302 return @byteSwap(int_64);
1303 } else {
1304 return int_64;
1305 }
1306 } else {
1307 if (need_bswap) {
1308 return @byteSwap(int_32);
1309 } else {
1310 return int_32;
1311 }
1312 }
1313}
1314
1315const builtin = @import("builtin");
1316const std = @import("../std.zig");
1317const mem = std.mem;
1318const elf = std.elf;
1319const fs = std.fs;
1320const assert = std.debug.assert;
1321const Target = std.Target;
1322const native_endian = builtin.cpu.arch.endian();
1323const posix = std.posix;
1324
1325test {1135test {
1326 _ = NativePaths;1136 _ = NativePaths;
13271137
src/main.zig+53-36
...@@ -1,5 +1,8 @@...@@ -1,5 +1,8 @@
1const std = @import("std");
2const builtin = @import("builtin");1const builtin = @import("builtin");
2const native_os = builtin.os.tag;
3
4const std = @import("std");
5const Io = std.Io;
3const assert = std.debug.assert;6const assert = std.debug.assert;
4const fs = std.fs;7const fs = std.fs;
5const mem = std.mem;8const mem = std.mem;
...@@ -10,7 +13,6 @@ const Color = std.zig.Color;...@@ -10,7 +13,6 @@ const Color = std.zig.Color;
10const warn = std.log.warn;13const warn = std.log.warn;
11const ThreadPool = std.Thread.Pool;14const ThreadPool = std.Thread.Pool;
12const cleanExit = std.process.cleanExit;15const cleanExit = std.process.cleanExit;
13const native_os = builtin.os.tag;
14const Cache = std.Build.Cache;16const Cache = std.Build.Cache;
15const Path = std.Build.Cache.Path;17const Path = std.Build.Cache.Path;
16const Directory = std.Build.Cache.Directory;18const Directory = std.Build.Cache.Directory;
...@@ -245,26 +247,30 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -245,26 +247,30 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
245 }247 }
246 }248 }
247249
250 var threaded: Io.Threaded = .init(gpa);
251 defer threaded.deinit();
252 const io = threaded.io();
253
248 const cmd = args[1];254 const cmd = args[1];
249 const cmd_args = args[2..];255 const cmd_args = args[2..];
250 if (mem.eql(u8, cmd, "build-exe")) {256 if (mem.eql(u8, cmd, "build-exe")) {
251 dev.check(.build_exe_command);257 dev.check(.build_exe_command);
252 return buildOutputType(gpa, arena, args, .{ .build = .Exe });258 return buildOutputType(gpa, arena, io, args, .{ .build = .Exe });
253 } else if (mem.eql(u8, cmd, "build-lib")) {259 } else if (mem.eql(u8, cmd, "build-lib")) {
254 dev.check(.build_lib_command);260 dev.check(.build_lib_command);
255 return buildOutputType(gpa, arena, args, .{ .build = .Lib });261 return buildOutputType(gpa, arena, io, args, .{ .build = .Lib });
256 } else if (mem.eql(u8, cmd, "build-obj")) {262 } else if (mem.eql(u8, cmd, "build-obj")) {
257 dev.check(.build_obj_command);263 dev.check(.build_obj_command);
258 return buildOutputType(gpa, arena, args, .{ .build = .Obj });264 return buildOutputType(gpa, arena, io, args, .{ .build = .Obj });
259 } else if (mem.eql(u8, cmd, "test")) {265 } else if (mem.eql(u8, cmd, "test")) {
260 dev.check(.test_command);266 dev.check(.test_command);
261 return buildOutputType(gpa, arena, args, .zig_test);267 return buildOutputType(gpa, arena, io, args, .zig_test);
262 } else if (mem.eql(u8, cmd, "test-obj")) {268 } else if (mem.eql(u8, cmd, "test-obj")) {
263 dev.check(.test_command);269 dev.check(.test_command);
264 return buildOutputType(gpa, arena, args, .zig_test_obj);270 return buildOutputType(gpa, arena, io, args, .zig_test_obj);
265 } else if (mem.eql(u8, cmd, "run")) {271 } else if (mem.eql(u8, cmd, "run")) {
266 dev.check(.run_command);272 dev.check(.run_command);
267 return buildOutputType(gpa, arena, args, .run);273 return buildOutputType(gpa, arena, io, args, .run);
268 } else if (mem.eql(u8, cmd, "dlltool") or274 } else if (mem.eql(u8, cmd, "dlltool") or
269 mem.eql(u8, cmd, "ranlib") or275 mem.eql(u8, cmd, "ranlib") or
270 mem.eql(u8, cmd, "lib") or276 mem.eql(u8, cmd, "lib") or
...@@ -274,7 +280,7 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -274,7 +280,7 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
274 return process.exit(try llvmArMain(arena, args));280 return process.exit(try llvmArMain(arena, args));
275 } else if (mem.eql(u8, cmd, "build")) {281 } else if (mem.eql(u8, cmd, "build")) {
276 dev.check(.build_command);282 dev.check(.build_command);
277 return cmdBuild(gpa, arena, cmd_args);283 return cmdBuild(gpa, arena, io, cmd_args);
278 } else if (mem.eql(u8, cmd, "clang") or284 } else if (mem.eql(u8, cmd, "clang") or
279 mem.eql(u8, cmd, "-cc1") or mem.eql(u8, cmd, "-cc1as"))285 mem.eql(u8, cmd, "-cc1") or mem.eql(u8, cmd, "-cc1as"))
280 {286 {
...@@ -288,16 +294,16 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -288,16 +294,16 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
288 return process.exit(try lldMain(arena, args, true));294 return process.exit(try lldMain(arena, args, true));
289 } else if (mem.eql(u8, cmd, "cc")) {295 } else if (mem.eql(u8, cmd, "cc")) {
290 dev.check(.cc_command);296 dev.check(.cc_command);
291 return buildOutputType(gpa, arena, args, .cc);297 return buildOutputType(gpa, arena, io, args, .cc);
292 } else if (mem.eql(u8, cmd, "c++")) {298 } else if (mem.eql(u8, cmd, "c++")) {
293 dev.check(.cc_command);299 dev.check(.cc_command);
294 return buildOutputType(gpa, arena, args, .cpp);300 return buildOutputType(gpa, arena, io, args, .cpp);
295 } else if (mem.eql(u8, cmd, "translate-c")) {301 } else if (mem.eql(u8, cmd, "translate-c")) {
296 dev.check(.translate_c_command);302 dev.check(.translate_c_command);
297 return buildOutputType(gpa, arena, args, .translate_c);303 return buildOutputType(gpa, arena, io, args, .translate_c);
298 } else if (mem.eql(u8, cmd, "rc")) {304 } else if (mem.eql(u8, cmd, "rc")) {
299 const use_server = cmd_args.len > 0 and std.mem.eql(u8, cmd_args[0], "--zig-integration");305 const use_server = cmd_args.len > 0 and std.mem.eql(u8, cmd_args[0], "--zig-integration");
300 return jitCmd(gpa, arena, cmd_args, .{306 return jitCmd(gpa, arena, io, cmd_args, .{
301 .cmd_name = "resinator",307 .cmd_name = "resinator",
302 .root_src_path = "resinator/main.zig",308 .root_src_path = "resinator/main.zig",
303 .depend_on_aro = true,309 .depend_on_aro = true,
...@@ -308,20 +314,20 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -308,20 +314,20 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
308 dev.check(.fmt_command);314 dev.check(.fmt_command);
309 return @import("fmt.zig").run(gpa, arena, cmd_args);315 return @import("fmt.zig").run(gpa, arena, cmd_args);
310 } else if (mem.eql(u8, cmd, "objcopy")) {316 } else if (mem.eql(u8, cmd, "objcopy")) {
311 return jitCmd(gpa, arena, cmd_args, .{317 return jitCmd(gpa, arena, io, cmd_args, .{
312 .cmd_name = "objcopy",318 .cmd_name = "objcopy",
313 .root_src_path = "objcopy.zig",319 .root_src_path = "objcopy.zig",
314 });320 });
315 } else if (mem.eql(u8, cmd, "fetch")) {321 } else if (mem.eql(u8, cmd, "fetch")) {
316 return cmdFetch(gpa, arena, cmd_args);322 return cmdFetch(gpa, arena, cmd_args);
317 } else if (mem.eql(u8, cmd, "libc")) {323 } else if (mem.eql(u8, cmd, "libc")) {
318 return jitCmd(gpa, arena, cmd_args, .{324 return jitCmd(gpa, arena, io, cmd_args, .{
319 .cmd_name = "libc",325 .cmd_name = "libc",
320 .root_src_path = "libc.zig",326 .root_src_path = "libc.zig",
321 .prepend_zig_lib_dir_path = true,327 .prepend_zig_lib_dir_path = true,
322 });328 });
323 } else if (mem.eql(u8, cmd, "std")) {329 } else if (mem.eql(u8, cmd, "std")) {
324 return jitCmd(gpa, arena, cmd_args, .{330 return jitCmd(gpa, arena, io, cmd_args, .{
325 .cmd_name = "std",331 .cmd_name = "std",
326 .root_src_path = "std-docs.zig",332 .root_src_path = "std-docs.zig",
327 .prepend_zig_lib_dir_path = true,333 .prepend_zig_lib_dir_path = true,
...@@ -332,7 +338,7 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -332,7 +338,7 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
332 return cmdInit(gpa, arena, cmd_args);338 return cmdInit(gpa, arena, cmd_args);
333 } else if (mem.eql(u8, cmd, "targets")) {339 } else if (mem.eql(u8, cmd, "targets")) {
334 dev.check(.targets_command);340 dev.check(.targets_command);
335 const host = std.zig.resolveTargetQueryOrFatal(.{});341 const host = std.zig.resolveTargetQueryOrFatal(io, .{});
336 var stdout_writer = fs.File.stdout().writer(&stdout_buffer);342 var stdout_writer = fs.File.stdout().writer(&stdout_buffer);
337 try @import("print_targets.zig").cmdTargets(arena, cmd_args, &stdout_writer.interface, &host);343 try @import("print_targets.zig").cmdTargets(arena, cmd_args, &stdout_writer.interface, &host);
338 return stdout_writer.interface.flush();344 return stdout_writer.interface.flush();
...@@ -351,7 +357,7 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -351,7 +357,7 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
351 );357 );
352 return stdout_writer.interface.flush();358 return stdout_writer.interface.flush();
353 } else if (mem.eql(u8, cmd, "reduce")) {359 } else if (mem.eql(u8, cmd, "reduce")) {
354 return jitCmd(gpa, arena, cmd_args, .{360 return jitCmd(gpa, arena, io, cmd_args, .{
355 .cmd_name = "reduce",361 .cmd_name = "reduce",
356 .root_src_path = "reduce.zig",362 .root_src_path = "reduce.zig",
357 });363 });
...@@ -364,7 +370,7 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -364,7 +370,7 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
364 } else if (mem.eql(u8, cmd, "ast-check")) {370 } else if (mem.eql(u8, cmd, "ast-check")) {
365 return cmdAstCheck(arena, cmd_args);371 return cmdAstCheck(arena, cmd_args);
366 } else if (mem.eql(u8, cmd, "detect-cpu")) {372 } else if (mem.eql(u8, cmd, "detect-cpu")) {
367 return cmdDetectCpu(cmd_args);373 return cmdDetectCpu(io, cmd_args);
368 } else if (build_options.enable_debug_extensions and mem.eql(u8, cmd, "changelist")) {374 } else if (build_options.enable_debug_extensions and mem.eql(u8, cmd, "changelist")) {
369 return cmdChangelist(arena, cmd_args);375 return cmdChangelist(arena, cmd_args);
370 } else if (build_options.enable_debug_extensions and mem.eql(u8, cmd, "dump-zir")) {376 } else if (build_options.enable_debug_extensions and mem.eql(u8, cmd, "dump-zir")) {
...@@ -792,6 +798,7 @@ const CliModule = struct {...@@ -792,6 +798,7 @@ const CliModule = struct {
792fn buildOutputType(798fn buildOutputType(
793 gpa: Allocator,799 gpa: Allocator,
794 arena: Allocator,800 arena: Allocator,
801 io: Io,
795 all_args: []const []const u8,802 all_args: []const []const u8,
796 arg_mode: ArgMode,803 arg_mode: ArgMode,
797) !void {804) !void {
...@@ -3017,7 +3024,7 @@ fn buildOutputType(...@@ -3017,7 +3024,7 @@ fn buildOutputType(
3017 create_module.opts.emit_bin = emit_bin != .no;3024 create_module.opts.emit_bin = emit_bin != .no;
3018 create_module.opts.any_c_source_files = create_module.c_source_files.items.len != 0;3025 create_module.opts.any_c_source_files = create_module.c_source_files.items.len != 0;
30193026
3020 const main_mod = try createModule(gpa, arena, &create_module, 0, null, color);3027 const main_mod = try createModule(gpa, arena, io, &create_module, 0, null, color);
3021 for (create_module.modules.keys(), create_module.modules.values()) |key, cli_mod| {3028 for (create_module.modules.keys(), create_module.modules.values()) |key, cli_mod| {
3022 if (cli_mod.resolved == null)3029 if (cli_mod.resolved == null)
3023 fatal("module '{s}' declared but not used", .{key});3030 fatal("module '{s}' declared but not used", .{key});
...@@ -3545,6 +3552,7 @@ fn buildOutputType(...@@ -3545,6 +3552,7 @@ fn buildOutputType(
3545 var stdin_reader = fs.File.stdin().reader(&stdin_buffer);3552 var stdin_reader = fs.File.stdin().reader(&stdin_buffer);
3546 var stdout_writer = fs.File.stdout().writer(&stdout_buffer);3553 var stdout_writer = fs.File.stdout().writer(&stdout_buffer);
3547 try serve(3554 try serve(
3555 io,
3548 comp,3556 comp,
3549 &stdin_reader.interface,3557 &stdin_reader.interface,
3550 &stdout_writer.interface,3558 &stdout_writer.interface,
...@@ -3571,6 +3579,7 @@ fn buildOutputType(...@@ -3571,6 +3579,7 @@ fn buildOutputType(
3571 var output = conn.stream.writer(&stdout_buffer);3579 var output = conn.stream.writer(&stdout_buffer);
35723580
3573 try serve(3581 try serve(
3582 io,
3574 comp,3583 comp,
3575 input.interface(),3584 input.interface(),
3576 &output.interface,3585 &output.interface,
...@@ -3646,6 +3655,7 @@ fn buildOutputType(...@@ -3646,6 +3655,7 @@ fn buildOutputType(
3646 comp,3655 comp,
3647 gpa,3656 gpa,
3648 arena,3657 arena,
3658 io,
3649 test_exec_args.items,3659 test_exec_args.items,
3650 self_exe_path,3660 self_exe_path,
3651 arg_mode,3661 arg_mode,
...@@ -3704,6 +3714,7 @@ const CreateModule = struct {...@@ -3704,6 +3714,7 @@ const CreateModule = struct {
3704fn createModule(3714fn createModule(
3705 gpa: Allocator,3715 gpa: Allocator,
3706 arena: Allocator,3716 arena: Allocator,
3717 io: Io,
3707 create_module: *CreateModule,3718 create_module: *CreateModule,
3708 index: usize,3719 index: usize,
3709 parent: ?*Package.Module,3720 parent: ?*Package.Module,
...@@ -3777,7 +3788,7 @@ fn createModule(...@@ -3777,7 +3788,7 @@ fn createModule(
3777 }3788 }
37783789
3779 const target_query = std.zig.parseTargetQueryOrReportFatalError(arena, target_parse_options);3790 const target_query = std.zig.parseTargetQueryOrReportFatalError(arena, target_parse_options);
3780 const target = std.zig.resolveTargetQueryOrFatal(target_query);3791 const target = std.zig.resolveTargetQueryOrFatal(io, target_query);
3781 break :t .{3792 break :t .{
3782 .result = target,3793 .result = target,
3783 .is_native_os = target_query.isNativeOs(),3794 .is_native_os = target_query.isNativeOs(),
...@@ -4022,7 +4033,7 @@ fn createModule(...@@ -4022,7 +4033,7 @@ fn createModule(
4022 for (cli_mod.deps) |dep| {4033 for (cli_mod.deps) |dep| {
4023 const dep_index = create_module.modules.getIndex(dep.value) orelse4034 const dep_index = create_module.modules.getIndex(dep.value) orelse
4024 fatal("module '{s}' depends on non-existent module '{s}'", .{ name, dep.key });4035 fatal("module '{s}' depends on non-existent module '{s}'", .{ name, dep.key });
4025 const dep_mod = try createModule(gpa, arena, create_module, dep_index, mod, color);4036 const dep_mod = try createModule(gpa, arena, io, create_module, dep_index, mod, color);
4026 try mod.deps.put(arena, dep.key, dep_mod);4037 try mod.deps.put(arena, dep.key, dep_mod);
4027 }4038 }
40284039
...@@ -4038,9 +4049,10 @@ fn saveState(comp: *Compilation, incremental: bool) void {...@@ -4038,9 +4049,10 @@ fn saveState(comp: *Compilation, incremental: bool) void {
4038}4049}
40394050
4040fn serve(4051fn serve(
4052 io: Io,
4041 comp: *Compilation,4053 comp: *Compilation,
4042 in: *std.Io.Reader,4054 in: *Io.Reader,
4043 out: *std.Io.Writer,4055 out: *Io.Writer,
4044 test_exec_args: []const ?[]const u8,4056 test_exec_args: []const ?[]const u8,
4045 self_exe_path: ?[]const u8,4057 self_exe_path: ?[]const u8,
4046 arg_mode: ArgMode,4058 arg_mode: ArgMode,
...@@ -4090,7 +4102,7 @@ fn serve(...@@ -4090,7 +4102,7 @@ fn serve(
4090 defer arena_instance.deinit();4102 defer arena_instance.deinit();
4091 const arena = arena_instance.allocator();4103 const arena = arena_instance.allocator();
4092 var output: Compilation.CImportResult = undefined;4104 var output: Compilation.CImportResult = undefined;
4093 try cmdTranslateC(comp, arena, &output, file_system_inputs, main_progress_node);4105 try cmdTranslateC(io, comp, arena, &output, file_system_inputs, main_progress_node);
4094 defer output.deinit(gpa);4106 defer output.deinit(gpa);
40954107
4096 if (file_system_inputs.items.len != 0) {4108 if (file_system_inputs.items.len != 0) {
...@@ -4126,6 +4138,7 @@ fn serve(...@@ -4126,6 +4138,7 @@ fn serve(
4126 // comp,4138 // comp,
4127 // gpa,4139 // gpa,
4128 // arena,4140 // arena,
4141 // io,
4129 // test_exec_args,4142 // test_exec_args,
4130 // self_exe_path.?,4143 // self_exe_path.?,
4131 // arg_mode,4144 // arg_mode,
...@@ -4280,6 +4293,7 @@ fn runOrTest(...@@ -4280,6 +4293,7 @@ fn runOrTest(
4280 comp: *Compilation,4293 comp: *Compilation,
4281 gpa: Allocator,4294 gpa: Allocator,
4282 arena: Allocator,4295 arena: Allocator,
4296 io: Io,
4283 test_exec_args: []const ?[]const u8,4297 test_exec_args: []const ?[]const u8,
4284 self_exe_path: []const u8,4298 self_exe_path: []const u8,
4285 arg_mode: ArgMode,4299 arg_mode: ArgMode,
...@@ -4334,7 +4348,7 @@ fn runOrTest(...@@ -4334,7 +4348,7 @@ fn runOrTest(
4334 std.debug.lockStdErr();4348 std.debug.lockStdErr();
4335 const err = process.execve(gpa, argv.items, &env_map);4349 const err = process.execve(gpa, argv.items, &env_map);
4336 std.debug.unlockStdErr();4350 std.debug.unlockStdErr();
4337 try warnAboutForeignBinaries(arena, arg_mode, target, link_libc);4351 try warnAboutForeignBinaries(io, arena, arg_mode, target, link_libc);
4338 const cmd = try std.mem.join(arena, " ", argv.items);4352 const cmd = try std.mem.join(arena, " ", argv.items);
4339 fatal("the following command failed to execve with '{s}':\n{s}", .{ @errorName(err), cmd });4353 fatal("the following command failed to execve with '{s}':\n{s}", .{ @errorName(err), cmd });
4340 } else if (process.can_spawn) {4354 } else if (process.can_spawn) {
...@@ -4355,7 +4369,7 @@ fn runOrTest(...@@ -4355,7 +4369,7 @@ fn runOrTest(
4355 break :t child.spawnAndWait();4369 break :t child.spawnAndWait();
4356 };4370 };
4357 const term = term_result catch |err| {4371 const term = term_result catch |err| {
4358 try warnAboutForeignBinaries(arena, arg_mode, target, link_libc);4372 try warnAboutForeignBinaries(io, arena, arg_mode, target, link_libc);
4359 const cmd = try std.mem.join(arena, " ", argv.items);4373 const cmd = try std.mem.join(arena, " ", argv.items);
4360 fatal("the following command failed with '{s}':\n{s}", .{ @errorName(err), cmd });4374 fatal("the following command failed with '{s}':\n{s}", .{ @errorName(err), cmd });
4361 };4375 };
...@@ -4594,11 +4608,12 @@ fn cmdTranslateC(...@@ -4594,11 +4608,12 @@ fn cmdTranslateC(
4594pub fn translateC(4608pub fn translateC(
4595 gpa: Allocator,4609 gpa: Allocator,
4596 arena: Allocator,4610 arena: Allocator,
4611 io: Io,
4597 argv: []const []const u8,4612 argv: []const []const u8,
4598 prog_node: std.Progress.Node,4613 prog_node: std.Progress.Node,
4599 capture: ?*[]u8,4614 capture: ?*[]u8,
4600) !void {4615) !void {
4601 try jitCmd(gpa, arena, argv, .{4616 try jitCmd(gpa, arena, io, argv, .{
4602 .cmd_name = "translate-c",4617 .cmd_name = "translate-c",
4603 .root_src_path = "translate-c/main.zig",4618 .root_src_path = "translate-c/main.zig",
4604 .depend_on_aro = true,4619 .depend_on_aro = true,
...@@ -4755,7 +4770,7 @@ test sanitizeExampleName {...@@ -4755,7 +4770,7 @@ test sanitizeExampleName {
4755 try std.testing.expectEqualStrings("test_project", try sanitizeExampleName(arena, "test project"));4770 try std.testing.expectEqualStrings("test_project", try sanitizeExampleName(arena, "test project"));
4756}4771}
47574772
4758fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {4773fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) !void {
4759 dev.check(.build_command);4774 dev.check(.build_command);
47604775
4761 var build_file: ?[]const u8 = null;4776 var build_file: ?[]const u8 = null;
...@@ -4983,7 +4998,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -4983,7 +4998,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
4983 .arch_os_abi = triple,4998 .arch_os_abi = triple,
4984 });4999 });
4985 break :t .{5000 break :t .{
4986 .result = std.zig.resolveTargetQueryOrFatal(target_query),5001 .result = std.zig.resolveTargetQueryOrFatal(io, target_query),
4987 .is_native_os = false,5002 .is_native_os = false,
4988 .is_native_abi = false,5003 .is_native_abi = false,
4989 .is_explicit_dynamic_linker = false,5004 .is_explicit_dynamic_linker = false,
...@@ -4991,7 +5006,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -4991,7 +5006,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
4991 }5006 }
4992 }5007 }
4993 break :t .{5008 break :t .{
4994 .result = std.zig.resolveTargetQueryOrFatal(.{}),5009 .result = std.zig.resolveTargetQueryOrFatal(io, .{}),
4995 .is_native_os = true,5010 .is_native_os = true,
4996 .is_native_abi = true,5011 .is_native_abi = true,
4997 .is_explicit_dynamic_linker = false,5012 .is_explicit_dynamic_linker = false,
...@@ -5400,6 +5415,7 @@ const JitCmdOptions = struct {...@@ -5400,6 +5415,7 @@ const JitCmdOptions = struct {
5400fn jitCmd(5415fn jitCmd(
5401 gpa: Allocator,5416 gpa: Allocator,
5402 arena: Allocator,5417 arena: Allocator,
5418 io: Io,
5403 args: []const []const u8,5419 args: []const []const u8,
5404 options: JitCmdOptions,5420 options: JitCmdOptions,
5405) !void {5421) !void {
...@@ -5412,7 +5428,7 @@ fn jitCmd(...@@ -5412,7 +5428,7 @@ fn jitCmd(
54125428
5413 const target_query: std.Target.Query = .{};5429 const target_query: std.Target.Query = .{};
5414 const resolved_target: Package.Module.ResolvedTarget = .{5430 const resolved_target: Package.Module.ResolvedTarget = .{
5415 .result = std.zig.resolveTargetQueryOrFatal(target_query),5431 .result = std.zig.resolveTargetQueryOrFatal(io, target_query),
5416 .is_native_os = true,5432 .is_native_os = true,
5417 .is_native_abi = true,5433 .is_native_abi = true,
5418 .is_explicit_dynamic_linker = false,5434 .is_explicit_dynamic_linker = false,
...@@ -6209,7 +6225,7 @@ fn cmdAstCheck(...@@ -6209,7 +6225,7 @@ fn cmdAstCheck(
6209 }6225 }
6210}6226}
62116227
6212fn cmdDetectCpu(args: []const []const u8) !void {6228fn cmdDetectCpu(io: Io, args: []const []const u8) !void {
6213 dev.check(.detect_cpu_command);6229 dev.check(.detect_cpu_command);
62146230
6215 const detect_cpu_usage =6231 const detect_cpu_usage =
...@@ -6254,7 +6270,7 @@ fn cmdDetectCpu(args: []const []const u8) !void {...@@ -6254,7 +6270,7 @@ fn cmdDetectCpu(args: []const []const u8) !void {
6254 const cpu = try detectNativeCpuWithLLVM(builtin.cpu.arch, name, features);6270 const cpu = try detectNativeCpuWithLLVM(builtin.cpu.arch, name, features);
6255 try printCpu(cpu);6271 try printCpu(cpu);
6256 } else {6272 } else {
6257 const host_target = std.zig.resolveTargetQueryOrFatal(.{});6273 const host_target = std.zig.resolveTargetQueryOrFatal(io, .{});
6258 try printCpu(host_target.cpu);6274 try printCpu(host_target.cpu);
6259 }6275 }
6260}6276}
...@@ -6521,13 +6537,14 @@ fn prefixedIntArg(arg: []const u8, prefix: []const u8) ?u64 {...@@ -6521,13 +6537,14 @@ fn prefixedIntArg(arg: []const u8, prefix: []const u8) ?u64 {
6521}6537}
65226538
6523fn warnAboutForeignBinaries(6539fn warnAboutForeignBinaries(
6540 io: Io,
6524 arena: Allocator,6541 arena: Allocator,
6525 arg_mode: ArgMode,6542 arg_mode: ArgMode,
6526 target: *const std.Target,6543 target: *const std.Target,
6527 link_libc: bool,6544 link_libc: bool,
6528) !void {6545) !void {
6529 const host_query: std.Target.Query = .{};6546 const host_query: std.Target.Query = .{};
6530 const host_target = std.zig.resolveTargetQueryOrFatal(host_query);6547 const host_target = std.zig.resolveTargetQueryOrFatal(io, host_query);
65316548
6532 switch (std.zig.system.getExternalExecutor(&host_target, target, .{ .link_libc = link_libc })) {6549 switch (std.zig.system.getExternalExecutor(&host_target, target, .{ .link_libc = link_libc })) {
6533 .native => return,6550 .native => return,
...@@ -7080,7 +7097,7 @@ fn cmdFetch(...@@ -7080,7 +7097,7 @@ fn cmdFetch(
7080 try fixups.append_string_after_node.put(gpa, manifest.version_node, dependencies_text);7097 try fixups.append_string_after_node.put(gpa, manifest.version_node, dependencies_text);
7081 }7098 }
70827099
7083 var aw: std.Io.Writer.Allocating = .init(gpa);7100 var aw: Io.Writer.Allocating = .init(gpa);
7084 defer aw.deinit();7101 defer aw.deinit();
7085 try ast.render(gpa, &aw.writer, fixups);7102 try ast.render(gpa, &aw.writer, fixups);
7086 const rendered = aw.written();7103 const rendered = aw.written();