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 {
3838
3939 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
4145 // skip my own exe name
4246 var arg_idx: usize = 1;
4347
......@@ -68,6 +72,7 @@ pub fn main() !void {
6872 };
6973
7074 var graph: std.Build.Graph = .{
75 .io = io,
7176 .arena = arena,
7277 .cache = .{
7378 .gpa = arena,
lib/std/Build.zig+6-2
......@@ -1,5 +1,7 @@
1const std = @import("std.zig");
21const builtin = @import("builtin");
2
3const std = @import("std.zig");
4const Io = std.Io;
35const fs = std.fs;
46const mem = std.mem;
57const debug = std.debug;
......@@ -110,6 +112,7 @@ pub const ReleaseMode = enum {
110112/// Shared state among all Build instances.
111113/// Settings that are here rather than in Build are not configurable per-package.
112114pub const Graph = struct {
115 io: Io,
113116 arena: Allocator,
114117 system_library_options: std.StringArrayHashMapUnmanaged(SystemLibraryMode) = .empty,
115118 system_package_mode: bool = false,
......@@ -2666,9 +2669,10 @@ pub fn resolveTargetQuery(b: *Build, query: Target.Query) ResolvedTarget {
26662669 // Hot path. This is faster than querying the native CPU and OS again.
26672670 return b.graph.host;
26682671 }
2672 const io = b.graph.io;
26692673 return .{
26702674 .query = query,
2671 .result = std.zig.system.resolveTargetQuery(query) catch
2675 .result = std.zig.system.resolveTargetQuery(io, query) catch
26722676 @panic("unable to resolve target query"),
26732677 };
26742678}
lib/std/Build/Step/Options.zig+3-1
......@@ -532,6 +532,8 @@ const Arg = struct {
532532test Options {
533533 if (builtin.os.tag == .wasi) return error.SkipZigTest;
534534
535 const io = std.testing.io;
536
535537 var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
536538 defer arena.deinit();
537539
......@@ -546,7 +548,7 @@ test Options {
546548 .global_cache_root = .{ .path = "test", .handle = std.fs.cwd() },
547549 .host = .{
548550 .query = .{},
549 .result = try std.zig.system.resolveTargetQuery(.{}),
551 .result = try std.zig.system.resolveTargetQuery(io, .{}),
550552 },
551553 .zig_lib_directory = std.Build.Cache.Directory.cwd(),
552554 .time_report = false,
lib/std/Build/WebServer.zig+2-1
......@@ -516,6 +516,7 @@ pub fn serveTarFile(
516516}
517517
518518fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.OptimizeMode) !Cache.Path {
519 const io = ws.graph.io;
519520 const root_name = "build-web";
520521 const arch_os_abi = "wasm32-freestanding";
521522 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
659660 };
660661 const bin_name = try std.zig.binNameAlloc(arena, .{
661662 .root_name = root_name,
662 .target = &(std.zig.system.resolveTargetQuery(std.Build.parseTargetQuery(.{
663 .target = &(std.zig.system.resolveTargetQuery(io, std.Build.parseTargetQuery(.{
663664 .arch_os_abi = arch_os_abi,
664665 .cpu_features = cpu_features,
665666 }) catch unreachable) catch unreachable),
lib/std/Io.zig+3-3
......@@ -738,9 +738,9 @@ pub const Timestamp = struct {
738738 /// * On Linux, corresponds `CLOCK_MONOTONIC`.
739739 /// * On macOS, corresponds to `CLOCK_UPTIME_RAW`.
740740 awake,
741 /// Identical to `awake` except it expresses intent to include time
742 /// that the system is suspended, however, it may be implemented
743 /// identically to `awake`.
741 /// Identical to `awake` except it expresses intent to **include time
742 /// that the system is suspended**, however, due to limitations it may
743 /// behave identically to `awake`.
744744 ///
745745 /// * On Linux, corresponds `CLOCK_BOOTTIME`.
746746 /// * 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!
10541054fn sleepLinux(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {
10551055 const pool: *Pool = @ptrCast(@alignCast(userdata));
10561056 const clock_id: posix.clockid_t = clockToPosix(switch (timeout) {
1057 .none => .monotonic,
1057 .none => .awake,
10581058 .duration => |d| d.clock,
10591059 .deadline => |d| d.clock,
10601060 });
......@@ -1087,7 +1087,6 @@ fn sleepWindows(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {
10871087 const ms = ms: {
10881088 const duration_and_clock = (try timeout.toDurationFromNow(pool.io())) orelse
10891089 break :ms std.math.maxInt(windows.DWORD);
1090 if (duration_and_clock.clock != .monotonic) return error.UnsupportedClock;
10911090 break :ms std.math.lossyCast(windows.DWORD, duration_and_clock.duration.toMilliseconds());
10921091 };
10931092 windows.kernel32.Sleep(ms);
......@@ -1132,8 +1131,6 @@ fn sleepPosix(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {
11321131 .sec = std.math.maxInt(sec_type),
11331132 .nsec = std.math.maxInt(nsec_type),
11341133 };
1135 // TODO check which clock nanosleep uses on this host
1136 // and return error.UnsupportedClock if it does not match
11371134 const ns = d.duration.nanoseconds;
11381135 break :t .{
11391136 .sec = @intCast(@divFloor(ns, std.time.ns_per_s)),
......@@ -2046,9 +2043,9 @@ fn clockToPosix(clock: Io.Timestamp.Clock) posix.clockid_t {
20462043fn clockToWasi(clock: Io.Timestamp.Clock) std.os.wasi.clockid_t {
20472044 return switch (clock) {
20482045 .realtime => .REALTIME,
2049 .monotonic => .MONOTONIC,
2050 .uptime => .MONOTONIC,
2051 .process_cputime_id => .PROCESS_CPUTIME_ID,
2052 .thread_cputime_id => .THREAD_CPUTIME_ID,
2046 .awake => .MONOTONIC,
2047 .boot => .MONOTONIC,
2048 .cpu_process => .PROCESS_CPUTIME_ID,
2049 .cpu_thread => .THREAD_CPUTIME_ID,
20532050 };
20542051}
lib/std/Io/net.zig+1-1
......@@ -228,7 +228,7 @@ pub const IpAddress = union(enum) {
228228 ///
229229 /// One bound `Socket` can be used to receive messages from multiple
230230 /// 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 {
232232 return io.vtable.ipBind(io.userdata, address, options);
233233 }
234234
lib/std/Target/Query.zig+7-5
......@@ -612,6 +612,8 @@ fn versionEqualOpt(a: ?SemanticVersion, b: ?SemanticVersion) bool {
612612}
613613
614614test parse {
615 const io = std.testing.io;
616
615617 if (builtin.target.isGnuLibC()) {
616618 var query = try Query.parse(.{});
617619 query.setGnuLibCVersion(2, 1, 1);
......@@ -654,7 +656,7 @@ test parse {
654656 .arch_os_abi = "x86_64-linux-gnu",
655657 .cpu_features = "x86_64-sse-sse2-avx-cx8",
656658 });
657 const target = try std.zig.system.resolveTargetQuery(query);
659 const target = try std.zig.system.resolveTargetQuery(io, query);
658660
659661 try std.testing.expect(target.os.tag == .linux);
660662 try std.testing.expect(target.abi == .gnu);
......@@ -679,7 +681,7 @@ test parse {
679681 .arch_os_abi = "arm-linux-musleabihf",
680682 .cpu_features = "generic+v8a",
681683 });
682 const target = try std.zig.system.resolveTargetQuery(query);
684 const target = try std.zig.system.resolveTargetQuery(io, query);
683685
684686 try std.testing.expect(target.os.tag == .linux);
685687 try std.testing.expect(target.abi == .musleabihf);
......@@ -696,7 +698,7 @@ test parse {
696698 .arch_os_abi = "aarch64-linux.3.10...4.4.1-gnu.2.27",
697699 .cpu_features = "generic+v8a",
698700 });
699 const target = try std.zig.system.resolveTargetQuery(query);
701 const target = try std.zig.system.resolveTargetQuery(io, query);
700702
701703 try std.testing.expect(target.cpu.arch == .aarch64);
702704 try std.testing.expect(target.os.tag == .linux);
......@@ -719,7 +721,7 @@ test parse {
719721 const query = try Query.parse(.{
720722 .arch_os_abi = "aarch64-linux.3.10...4.4.1-android.30",
721723 });
722 const target = try std.zig.system.resolveTargetQuery(query);
724 const target = try std.zig.system.resolveTargetQuery(io, query);
723725
724726 try std.testing.expect(target.cpu.arch == .aarch64);
725727 try std.testing.expect(target.os.tag == .linux);
......@@ -740,7 +742,7 @@ test parse {
740742 const query = try Query.parse(.{
741743 .arch_os_abi = "x86-windows.xp...win8-msvc",
742744 });
743 const target = try std.zig.system.resolveTargetQuery(query);
745 const target = try std.zig.system.resolveTargetQuery(io, query);
744746
745747 try std.testing.expect(target.cpu.arch == .x86);
746748 try std.testing.expect(target.os.tag == .windows);
lib/std/elf.zig+121-45
......@@ -1,9 +1,11 @@
11//! Executable and Linkable Format.
22
33const std = @import("std.zig");
4const Io = std.Io;
45const math = std.math;
56const mem = std.mem;
67const assert = std.debug.assert;
8const Endian = std.builtin.Endian;
79const native_endian = @import("builtin").target.cpu.arch.endian();
810
911pub const AT_NULL = 0;
......@@ -568,7 +570,7 @@ pub const ET = enum(u16) {
568570/// All integers are native endian.
569571pub const Header = struct {
570572 is_64: bool,
571 endian: std.builtin.Endian,
573 endian: Endian,
572574 os_abi: OSABI,
573575 /// The meaning of this value depends on `os_abi`.
574576 abi_version: u8,
......@@ -583,48 +585,76 @@ pub const Header = struct {
583585 shnum: u16,
584586 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 {
587589 return .{
588 .elf_header = h,
590 .is_64 = h.is_64,
591 .endian = h.endian,
592 .phnum = h.phnum,
593 .phoff = h.phoff,
589594 .file_reader = file_reader,
590595 };
591596 }
592597
593 pub fn iterateProgramHeadersBuffer(h: Header, buf: []const u8) ProgramHeaderBufferIterator {
598 pub fn iterateProgramHeadersBuffer(h: *const Header, buf: []const u8) ProgramHeaderBufferIterator {
594599 return .{
595 .elf_header = h,
600 .is_64 = h.is_64,
601 .endian = h.endian,
602 .phnum = h.phnum,
603 .phoff = h.phoff,
596604 .buf = buf,
597605 };
598606 }
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 {
601609 return .{
602 .elf_header = h,
610 .is_64 = h.is_64,
611 .endian = h.endian,
612 .shnum = h.shnum,
613 .shoff = h.shoff,
603614 .file_reader = file_reader,
604615 };
605616 }
606617
607 pub fn iterateSectionHeadersBuffer(h: Header, buf: []const u8) SectionHeaderBufferIterator {
618 pub fn iterateSectionHeadersBuffer(h: *const Header, buf: []const u8) SectionHeaderBufferIterator {
608619 return .{
609 .elf_header = h,
620 .is_64 = h.is_64,
621 .endian = h.endian,
622 .shnum = h.shnum,
623 .shoff = h.shoff,
610624 .buf = buf,
611625 };
612626 }
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{
615644 InvalidElfMagic,
616645 InvalidElfVersion,
617646 InvalidElfClass,
618647 InvalidElfEndian,
619648 };
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 {
622652 const buf = try r.peek(@sizeOf(Elf64_Ehdr));
623653
624654 if (!mem.eql(u8, buf[0..4], MAGIC)) return error.InvalidElfMagic;
625655 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]) {
628658 ELFDATA2LSB => .little,
629659 ELFDATA2MSB => .big,
630660 else => return error.InvalidElfEndian,
......@@ -637,7 +667,7 @@ pub const Header = struct {
637667 };
638668 }
639669
640 pub fn init(hdr: anytype, endian: std.builtin.Endian) Header {
670 pub fn init(hdr: anytype, endian: Endian) Header {
641671 // Converting integers to exhaustive enums using `@enumFromInt` could cause a panic.
642672 comptime assert(!@typeInfo(OSABI).@"enum".is_exhaustive);
643673 return .{
......@@ -664,46 +694,54 @@ pub const Header = struct {
664694};
665695
666696pub const ProgramHeaderIterator = struct {
667 elf_header: Header,
668 file_reader: *std.fs.File.Reader,
697 is_64: bool,
698 endian: Endian,
699 phnum: u16,
700 phoff: u64,
701
702 file_reader: *Io.File.Reader,
669703 index: usize = 0,
670704
671705 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;
673707 defer it.index += 1;
674708
675 const size: u64 = if (it.elf_header.is_64) @sizeOf(Elf64_Phdr) else @sizeOf(Elf32_Phdr);
676 const offset = it.elf_header.phoff + size * it.index;
709 const size: u64 = if (it.is_64) @sizeOf(Elf64_Phdr) else @sizeOf(Elf32_Phdr);
710 const offset = it.phoff + size * it.index;
677711 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);
680714 }
681715};
682716
683717pub const ProgramHeaderBufferIterator = struct {
684 elf_header: Header,
718 is_64: bool,
719 endian: Endian,
720 phnum: u16,
721 phoff: u64,
722
685723 buf: []const u8,
686724 index: usize = 0,
687725
688726 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;
690728 defer it.index += 1;
691729
692 const size: u64 = if (it.elf_header.is_64) @sizeOf(Elf64_Phdr) else @sizeOf(Elf32_Phdr);
693 const offset = it.elf_header.phoff + size * it.index;
694 var reader = std.Io.Reader.fixed(it.buf[offset..]);
730 const size: u64 = if (it.is_64) @sizeOf(Elf64_Phdr) else @sizeOf(Elf32_Phdr);
731 const offset = it.phoff + size * it.index;
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);
697735 }
698736};
699737
700fn takePhdr(reader: *std.Io.Reader, elf_header: Header) !?Elf64_Phdr {
701 if (elf_header.is_64) {
702 const phdr = try reader.takeStruct(Elf64_Phdr, elf_header.endian);
738pub fn takeProgramHeader(reader: *Io.Reader, is_64: bool, endian: Endian) !Elf64_Phdr {
739 if (is_64) {
740 const phdr = try reader.takeStruct(Elf64_Phdr, endian);
703741 return phdr;
704742 }
705743
706 const phdr = try reader.takeStruct(Elf32_Phdr, elf_header.endian);
744 const phdr = try reader.takeStruct(Elf32_Phdr, endian);
707745 return .{
708746 .p_type = phdr.p_type,
709747 .p_offset = phdr.p_offset,
......@@ -717,47 +755,55 @@ fn takePhdr(reader: *std.Io.Reader, elf_header: Header) !?Elf64_Phdr {
717755}
718756
719757pub const SectionHeaderIterator = struct {
720 elf_header: Header,
721 file_reader: *std.fs.File.Reader,
758 is_64: bool,
759 endian: Endian,
760 shnum: u16,
761 shoff: u64,
762
763 file_reader: *Io.File.Reader,
722764 index: usize = 0,
723765
724766 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;
726768 defer it.index += 1;
727769
728 const size: u64 = if (it.elf_header.is_64) @sizeOf(Elf64_Shdr) else @sizeOf(Elf32_Shdr);
729 const offset = it.elf_header.shoff + size * it.index;
770 const size: u64 = if (it.is_64) @sizeOf(Elf64_Shdr) else @sizeOf(Elf32_Shdr);
771 const offset = it.shoff + size * it.index;
730772 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);
733775 }
734776};
735777
736778pub const SectionHeaderBufferIterator = struct {
737 elf_header: Header,
779 is_64: bool,
780 endian: Endian,
781 shnum: u16,
782 shoff: u64,
783
738784 buf: []const u8,
739785 index: usize = 0,
740786
741787 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;
743789 defer it.index += 1;
744790
745 const size: u64 = if (it.elf_header.is_64) @sizeOf(Elf64_Shdr) else @sizeOf(Elf32_Shdr);
746 const offset = it.elf_header.shoff + size * it.index;
791 const size: u64 = if (it.is_64) @sizeOf(Elf64_Shdr) else @sizeOf(Elf32_Shdr);
792 const offset = it.shoff + size * it.index;
747793 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);
751797 }
752798};
753799
754fn takeShdr(reader: *std.Io.Reader, elf_header: Header) !?Elf64_Shdr {
755 if (elf_header.is_64) {
756 const shdr = try reader.takeStruct(Elf64_Shdr, elf_header.endian);
800pub fn takeSectionHeader(reader: *Io.Reader, is_64: bool, endian: Endian) !Elf64_Shdr {
801 if (is_64) {
802 const shdr = try reader.takeStruct(Elf64_Shdr, endian);
757803 return shdr;
758804 }
759805
760 const shdr = try reader.takeStruct(Elf32_Shdr, elf_header.endian);
806 const shdr = try reader.takeStruct(Elf32_Shdr, endian);
761807 return .{
762808 .sh_name = shdr.sh_name,
763809 .sh_type = shdr.sh_type,
......@@ -772,6 +818,36 @@ fn takeShdr(reader: *std.Io.Reader, elf_header: Header) !?Elf64_Shdr {
772818 };
773819}
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
775851pub const EI = struct {
776852 pub const CLASS = 4;
777853 pub const DATA = 5;
lib/std/zig.zig+6-5
......@@ -6,6 +6,7 @@ const std = @import("std.zig");
66const tokenizer = @import("zig/tokenizer.zig");
77const assert = std.debug.assert;
88const Allocator = std.mem.Allocator;
9const Io = std.Io;
910const Writer = std.Io.Writer;
1011
1112pub const ErrorBundle = @import("zig/ErrorBundle.zig");
......@@ -52,9 +53,9 @@ pub const Color = enum {
5253 /// Assume stderr is a terminal.
5354 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 {
5657 return switch (color) {
57 .auto => std.Io.tty.detectConfig(std.fs.File.stderr()),
58 .auto => Io.tty.detectConfig(std.fs.File.stderr()),
5859 .on => .escape_codes,
5960 .off => .no_color,
6061 };
......@@ -323,7 +324,7 @@ pub const BuildId = union(enum) {
323324 try std.testing.expectError(error.InvalidBuildIdStyle, parse("yaddaxxx"));
324325 }
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 {
327328 switch (id) {
328329 .none, .fast, .uuid, .sha1, .md5 => {
329330 try writer.writeAll(@tagName(id));
......@@ -620,8 +621,8 @@ pub fn putAstErrorsIntoBundle(
620621 try wip_errors.addZirErrorMessages(zir, tree, tree.source, path);
621622}
622623
623pub fn resolveTargetQueryOrFatal(target_query: std.Target.Query) std.Target {
624 return std.zig.system.resolveTargetQuery(target_query) catch |err|
624pub fn resolveTargetQueryOrFatal(io: Io, target_query: std.Target.Query) std.Target {
625 return std.zig.system.resolveTargetQuery(io, target_query) catch |err|
625626 std.process.fatal("unable to resolve target: {s}", .{@errorName(err)});
626627}
627628
lib/std/zig/system.zig+221-411
......@@ -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
112pub const NativePaths = @import("system/NativePaths.zig");
213
314pub const windows = @import("system/windows.zig");
......@@ -199,14 +210,14 @@ pub const DetectError = error{
199210 OSVersionDetectionFail,
200211 Unexpected,
201212 ProcessNotFound,
202};
213} || Io.Cancelable;
203214
204215/// Given a `Target.Query`, which specifies in detail which parts of the
205216/// target should be detected natively, which should be standard or default,
206217/// and which are provided explicitly, this function resolves the native
207218/// components by detecting the native system, and then resolves
208219/// standard/default parts relative to that.
209pub fn resolveTargetQuery(query: Target.Query) DetectError!Target {
220pub fn resolveTargetQuery(io: Io, query: Target.Query) DetectError!Target {
210221 // Until https://github.com/ziglang/zig/issues/4592 is implemented (support detecting the
211222 // native CPU architecture as being different than the current target), we use this:
212223 const query_cpu_arch = query.cpu_arch orelse builtin.cpu.arch;
......@@ -411,7 +422,33 @@ pub fn resolveTargetQuery(query: Target.Query) DetectError!Target {
411422 query.cpu_features_sub,
412423 );
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
416453 // These CPU feature hacks have to come after ABI detection.
417454 {
......@@ -505,54 +542,16 @@ fn detectNativeCpuAndFeatures(cpu_arch: Target.Cpu.Arch, os: Target.Os, query: T
505542 return null;
506543}
507544
508pub 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};
545pub const AbiAndDynamicLinkerFromFileError = error{};
526546
527547pub fn abiAndDynamicLinkerFromFile(
528 file: fs.File,
548 file_reader: *Io.File.Reader,
549 header: *const elf.Header,
529550 cpu: Target.Cpu,
530551 os: Target.Os,
531552 ld_info_list: []const LdInfo,
532553 query: Target.Query,
533554) 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
556555 var result: Target = .{
557556 .cpu = cpu,
558557 .os = os,
......@@ -563,167 +562,87 @@ pub fn abiAndDynamicLinkerFromFile(
563562 var rpath_offset: ?u64 = null; // Found inside PT_DYNAMIC
564563 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;
570565 var got_dyn_section: bool = false;
571
572 while (ph_i < phnum) {
573 // Reserve some bytes so that we can deref the 64-bit struct fields
574 // even when the ELF file is 32-bits.
575 const ph_reserve: usize = @sizeOf(elf.Elf64_Phdr) - @sizeOf(elf.Elf32_Phdr);
576 const ph_read_byte_len = try preadAtLeast(file, ph_buf[0 .. ph_buf.len - ph_reserve], phoff, phentsize);
577 var ph_buf_i: usize = 0;
578 while (ph_buf_i < ph_read_byte_len and ph_i < phnum) : ({
579 ph_i += 1;
580 phoff += phentsize;
581 ph_buf_i += phentsize;
582 }) {
583 const ph32: *elf.Elf32_Phdr = @ptrCast(@alignCast(&ph_buf[ph_buf_i]));
584 const ph64: *elf.Elf64_Phdr = @ptrCast(@alignCast(&ph_buf[ph_buf_i]));
585 const p_type = elfInt(is_64, need_bswap, ph32.p_type, ph64.p_type);
586 switch (p_type) {
587 elf.PT_INTERP => {
588 got_dyn_section = true;
589
590 if (look_for_ld) {
591 const p_offset = elfInt(is_64, need_bswap, ph32.p_offset, ph64.p_offset);
592 const p_filesz = elfInt(is_64, need_bswap, ph32.p_filesz, ph64.p_filesz);
593 if (p_filesz > result.dynamic_linker.buffer.len) return error.NameTooLong;
594 const filesz: usize = @intCast(p_filesz);
595 _ = try preadAtLeast(file, result.dynamic_linker.buffer[0..filesz], p_offset, filesz);
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 }
566 {
567 var it = header.iterateProgramHeaders(file_reader);
568 while (try it.next()) |phdr| switch (phdr.p_type) {
569 elf.PT_INTERP => {
570 got_dyn_section = true;
571
572 if (look_for_ld) {
573 const p_filesz = phdr.p_filesz;
574 if (p_filesz > result.dynamic_linker.buffer.len) return error.NameTooLong;
575 const filesz: usize = @intCast(p_filesz);
576 try file_reader.seekTo(phdr.p_offset);
577 try file_reader.interface.readSliceAll(result.dynamic_linker.buffer[0..filesz]);
578 // PT_INTERP includes a null byte in filesz.
579 const len = filesz - 1;
580 // dynamic_linker.max_byte is "max", not "len".
581 // We know it will fit in u8 because we check against dynamic_linker.buffer.len above.
582 result.dynamic_linker.len = @intCast(len);
583
584 // Use it to determine ABI.
585 const full_ld_path = result.dynamic_linker.buffer[0..len];
586 for (ld_info_list) |ld_info| {
587 const standard_ld_basename = fs.path.basename(ld_info.ld.get().?);
588 if (std.mem.endsWith(u8, full_ld_path, standard_ld_basename)) {
589 result.abi = ld_info.abi;
590 break;
610591 }
611592 }
612 },
613 // We only need this for detecting glibc version.
614 elf.PT_DYNAMIC => {
615 got_dyn_section = true;
616
617 if (builtin.target.os.tag == .linux and result.isGnuLibC() and
618 query.glibc_version == null)
619 {
620 var dyn_off = elfInt(is_64, need_bswap, ph32.p_offset, ph64.p_offset);
621 const p_filesz = elfInt(is_64, need_bswap, ph32.p_filesz, ph64.p_filesz);
622 const dyn_size: usize = if (is_64) @sizeOf(elf.Elf64_Dyn) else @sizeOf(elf.Elf32_Dyn);
623 const dyn_num = p_filesz / dyn_size;
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 }
593 }
594 },
595 // We only need this for detecting glibc version.
596 elf.PT_DYNAMIC => {
597 got_dyn_section = true;
598
599 if (builtin.target.os.tag == .linux and result.isGnuLibC() and query.glibc_version == null) {
600 var dyn_it = header.iterateDynamicSection(file_reader, phdr.p_offset, phdr.p_filesz);
601 while (try dyn_it.next()) |dyn| {
602 if (dyn.d_tag == elf.DT_RUNPATH) {
603 rpath_offset = dyn.d_val;
604 break;
651605 }
652606 }
653 },
654 else => continue,
655 }
656 }
607 }
608 },
609 else => continue,
610 };
657611 }
658612
659613 if (!got_dyn_section) {
660614 return error.StaticElfFile;
661615 }
662616
663 if (builtin.target.os.tag == .linux and result.isGnuLibC() and
664 query.glibc_version == null)
665 {
666 const shstrndx = elfInt(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx);
667
668 var shoff = elfInt(is_64, need_bswap, hdr32.e_shoff, hdr64.e_shoff);
669 const shentsize = elfInt(is_64, need_bswap, hdr32.e_shentsize, hdr64.e_shentsize);
670 const str_section_off = shoff + @as(u64, shentsize) * @as(u64, shstrndx);
671
672 var sh_buf: [16 * @sizeOf(elf.Elf64_Shdr)]u8 align(@alignOf(elf.Elf64_Shdr)) = undefined;
673 if (sh_buf.len < shentsize) return error.InvalidElfFile;
674
675 _ = try preadAtLeast(file, &sh_buf, str_section_off, shentsize);
676 const shstr32: *elf.Elf32_Shdr = @ptrCast(@alignCast(&sh_buf));
677 const shstr64: *elf.Elf64_Shdr = @ptrCast(@alignCast(&sh_buf));
678 const shstrtab_off = elfInt(is_64, need_bswap, shstr32.sh_offset, shstr64.sh_offset);
679 const shstrtab_size = elfInt(is_64, need_bswap, shstr32.sh_size, shstr64.sh_size);
680 var strtab_buf: [4096:0]u8 = undefined;
681 const shstrtab_len = @min(shstrtab_size, strtab_buf.len);
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
617 if (builtin.target.os.tag == .linux and result.isGnuLibC() and query.glibc_version == null) {
618 const str_section_off = header.shoff + @as(u64, header.shentsize) * @as(u64, header.shstrndx);
619 try file_reader.seekTo(str_section_off);
620 const shstr = try elf.takeSectionHeader(&file_reader.interface, header.is_64, header.endian);
621 var strtab_buf: [4096]u8 = undefined;
622 const shstrtab = strtab_buf[0..@min(shstr.sh_size, strtab_buf.len)];
623 try file_reader.seekTo(shstr.sh_offset);
624 try file_reader.interface.readSliceAll(shstrtab);
625 const dynstr: ?struct { offset: u64, size: u64 } = find_dyn_str: {
626 var it = header.iterateSectionHeaders(&file_reader.interface);
627 while (it.next()) |shdr| {
628 const end = mem.findScalarPos(u8, shstrtab, shdr.sh_name, 0) orelse continue;
629 const sh_name = shstrtab[shdr.sh_name..end :0];
630 if (mem.eql(u8, sh_name, ".dynstr")) break :find_dyn_str .{
631 .offset = shdr.sh_offset,
632 .size = shdr.sh_size,
633 };
634 } else break :find_dyn_str null;
635 };
716636 if (dynstr) |ds| {
717637 if (rpath_offset) |rpoff| {
718638 if (rpoff > ds.size) return error.InvalidElfFile;
719639 const rpoff_file = ds.offset + rpoff;
720640 const rp_max_size = ds.size - rpoff;
721641
722 const strtab_len = @min(rp_max_size, strtab_buf.len);
723 const strtab_read_len = try preadAtLeast(file, &strtab_buf, rpoff_file, strtab_len);
724 const strtab = strtab_buf[0..strtab_read_len];
642 try file_reader.seekTo(rpoff_file);
643 const rpath_list = try file_reader.interface.takeSentinel(0);
644 if (rpath_list.len > rp_max_size) return error.StreamTooLong;
725645
726 const rpath_list = mem.sliceTo(strtab, 0);
727646 var it = mem.tokenizeScalar(u8, rpath_list, ':');
728647 while (it.next()) |rpath| {
729648 if (glibcVerFromRPath(rpath)) |ver| {
......@@ -845,7 +764,7 @@ test glibcVerFromLinkName {
845764 try std.testing.expectError(error.InvalidGnuLibCVersion, glibcVerFromLinkName("ld-2.37.4.5.so", "ld-"));
846765}
847766
848fn glibcVerFromRPath(rpath: []const u8) !std.SemanticVersion {
767fn glibcVerFromRPath(io: Io, rpath: []const u8) !std.SemanticVersion {
849768 var dir = fs.cwd().openDir(rpath, .{}) catch |err| switch (err) {
850769 error.NameTooLong => unreachable,
851770 error.InvalidUtf8 => unreachable, // WASI only
......@@ -879,7 +798,7 @@ fn glibcVerFromRPath(rpath: []const u8) !std.SemanticVersion {
879798 // .dynstr section, and finding the max version number of symbols
880799 // that start with "GLIBC_2.".
881800 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) {
883802 error.NameTooLong => unreachable,
884803 error.InvalidUtf8 => unreachable, // WASI only
885804 error.InvalidWtf8 => unreachable, // Windows only
......@@ -913,16 +832,20 @@ fn glibcVerFromRPath(rpath: []const u8) !std.SemanticVersion {
913832 error.Unexpected,
914833 => |e| return e,
915834 };
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) {
919842 error.InvalidElfMagic,
920843 error.InvalidElfEndian,
921844 error.InvalidElfClass,
922845 error.InvalidElfFile,
923846 error.InvalidElfVersion,
924847 error.InvalidGnuLibCVersion,
925 error.UnexpectedEndOfFile,
848 error.EndOfStream,
926849 => return error.GLibCNotFound,
927850
928851 error.SystemResources,
......@@ -934,88 +857,34 @@ fn glibcVerFromRPath(rpath: []const u8) !std.SemanticVersion {
934857 };
935858}
936859
937fn glibcVerFromSoFile(file: fs.File) !std.SemanticVersion {
938 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 align(@alignOf(elf.Elf64_Ehdr)) = undefined;
939 _ = try preadAtLeast(file, &hdr_buf, 0, hdr_buf.len);
940 const hdr32: *elf.Elf32_Ehdr = @ptrCast(&hdr_buf);
941 const hdr64: *elf.Elf64_Ehdr = @ptrCast(&hdr_buf);
942 if (!mem.eql(u8, hdr32.e_ident[0..4], elf.MAGIC)) return error.InvalidElfMagic;
943 const elf_endian: std.builtin.Endian = switch (hdr32.e_ident[elf.EI.DATA]) {
944 elf.ELFDATA2LSB => .little,
945 elf.ELFDATA2MSB => .big,
946 else => return error.InvalidElfEndian,
947 };
948 const need_bswap = elf_endian != native_endian;
949 if (hdr32.e_ident[elf.EI.VERSION] != 1) return error.InvalidElfVersion;
950
951 const is_64 = switch (hdr32.e_ident[elf.EI.CLASS]) {
952 elf.ELFCLASS32 => false,
953 elf.ELFCLASS64 => true,
954 else => return error.InvalidElfClass,
860fn glibcVerFromSoFile(file_reader: *Io.File.Reader) !std.SemanticVersion {
861 const header = try elf.Header.read(&file_reader.interface);
862 const str_section_off = header.shoff + @as(u64, header.shentsize) * @as(u64, header.shstrndx);
863 try file_reader.seekTo(str_section_off);
864 const shstr = try elf.takeSectionHeader(&file_reader.interface, header.is_64, header.endian);
865 var strtab_buf: [4096]u8 = undefined;
866 const shstrtab = strtab_buf[0..@min(shstr.sh_size, strtab_buf.len)];
867 try file_reader.seekTo(shstr.sh_offset);
868 try file_reader.interface.readSliceAll(shstrtab);
869 const dynstr: struct { offset: u64, size: u64 } = find_dyn_str: {
870 var it = header.iterateSectionHeaders(&file_reader.interface);
871 while (it.next()) |shdr| {
872 const end = mem.findScalarPos(u8, shstrtab, shdr.sh_name, 0) orelse continue;
873 const sh_name = shstrtab[shdr.sh_name..end :0];
874 if (mem.eql(u8, sh_name, ".dynstr")) break :find_dyn_str .{
875 .offset = shdr.sh_offset,
876 .size = shdr.sh_size,
877 };
878 } else return error.InvalidGnuLibCVersion;
955879 };
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
1003881 // Here we loop over all the strings in the dynstr string table, assuming that any
1004882 // strings that start with "GLIBC_2." indicate the existence of such a glibc version,
1005883 // 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);
1017884 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| {
1019888 if (mem.startsWith(u8, s, "GLIBC_2.")) {
1020889 const chopped = s["GLIBC_".len..];
1021890 const ver = Target.Query.parseVersion(chopped) catch |err| switch (err) {
......@@ -1028,6 +897,7 @@ fn glibcVerFromSoFile(file: fs.File) !std.SemanticVersion {
1028897 }
1029898 }
1030899 }
900
1031901 return max_ver;
1032902}
1033903
......@@ -1044,11 +914,7 @@ fn glibcVerFromSoFile(file: fs.File) !std.SemanticVersion {
1044914/// answer to these questions, or if there is a shebang line, then it chases the referenced
1045915/// file recursively. If that does not provide the answer, then the function falls back to
1046916/// defaults.
1047fn detectAbiAndDynamicLinker(
1048 cpu: Target.Cpu,
1049 os: Target.Os,
1050 query: Target.Query,
1051) DetectError!Target {
917fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Target.Query) !Target {
1052918 const native_target_has_ld = comptime Target.DynamicLinker.kind(builtin.os.tag) != .none;
1053919 const is_linux = builtin.target.os.tag == .linux;
1054920 const is_illumos = builtin.target.os.tag == .illumos;
......@@ -1111,49 +977,52 @@ fn detectAbiAndDynamicLinker(
1111977
1112978 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
1114996 // Best case scenario: the executable is dynamically linked, and we can iterate
1115997 // over our own shared objects and find a dynamic linker.
1116 const elf_file = elf_file: {
1117 // This block looks for a shebang line in /usr/bin/env,
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,
1119 // doing the same logic recursively in case it finds another shebang line.
998 const header = elf_file: {
999 // This block looks for a shebang line in "/usr/bin/env". If it finds
1000 // one, then instead of using "/usr/bin/env" as the ELF file to examine,
1001 // it uses the file it references instead, doing the same logic
1002 // recursively in case it finds another shebang line.
11201003
11211004 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 a
1123 // reasonably reliable path to start with.
1005 // Since /usr/bin/env is hard-coded into the shebang line of many
1006 // portable scripts, it's a reasonably reliable path to start with.
11241007 else => "/usr/bin/env",
11251008 // Haiku does not have a /usr root directory.
11261009 .haiku => "/bin/env",
11271010 };
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;
11411012 while (true) {
1142 // Interpreter path can be relative on Linux, but
1143 // for simplicity we are asserting it is an absolute path.
11441013 const file = fs.openFileAbsolute(file_name, .{}) catch |err| switch (err) {
1145 error.NoSpaceLeft => unreachable,
1146 error.NameTooLong => unreachable,
1147 error.PathAlreadyExists => unreachable,
1148 error.SharingViolation => unreachable,
1149 error.InvalidUtf8 => unreachable, // WASI only
1150 error.InvalidWtf8 => unreachable, // Windows only
1151 error.BadPathName => unreachable,
1152 error.PipeBusy => unreachable,
1153 error.FileLocksNotSupported => unreachable,
1154 error.WouldBlock => unreachable,
1155 error.FileBusy => unreachable, // opened without write permissions
1156 error.AntivirusInterference => unreachable, // Windows-only error
1014 error.NoSpaceLeft => return error.Unexpected,
1015 error.NameTooLong => return error.Unexpected,
1016 error.PathAlreadyExists => return error.Unexpected,
1017 error.SharingViolation => return error.Unexpected,
1018 error.InvalidUtf8 => return error.Unexpected, // WASI only
1019 error.InvalidWtf8 => return error.Unexpected, // Windows only
1020 error.BadPathName => return error.Unexpected,
1021 error.PipeBusy => return error.Unexpected,
1022 error.FileLocksNotSupported => return error.Unexpected,
1023 error.WouldBlock => return error.Unexpected,
1024 error.FileBusy => return error.Unexpected, // opened without write permissions
1025 error.AntivirusInterference => return error.Unexpected, // Windows-only error
11571026
11581027 error.IsDir,
11591028 error.NotDir,
......@@ -1164,66 +1033,58 @@ fn detectAbiAndDynamicLinker(
11641033 error.NetworkNotFound,
11651034 error.FileTooBig,
11661035 error.Unexpected,
1167 => |e| {
1168 std.log.warn("Encountered error: {s}, falling back to default ABI and dynamic linker.", .{@errorName(e)});
1169 return defaultAbiAndDynamicLinker(cpu, os, query);
1170 },
1036 => return error.UnableToOpenElfFile,
11711037
11721038 else => |e| return e,
11731039 };
11741040 var is_elf_file = false;
1175 defer if (is_elf_file == false) file.close();
1176
1177 // Shortest working interpreter path is "#!/i" (4)
1178 // (interpreter is "/i", assuming all paths are absolute, like in above comment).
1179 // ELF magic number length is also 4.
1180 //
1181 // If file is shorter than that, it is definitely not ELF file
1182 // nor file with "shebang" line.
1183 const min_len: usize = 4;
1184
1185 const len = preadAtLeast(file, &buffer, 0, min_len) catch |err| switch (err) {
1186 error.UnexpectedEndOfFile,
1187 error.UnableToReadElfFile,
1188 error.ProcessNotFound,
1189 => return defaultAbiAndDynamicLinker(cpu, os, query),
1041 defer if (!is_elf_file) file.close();
1042
1043 file_reader = .initAdapted(file, io, &file_reader_buffer);
1044 file_name = undefined; // it aliases file_reader_buffer
1045
1046 const header = elf.Header.read(&file_reader.interface) catch |hdr_err| switch (hdr_err) {
1047 error.EndOfStream,
1048 error.InvalidElfMagic,
1049 => {
1050 const shebang_line = file_reader.interface.takeSentinel('\n') catch |err| switch (err) {
1051 error.ReadFailed => return file_reader.err.?,
1052 // It's neither an ELF file nor file with shebang line.
1053 error.EndOfStream, error.StreamTooLong => return error.UnhelpfulFile,
1054 };
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.?,
11921080 };
1193 const content = buffer[0..len];
1194
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 }
1081 is_elf_file = true;
1082 break :elf_file header;
12201083 }
12211084 };
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 find
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) {
1087 return abiAndDynamicLinkerFromFile(&file_reader, &header, cpu, os, ld_info_list, query) catch |err| switch (err) {
12271088 error.FileSystem,
12281089 error.SystemResources,
12291090 error.SymLinkLoop,
......@@ -1232,6 +1093,8 @@ fn detectAbiAndDynamicLinker(
12321093 error.ProcessNotFound,
12331094 => |e| return e,
12341095
1096 error.ReadFailed => return file_reader.err.?,
1097
12351098 error.UnableToReadElfFile,
12361099 error.InvalidElfClass,
12371100 error.InvalidElfVersion,
......@@ -1239,12 +1102,12 @@ fn detectAbiAndDynamicLinker(
12391102 error.InvalidElfFile,
12401103 error.InvalidElfMagic,
12411104 error.Unexpected,
1242 error.UnexpectedEndOfFile,
1105 error.EndOfStream,
12431106 error.NameTooLong,
12441107 error.StaticElfFile,
12451108 // Finally, we fall back on the standard path.
12461109 => |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});
12481111 return defaultAbiAndDynamicLinker(cpu, os, query);
12491112 },
12501113 };
......@@ -1269,59 +1132,6 @@ const LdInfo = struct {
12691132 abi: Target.Abi,
12701133};
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
13251135test {
13261136 _ = NativePaths;
13271137
src/main.zig+53-36
......@@ -1,5 +1,8 @@
1const std = @import("std");
21const builtin = @import("builtin");
2const native_os = builtin.os.tag;
3
4const std = @import("std");
5const Io = std.Io;
36const assert = std.debug.assert;
47const fs = std.fs;
58const mem = std.mem;
......@@ -10,7 +13,6 @@ const Color = std.zig.Color;
1013const warn = std.log.warn;
1114const ThreadPool = std.Thread.Pool;
1215const cleanExit = std.process.cleanExit;
13const native_os = builtin.os.tag;
1416const Cache = std.Build.Cache;
1517const Path = std.Build.Cache.Path;
1618const Directory = std.Build.Cache.Directory;
......@@ -245,26 +247,30 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
245247 }
246248 }
247249
250 var threaded: Io.Threaded = .init(gpa);
251 defer threaded.deinit();
252 const io = threaded.io();
253
248254 const cmd = args[1];
249255 const cmd_args = args[2..];
250256 if (mem.eql(u8, cmd, "build-exe")) {
251257 dev.check(.build_exe_command);
252 return buildOutputType(gpa, arena, args, .{ .build = .Exe });
258 return buildOutputType(gpa, arena, io, args, .{ .build = .Exe });
253259 } else if (mem.eql(u8, cmd, "build-lib")) {
254260 dev.check(.build_lib_command);
255 return buildOutputType(gpa, arena, args, .{ .build = .Lib });
261 return buildOutputType(gpa, arena, io, args, .{ .build = .Lib });
256262 } else if (mem.eql(u8, cmd, "build-obj")) {
257263 dev.check(.build_obj_command);
258 return buildOutputType(gpa, arena, args, .{ .build = .Obj });
264 return buildOutputType(gpa, arena, io, args, .{ .build = .Obj });
259265 } else if (mem.eql(u8, cmd, "test")) {
260266 dev.check(.test_command);
261 return buildOutputType(gpa, arena, args, .zig_test);
267 return buildOutputType(gpa, arena, io, args, .zig_test);
262268 } else if (mem.eql(u8, cmd, "test-obj")) {
263269 dev.check(.test_command);
264 return buildOutputType(gpa, arena, args, .zig_test_obj);
270 return buildOutputType(gpa, arena, io, args, .zig_test_obj);
265271 } else if (mem.eql(u8, cmd, "run")) {
266272 dev.check(.run_command);
267 return buildOutputType(gpa, arena, args, .run);
273 return buildOutputType(gpa, arena, io, args, .run);
268274 } else if (mem.eql(u8, cmd, "dlltool") or
269275 mem.eql(u8, cmd, "ranlib") or
270276 mem.eql(u8, cmd, "lib") or
......@@ -274,7 +280,7 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
274280 return process.exit(try llvmArMain(arena, args));
275281 } else if (mem.eql(u8, cmd, "build")) {
276282 dev.check(.build_command);
277 return cmdBuild(gpa, arena, cmd_args);
283 return cmdBuild(gpa, arena, io, cmd_args);
278284 } else if (mem.eql(u8, cmd, "clang") or
279285 mem.eql(u8, cmd, "-cc1") or mem.eql(u8, cmd, "-cc1as"))
280286 {
......@@ -288,16 +294,16 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
288294 return process.exit(try lldMain(arena, args, true));
289295 } else if (mem.eql(u8, cmd, "cc")) {
290296 dev.check(.cc_command);
291 return buildOutputType(gpa, arena, args, .cc);
297 return buildOutputType(gpa, arena, io, args, .cc);
292298 } else if (mem.eql(u8, cmd, "c++")) {
293299 dev.check(.cc_command);
294 return buildOutputType(gpa, arena, args, .cpp);
300 return buildOutputType(gpa, arena, io, args, .cpp);
295301 } else if (mem.eql(u8, cmd, "translate-c")) {
296302 dev.check(.translate_c_command);
297 return buildOutputType(gpa, arena, args, .translate_c);
303 return buildOutputType(gpa, arena, io, args, .translate_c);
298304 } else if (mem.eql(u8, cmd, "rc")) {
299305 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, .{
301307 .cmd_name = "resinator",
302308 .root_src_path = "resinator/main.zig",
303309 .depend_on_aro = true,
......@@ -308,20 +314,20 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
308314 dev.check(.fmt_command);
309315 return @import("fmt.zig").run(gpa, arena, cmd_args);
310316 } else if (mem.eql(u8, cmd, "objcopy")) {
311 return jitCmd(gpa, arena, cmd_args, .{
317 return jitCmd(gpa, arena, io, cmd_args, .{
312318 .cmd_name = "objcopy",
313319 .root_src_path = "objcopy.zig",
314320 });
315321 } else if (mem.eql(u8, cmd, "fetch")) {
316322 return cmdFetch(gpa, arena, cmd_args);
317323 } else if (mem.eql(u8, cmd, "libc")) {
318 return jitCmd(gpa, arena, cmd_args, .{
324 return jitCmd(gpa, arena, io, cmd_args, .{
319325 .cmd_name = "libc",
320326 .root_src_path = "libc.zig",
321327 .prepend_zig_lib_dir_path = true,
322328 });
323329 } else if (mem.eql(u8, cmd, "std")) {
324 return jitCmd(gpa, arena, cmd_args, .{
330 return jitCmd(gpa, arena, io, cmd_args, .{
325331 .cmd_name = "std",
326332 .root_src_path = "std-docs.zig",
327333 .prepend_zig_lib_dir_path = true,
......@@ -332,7 +338,7 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
332338 return cmdInit(gpa, arena, cmd_args);
333339 } else if (mem.eql(u8, cmd, "targets")) {
334340 dev.check(.targets_command);
335 const host = std.zig.resolveTargetQueryOrFatal(.{});
341 const host = std.zig.resolveTargetQueryOrFatal(io, .{});
336342 var stdout_writer = fs.File.stdout().writer(&stdout_buffer);
337343 try @import("print_targets.zig").cmdTargets(arena, cmd_args, &stdout_writer.interface, &host);
338344 return stdout_writer.interface.flush();
......@@ -351,7 +357,7 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
351357 );
352358 return stdout_writer.interface.flush();
353359 } else if (mem.eql(u8, cmd, "reduce")) {
354 return jitCmd(gpa, arena, cmd_args, .{
360 return jitCmd(gpa, arena, io, cmd_args, .{
355361 .cmd_name = "reduce",
356362 .root_src_path = "reduce.zig",
357363 });
......@@ -364,7 +370,7 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
364370 } else if (mem.eql(u8, cmd, "ast-check")) {
365371 return cmdAstCheck(arena, cmd_args);
366372 } else if (mem.eql(u8, cmd, "detect-cpu")) {
367 return cmdDetectCpu(cmd_args);
373 return cmdDetectCpu(io, cmd_args);
368374 } else if (build_options.enable_debug_extensions and mem.eql(u8, cmd, "changelist")) {
369375 return cmdChangelist(arena, cmd_args);
370376 } else if (build_options.enable_debug_extensions and mem.eql(u8, cmd, "dump-zir")) {
......@@ -792,6 +798,7 @@ const CliModule = struct {
792798fn buildOutputType(
793799 gpa: Allocator,
794800 arena: Allocator,
801 io: Io,
795802 all_args: []const []const u8,
796803 arg_mode: ArgMode,
797804) !void {
......@@ -3017,7 +3024,7 @@ fn buildOutputType(
30173024 create_module.opts.emit_bin = emit_bin != .no;
30183025 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);
30213028 for (create_module.modules.keys(), create_module.modules.values()) |key, cli_mod| {
30223029 if (cli_mod.resolved == null)
30233030 fatal("module '{s}' declared but not used", .{key});
......@@ -3545,6 +3552,7 @@ fn buildOutputType(
35453552 var stdin_reader = fs.File.stdin().reader(&stdin_buffer);
35463553 var stdout_writer = fs.File.stdout().writer(&stdout_buffer);
35473554 try serve(
3555 io,
35483556 comp,
35493557 &stdin_reader.interface,
35503558 &stdout_writer.interface,
......@@ -3571,6 +3579,7 @@ fn buildOutputType(
35713579 var output = conn.stream.writer(&stdout_buffer);
35723580
35733581 try serve(
3582 io,
35743583 comp,
35753584 input.interface(),
35763585 &output.interface,
......@@ -3646,6 +3655,7 @@ fn buildOutputType(
36463655 comp,
36473656 gpa,
36483657 arena,
3658 io,
36493659 test_exec_args.items,
36503660 self_exe_path,
36513661 arg_mode,
......@@ -3704,6 +3714,7 @@ const CreateModule = struct {
37043714fn createModule(
37053715 gpa: Allocator,
37063716 arena: Allocator,
3717 io: Io,
37073718 create_module: *CreateModule,
37083719 index: usize,
37093720 parent: ?*Package.Module,
......@@ -3777,7 +3788,7 @@ fn createModule(
37773788 }
37783789
37793790 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);
37813792 break :t .{
37823793 .result = target,
37833794 .is_native_os = target_query.isNativeOs(),
......@@ -4022,7 +4033,7 @@ fn createModule(
40224033 for (cli_mod.deps) |dep| {
40234034 const dep_index = create_module.modules.getIndex(dep.value) orelse
40244035 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);
40264037 try mod.deps.put(arena, dep.key, dep_mod);
40274038 }
40284039
......@@ -4038,9 +4049,10 @@ fn saveState(comp: *Compilation, incremental: bool) void {
40384049}
40394050
40404051fn serve(
4052 io: Io,
40414053 comp: *Compilation,
4042 in: *std.Io.Reader,
4043 out: *std.Io.Writer,
4054 in: *Io.Reader,
4055 out: *Io.Writer,
40444056 test_exec_args: []const ?[]const u8,
40454057 self_exe_path: ?[]const u8,
40464058 arg_mode: ArgMode,
......@@ -4090,7 +4102,7 @@ fn serve(
40904102 defer arena_instance.deinit();
40914103 const arena = arena_instance.allocator();
40924104 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);
40944106 defer output.deinit(gpa);
40954107
40964108 if (file_system_inputs.items.len != 0) {
......@@ -4126,6 +4138,7 @@ fn serve(
41264138 // comp,
41274139 // gpa,
41284140 // arena,
4141 // io,
41294142 // test_exec_args,
41304143 // self_exe_path.?,
41314144 // arg_mode,
......@@ -4280,6 +4293,7 @@ fn runOrTest(
42804293 comp: *Compilation,
42814294 gpa: Allocator,
42824295 arena: Allocator,
4296 io: Io,
42834297 test_exec_args: []const ?[]const u8,
42844298 self_exe_path: []const u8,
42854299 arg_mode: ArgMode,
......@@ -4334,7 +4348,7 @@ fn runOrTest(
43344348 std.debug.lockStdErr();
43354349 const err = process.execve(gpa, argv.items, &env_map);
43364350 std.debug.unlockStdErr();
4337 try warnAboutForeignBinaries(arena, arg_mode, target, link_libc);
4351 try warnAboutForeignBinaries(io, arena, arg_mode, target, link_libc);
43384352 const cmd = try std.mem.join(arena, " ", argv.items);
43394353 fatal("the following command failed to execve with '{s}':\n{s}", .{ @errorName(err), cmd });
43404354 } else if (process.can_spawn) {
......@@ -4355,7 +4369,7 @@ fn runOrTest(
43554369 break :t child.spawnAndWait();
43564370 };
43574371 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);
43594373 const cmd = try std.mem.join(arena, " ", argv.items);
43604374 fatal("the following command failed with '{s}':\n{s}", .{ @errorName(err), cmd });
43614375 };
......@@ -4594,11 +4608,12 @@ fn cmdTranslateC(
45944608pub fn translateC(
45954609 gpa: Allocator,
45964610 arena: Allocator,
4611 io: Io,
45974612 argv: []const []const u8,
45984613 prog_node: std.Progress.Node,
45994614 capture: ?*[]u8,
46004615) !void {
4601 try jitCmd(gpa, arena, argv, .{
4616 try jitCmd(gpa, arena, io, argv, .{
46024617 .cmd_name = "translate-c",
46034618 .root_src_path = "translate-c/main.zig",
46044619 .depend_on_aro = true,
......@@ -4755,7 +4770,7 @@ test sanitizeExampleName {
47554770 try std.testing.expectEqualStrings("test_project", try sanitizeExampleName(arena, "test project"));
47564771}
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 {
47594774 dev.check(.build_command);
47604775
47614776 var build_file: ?[]const u8 = null;
......@@ -4983,7 +4998,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
49834998 .arch_os_abi = triple,
49844999 });
49855000 break :t .{
4986 .result = std.zig.resolveTargetQueryOrFatal(target_query),
5001 .result = std.zig.resolveTargetQueryOrFatal(io, target_query),
49875002 .is_native_os = false,
49885003 .is_native_abi = false,
49895004 .is_explicit_dynamic_linker = false,
......@@ -4991,7 +5006,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
49915006 }
49925007 }
49935008 break :t .{
4994 .result = std.zig.resolveTargetQueryOrFatal(.{}),
5009 .result = std.zig.resolveTargetQueryOrFatal(io, .{}),
49955010 .is_native_os = true,
49965011 .is_native_abi = true,
49975012 .is_explicit_dynamic_linker = false,
......@@ -5400,6 +5415,7 @@ const JitCmdOptions = struct {
54005415fn jitCmd(
54015416 gpa: Allocator,
54025417 arena: Allocator,
5418 io: Io,
54035419 args: []const []const u8,
54045420 options: JitCmdOptions,
54055421) !void {
......@@ -5412,7 +5428,7 @@ fn jitCmd(
54125428
54135429 const target_query: std.Target.Query = .{};
54145430 const resolved_target: Package.Module.ResolvedTarget = .{
5415 .result = std.zig.resolveTargetQueryOrFatal(target_query),
5431 .result = std.zig.resolveTargetQueryOrFatal(io, target_query),
54165432 .is_native_os = true,
54175433 .is_native_abi = true,
54185434 .is_explicit_dynamic_linker = false,
......@@ -6209,7 +6225,7 @@ fn cmdAstCheck(
62096225 }
62106226}
62116227
6212fn cmdDetectCpu(args: []const []const u8) !void {
6228fn cmdDetectCpu(io: Io, args: []const []const u8) !void {
62136229 dev.check(.detect_cpu_command);
62146230
62156231 const detect_cpu_usage =
......@@ -6254,7 +6270,7 @@ fn cmdDetectCpu(args: []const []const u8) !void {
62546270 const cpu = try detectNativeCpuWithLLVM(builtin.cpu.arch, name, features);
62556271 try printCpu(cpu);
62566272 } else {
6257 const host_target = std.zig.resolveTargetQueryOrFatal(.{});
6273 const host_target = std.zig.resolveTargetQueryOrFatal(io, .{});
62586274 try printCpu(host_target.cpu);
62596275 }
62606276}
......@@ -6521,13 +6537,14 @@ fn prefixedIntArg(arg: []const u8, prefix: []const u8) ?u64 {
65216537}
65226538
65236539fn warnAboutForeignBinaries(
6540 io: Io,
65246541 arena: Allocator,
65256542 arg_mode: ArgMode,
65266543 target: *const std.Target,
65276544 link_libc: bool,
65286545) !void {
65296546 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
65326549 switch (std.zig.system.getExternalExecutor(&host_target, target, .{ .link_libc = link_libc })) {
65336550 .native => return,
......@@ -7080,7 +7097,7 @@ fn cmdFetch(
70807097 try fixups.append_string_after_node.put(gpa, manifest.version_node, dependencies_text);
70817098 }
70827099
7083 var aw: std.Io.Writer.Allocating = .init(gpa);
7100 var aw: Io.Writer.Allocating = .init(gpa);
70847101 defer aw.deinit();
70857102 try ast.render(gpa, &aw.writer, fixups);
70867103 const rendered = aw.written();