Commit 066864a0bf
Changed files (12)
lib
compiler
std
Build
Target
zig
src
lib/compiler/build_runner.zig
@@ -38,6 +38,10 @@ pub fn main() !void {
const args = try process.argsAlloc(arena);
+ var threaded: std.Io.Threaded = .init(gpa);
+ defer threaded.deinit();
+ const io = threaded.io();
+
// skip my own exe name
var arg_idx: usize = 1;
@@ -68,6 +72,7 @@ pub fn main() !void {
};
var graph: std.Build.Graph = .{
+ .io = io,
.arena = arena,
.cache = .{
.gpa = arena,
lib/std/Build/Step/Options.zig
@@ -532,6 +532,8 @@ const Arg = struct {
test Options {
if (builtin.os.tag == .wasi) return error.SkipZigTest;
+ const io = std.testing.io;
+
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
defer arena.deinit();
@@ -546,7 +548,7 @@ test Options {
.global_cache_root = .{ .path = "test", .handle = std.fs.cwd() },
.host = .{
.query = .{},
- .result = try std.zig.system.resolveTargetQuery(.{}),
+ .result = try std.zig.system.resolveTargetQuery(io, .{}),
},
.zig_lib_directory = std.Build.Cache.Directory.cwd(),
.time_report = false,
lib/std/Build/WebServer.zig
@@ -516,6 +516,7 @@ pub fn serveTarFile(
}
fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.OptimizeMode) !Cache.Path {
+ const io = ws.graph.io;
const root_name = "build-web";
const arch_os_abi = "wasm32-freestanding";
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
};
const bin_name = try std.zig.binNameAlloc(arena, .{
.root_name = root_name,
- .target = &(std.zig.system.resolveTargetQuery(std.Build.parseTargetQuery(.{
+ .target = &(std.zig.system.resolveTargetQuery(io, std.Build.parseTargetQuery(.{
.arch_os_abi = arch_os_abi,
.cpu_features = cpu_features,
}) catch unreachable) catch unreachable),
lib/std/Io/net.zig
@@ -228,7 +228,7 @@ pub const IpAddress = union(enum) {
///
/// One bound `Socket` can be used to receive messages from multiple
/// different addresses.
- pub fn bind(address: IpAddress, io: Io, options: BindOptions) BindError!Socket {
+ pub fn bind(address: *const IpAddress, io: Io, options: BindOptions) BindError!Socket {
return io.vtable.ipBind(io.userdata, address, options);
}
lib/std/Io/Threaded.zig
@@ -1054,7 +1054,7 @@ fn nowWasi(userdata: ?*anyopaque, clock: Io.Timestamp.Clock) Io.Timestamp.Error!
fn sleepLinux(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {
const pool: *Pool = @ptrCast(@alignCast(userdata));
const clock_id: posix.clockid_t = clockToPosix(switch (timeout) {
- .none => .monotonic,
+ .none => .awake,
.duration => |d| d.clock,
.deadline => |d| d.clock,
});
@@ -1087,7 +1087,6 @@ fn sleepWindows(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {
const ms = ms: {
const duration_and_clock = (try timeout.toDurationFromNow(pool.io())) orelse
break :ms std.math.maxInt(windows.DWORD);
- if (duration_and_clock.clock != .monotonic) return error.UnsupportedClock;
break :ms std.math.lossyCast(windows.DWORD, duration_and_clock.duration.toMilliseconds());
};
windows.kernel32.Sleep(ms);
@@ -1132,8 +1131,6 @@ fn sleepPosix(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {
.sec = std.math.maxInt(sec_type),
.nsec = std.math.maxInt(nsec_type),
};
- // TODO check which clock nanosleep uses on this host
- // and return error.UnsupportedClock if it does not match
const ns = d.duration.nanoseconds;
break :t .{
.sec = @intCast(@divFloor(ns, std.time.ns_per_s)),
@@ -2046,9 +2043,9 @@ fn clockToPosix(clock: Io.Timestamp.Clock) posix.clockid_t {
fn clockToWasi(clock: Io.Timestamp.Clock) std.os.wasi.clockid_t {
return switch (clock) {
.realtime => .REALTIME,
- .monotonic => .MONOTONIC,
- .uptime => .MONOTONIC,
- .process_cputime_id => .PROCESS_CPUTIME_ID,
- .thread_cputime_id => .THREAD_CPUTIME_ID,
+ .awake => .MONOTONIC,
+ .boot => .MONOTONIC,
+ .cpu_process => .PROCESS_CPUTIME_ID,
+ .cpu_thread => .THREAD_CPUTIME_ID,
};
}
lib/std/Target/Query.zig
@@ -612,6 +612,8 @@ fn versionEqualOpt(a: ?SemanticVersion, b: ?SemanticVersion) bool {
}
test parse {
+ const io = std.testing.io;
+
if (builtin.target.isGnuLibC()) {
var query = try Query.parse(.{});
query.setGnuLibCVersion(2, 1, 1);
@@ -654,7 +656,7 @@ test parse {
.arch_os_abi = "x86_64-linux-gnu",
.cpu_features = "x86_64-sse-sse2-avx-cx8",
});
- const target = try std.zig.system.resolveTargetQuery(query);
+ const target = try std.zig.system.resolveTargetQuery(io, query);
try std.testing.expect(target.os.tag == .linux);
try std.testing.expect(target.abi == .gnu);
@@ -679,7 +681,7 @@ test parse {
.arch_os_abi = "arm-linux-musleabihf",
.cpu_features = "generic+v8a",
});
- const target = try std.zig.system.resolveTargetQuery(query);
+ const target = try std.zig.system.resolveTargetQuery(io, query);
try std.testing.expect(target.os.tag == .linux);
try std.testing.expect(target.abi == .musleabihf);
@@ -696,7 +698,7 @@ test parse {
.arch_os_abi = "aarch64-linux.3.10...4.4.1-gnu.2.27",
.cpu_features = "generic+v8a",
});
- const target = try std.zig.system.resolveTargetQuery(query);
+ const target = try std.zig.system.resolveTargetQuery(io, query);
try std.testing.expect(target.cpu.arch == .aarch64);
try std.testing.expect(target.os.tag == .linux);
@@ -719,7 +721,7 @@ test parse {
const query = try Query.parse(.{
.arch_os_abi = "aarch64-linux.3.10...4.4.1-android.30",
});
- const target = try std.zig.system.resolveTargetQuery(query);
+ const target = try std.zig.system.resolveTargetQuery(io, query);
try std.testing.expect(target.cpu.arch == .aarch64);
try std.testing.expect(target.os.tag == .linux);
@@ -740,7 +742,7 @@ test parse {
const query = try Query.parse(.{
.arch_os_abi = "x86-windows.xp...win8-msvc",
});
- const target = try std.zig.system.resolveTargetQuery(query);
+ const target = try std.zig.system.resolveTargetQuery(io, query);
try std.testing.expect(target.cpu.arch == .x86);
try std.testing.expect(target.os.tag == .windows);
lib/std/zig/system.zig
@@ -1,3 +1,14 @@
+const builtin = @import("builtin");
+const std = @import("../std.zig");
+const mem = std.mem;
+const elf = std.elf;
+const fs = std.fs;
+const assert = std.debug.assert;
+const Target = std.Target;
+const native_endian = builtin.cpu.arch.endian();
+const posix = std.posix;
+const Io = std.Io;
+
pub const NativePaths = @import("system/NativePaths.zig");
pub const windows = @import("system/windows.zig");
@@ -199,14 +210,14 @@ pub const DetectError = error{
OSVersionDetectionFail,
Unexpected,
ProcessNotFound,
-};
+} || Io.Cancelable;
/// Given a `Target.Query`, which specifies in detail which parts of the
/// target should be detected natively, which should be standard or default,
/// and which are provided explicitly, this function resolves the native
/// components by detecting the native system, and then resolves
/// standard/default parts relative to that.
-pub fn resolveTargetQuery(query: Target.Query) DetectError!Target {
+pub fn resolveTargetQuery(io: Io, query: Target.Query) DetectError!Target {
// Until https://github.com/ziglang/zig/issues/4592 is implemented (support detecting the
// native CPU architecture as being different than the current target), we use this:
const query_cpu_arch = query.cpu_arch orelse builtin.cpu.arch;
@@ -411,7 +422,33 @@ pub fn resolveTargetQuery(query: Target.Query) DetectError!Target {
query.cpu_features_sub,
);
- var result = try detectAbiAndDynamicLinker(cpu, os, query);
+ var result = detectAbiAndDynamicLinker(io, cpu, os, query) catch |err| switch (err) {
+ error.Canceled => |e| return e,
+ error.Unexpected => |e| return e,
+ error.WouldBlock => return error.Unexpected,
+ error.BrokenPipe => return error.Unexpected,
+ error.ConnectionResetByPeer => return error.Unexpected,
+ error.ConnectionTimedOut => return error.Unexpected,
+ error.NotOpenForReading => return error.Unexpected,
+ error.SocketUnconnected => return error.Unexpected,
+
+ error.AccessDenied,
+ error.ProcessNotFound,
+ error.SymLinkLoop,
+ error.ProcessFdQuotaExceeded,
+ error.SystemFdQuotaExceeded,
+ error.SystemResources,
+ error.IsDir,
+ error.DeviceBusy,
+ error.InputOutput,
+ error.LockViolation,
+
+ error.UnableToOpenElfFile,
+ error.UnhelpfulFile,
+ error.InvalidElfFile,
+ error.RelativeShebang,
+ => return defaultAbiAndDynamicLinker(cpu, os, query),
+ };
// These CPU feature hacks have to come after ABI detection.
{
@@ -505,54 +542,16 @@ fn detectNativeCpuAndFeatures(cpu_arch: Target.Cpu.Arch, os: Target.Os, query: T
return null;
}
-pub const AbiAndDynamicLinkerFromFileError = error{
- FileSystem,
- SystemResources,
- SymLinkLoop,
- ProcessFdQuotaExceeded,
- SystemFdQuotaExceeded,
- UnableToReadElfFile,
- InvalidElfClass,
- InvalidElfVersion,
- InvalidElfEndian,
- InvalidElfFile,
- InvalidElfMagic,
- Unexpected,
- UnexpectedEndOfFile,
- NameTooLong,
- ProcessNotFound,
- StaticElfFile,
-};
+pub const AbiAndDynamicLinkerFromFileError = error{};
pub fn abiAndDynamicLinkerFromFile(
- file: fs.File,
+ file_reader: *Io.File.Reader,
+ header: *const elf.Header,
cpu: Target.Cpu,
os: Target.Os,
ld_info_list: []const LdInfo,
query: Target.Query,
) AbiAndDynamicLinkerFromFileError!Target {
- var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 align(@alignOf(elf.Elf64_Ehdr)) = undefined;
- _ = try preadAtLeast(file, &hdr_buf, 0, hdr_buf.len);
- const hdr32: *elf.Elf32_Ehdr = @ptrCast(&hdr_buf);
- const hdr64: *elf.Elf64_Ehdr = @ptrCast(&hdr_buf);
- if (!mem.eql(u8, hdr32.e_ident[0..4], elf.MAGIC)) return error.InvalidElfMagic;
- const elf_endian: std.builtin.Endian = switch (hdr32.e_ident[elf.EI.DATA]) {
- elf.ELFDATA2LSB => .little,
- elf.ELFDATA2MSB => .big,
- else => return error.InvalidElfEndian,
- };
- const need_bswap = elf_endian != native_endian;
- if (hdr32.e_ident[elf.EI.VERSION] != 1) return error.InvalidElfVersion;
-
- const is_64 = switch (hdr32.e_ident[elf.EI.CLASS]) {
- elf.ELFCLASS32 => false,
- elf.ELFCLASS64 => true,
- else => return error.InvalidElfClass,
- };
- var phoff = elfInt(is_64, need_bswap, hdr32.e_phoff, hdr64.e_phoff);
- const phentsize = elfInt(is_64, need_bswap, hdr32.e_phentsize, hdr64.e_phentsize);
- const phnum = elfInt(is_64, need_bswap, hdr32.e_phnum, hdr64.e_phnum);
-
var result: Target = .{
.cpu = cpu,
.os = os,
@@ -563,167 +562,87 @@ pub fn abiAndDynamicLinkerFromFile(
var rpath_offset: ?u64 = null; // Found inside PT_DYNAMIC
const look_for_ld = query.dynamic_linker.get() == null;
- var ph_buf: [16 * @sizeOf(elf.Elf64_Phdr)]u8 align(@alignOf(elf.Elf64_Phdr)) = undefined;
- if (phentsize > @sizeOf(elf.Elf64_Phdr)) return error.InvalidElfFile;
-
- var ph_i: u16 = 0;
var got_dyn_section: bool = false;
-
- while (ph_i < phnum) {
- // Reserve some bytes so that we can deref the 64-bit struct fields
- // even when the ELF file is 32-bits.
- const ph_reserve: usize = @sizeOf(elf.Elf64_Phdr) - @sizeOf(elf.Elf32_Phdr);
- const ph_read_byte_len = try preadAtLeast(file, ph_buf[0 .. ph_buf.len - ph_reserve], phoff, phentsize);
- var ph_buf_i: usize = 0;
- while (ph_buf_i < ph_read_byte_len and ph_i < phnum) : ({
- ph_i += 1;
- phoff += phentsize;
- ph_buf_i += phentsize;
- }) {
- const ph32: *elf.Elf32_Phdr = @ptrCast(@alignCast(&ph_buf[ph_buf_i]));
- const ph64: *elf.Elf64_Phdr = @ptrCast(@alignCast(&ph_buf[ph_buf_i]));
- const p_type = elfInt(is_64, need_bswap, ph32.p_type, ph64.p_type);
- switch (p_type) {
- elf.PT_INTERP => {
- got_dyn_section = true;
-
- if (look_for_ld) {
- const p_offset = elfInt(is_64, need_bswap, ph32.p_offset, ph64.p_offset);
- const p_filesz = elfInt(is_64, need_bswap, ph32.p_filesz, ph64.p_filesz);
- if (p_filesz > result.dynamic_linker.buffer.len) return error.NameTooLong;
- const filesz: usize = @intCast(p_filesz);
- _ = try preadAtLeast(file, result.dynamic_linker.buffer[0..filesz], p_offset, filesz);
- // PT_INTERP includes a null byte in filesz.
- const len = filesz - 1;
- // dynamic_linker.max_byte is "max", not "len".
- // We know it will fit in u8 because we check against dynamic_linker.buffer.len above.
- result.dynamic_linker.len = @intCast(len);
-
- // Use it to determine ABI.
- const full_ld_path = result.dynamic_linker.buffer[0..len];
- for (ld_info_list) |ld_info| {
- const standard_ld_basename = fs.path.basename(ld_info.ld.get().?);
- if (std.mem.endsWith(u8, full_ld_path, standard_ld_basename)) {
- result.abi = ld_info.abi;
- break;
- }
+ {
+ var it = header.iterateProgramHeaders(file_reader);
+ while (try it.next()) |phdr| switch (phdr.p_type) {
+ elf.PT_INTERP => {
+ got_dyn_section = true;
+
+ if (look_for_ld) {
+ const p_filesz = phdr.p_filesz;
+ if (p_filesz > result.dynamic_linker.buffer.len) return error.NameTooLong;
+ const filesz: usize = @intCast(p_filesz);
+ try file_reader.seekTo(phdr.p_offset);
+ try file_reader.interface.readSliceAll(result.dynamic_linker.buffer[0..filesz]);
+ // PT_INTERP includes a null byte in filesz.
+ const len = filesz - 1;
+ // dynamic_linker.max_byte is "max", not "len".
+ // We know it will fit in u8 because we check against dynamic_linker.buffer.len above.
+ result.dynamic_linker.len = @intCast(len);
+
+ // Use it to determine ABI.
+ const full_ld_path = result.dynamic_linker.buffer[0..len];
+ for (ld_info_list) |ld_info| {
+ const standard_ld_basename = fs.path.basename(ld_info.ld.get().?);
+ if (std.mem.endsWith(u8, full_ld_path, standard_ld_basename)) {
+ result.abi = ld_info.abi;
+ break;
}
}
- },
- // We only need this for detecting glibc version.
- elf.PT_DYNAMIC => {
- got_dyn_section = true;
-
- if (builtin.target.os.tag == .linux and result.isGnuLibC() and
- query.glibc_version == null)
- {
- var dyn_off = elfInt(is_64, need_bswap, ph32.p_offset, ph64.p_offset);
- const p_filesz = elfInt(is_64, need_bswap, ph32.p_filesz, ph64.p_filesz);
- const dyn_size: usize = if (is_64) @sizeOf(elf.Elf64_Dyn) else @sizeOf(elf.Elf32_Dyn);
- const dyn_num = p_filesz / dyn_size;
- var dyn_buf: [16 * @sizeOf(elf.Elf64_Dyn)]u8 align(@alignOf(elf.Elf64_Dyn)) = undefined;
- var dyn_i: usize = 0;
- dyn: while (dyn_i < dyn_num) {
- // Reserve some bytes so that we can deref the 64-bit struct fields
- // even when the ELF file is 32-bits.
- const dyn_reserve: usize = @sizeOf(elf.Elf64_Dyn) - @sizeOf(elf.Elf32_Dyn);
- const dyn_read_byte_len = try preadAtLeast(
- file,
- dyn_buf[0 .. dyn_buf.len - dyn_reserve],
- dyn_off,
- dyn_size,
- );
- var dyn_buf_i: usize = 0;
- while (dyn_buf_i < dyn_read_byte_len and dyn_i < dyn_num) : ({
- dyn_i += 1;
- dyn_off += dyn_size;
- dyn_buf_i += dyn_size;
- }) {
- const dyn32: *elf.Elf32_Dyn = @ptrCast(@alignCast(&dyn_buf[dyn_buf_i]));
- const dyn64: *elf.Elf64_Dyn = @ptrCast(@alignCast(&dyn_buf[dyn_buf_i]));
- const tag = elfInt(is_64, need_bswap, dyn32.d_tag, dyn64.d_tag);
- const val = elfInt(is_64, need_bswap, dyn32.d_val, dyn64.d_val);
- if (tag == elf.DT_RUNPATH) {
- rpath_offset = val;
- break :dyn;
- }
- }
+ }
+ },
+ // We only need this for detecting glibc version.
+ elf.PT_DYNAMIC => {
+ got_dyn_section = true;
+
+ if (builtin.target.os.tag == .linux and result.isGnuLibC() and query.glibc_version == null) {
+ var dyn_it = header.iterateDynamicSection(file_reader, phdr.p_offset, phdr.p_filesz);
+ while (try dyn_it.next()) |dyn| {
+ if (dyn.d_tag == elf.DT_RUNPATH) {
+ rpath_offset = dyn.d_val;
+ break;
}
}
- },
- else => continue,
- }
- }
+ }
+ },
+ else => continue,
+ };
}
if (!got_dyn_section) {
return error.StaticElfFile;
}
- if (builtin.target.os.tag == .linux and result.isGnuLibC() and
- query.glibc_version == null)
- {
- const shstrndx = elfInt(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx);
-
- var shoff = elfInt(is_64, need_bswap, hdr32.e_shoff, hdr64.e_shoff);
- const shentsize = elfInt(is_64, need_bswap, hdr32.e_shentsize, hdr64.e_shentsize);
- const str_section_off = shoff + @as(u64, shentsize) * @as(u64, shstrndx);
-
- var sh_buf: [16 * @sizeOf(elf.Elf64_Shdr)]u8 align(@alignOf(elf.Elf64_Shdr)) = undefined;
- if (sh_buf.len < shentsize) return error.InvalidElfFile;
-
- _ = try preadAtLeast(file, &sh_buf, str_section_off, shentsize);
- const shstr32: *elf.Elf32_Shdr = @ptrCast(@alignCast(&sh_buf));
- const shstr64: *elf.Elf64_Shdr = @ptrCast(@alignCast(&sh_buf));
- const shstrtab_off = elfInt(is_64, need_bswap, shstr32.sh_offset, shstr64.sh_offset);
- const shstrtab_size = elfInt(is_64, need_bswap, shstr32.sh_size, shstr64.sh_size);
- var strtab_buf: [4096:0]u8 = undefined;
- const shstrtab_len = @min(shstrtab_size, strtab_buf.len);
- const shstrtab_read_len = try preadAtLeast(file, &strtab_buf, shstrtab_off, shstrtab_len);
- const shstrtab = strtab_buf[0..shstrtab_read_len];
-
- const shnum = elfInt(is_64, need_bswap, hdr32.e_shnum, hdr64.e_shnum);
- var sh_i: u16 = 0;
- const dynstr: ?struct { offset: u64, size: u64 } = find_dyn_str: while (sh_i < shnum) {
- // Reserve some bytes so that we can deref the 64-bit struct fields
- // even when the ELF file is 32-bits.
- const sh_reserve: usize = @sizeOf(elf.Elf64_Shdr) - @sizeOf(elf.Elf32_Shdr);
- const sh_read_byte_len = try preadAtLeast(
- file,
- sh_buf[0 .. sh_buf.len - sh_reserve],
- shoff,
- shentsize,
- );
- var sh_buf_i: usize = 0;
- while (sh_buf_i < sh_read_byte_len and sh_i < shnum) : ({
- sh_i += 1;
- shoff += shentsize;
- sh_buf_i += shentsize;
- }) {
- const sh32: *elf.Elf32_Shdr = @ptrCast(@alignCast(&sh_buf[sh_buf_i]));
- const sh64: *elf.Elf64_Shdr = @ptrCast(@alignCast(&sh_buf[sh_buf_i]));
- const sh_name_off = elfInt(is_64, need_bswap, sh32.sh_name, sh64.sh_name);
- const sh_name = mem.sliceTo(shstrtab[sh_name_off..], 0);
- if (mem.eql(u8, sh_name, ".dynstr")) {
- break :find_dyn_str .{
- .offset = elfInt(is_64, need_bswap, sh32.sh_offset, sh64.sh_offset),
- .size = elfInt(is_64, need_bswap, sh32.sh_size, sh64.sh_size),
- };
- }
- }
- } else null;
-
+ if (builtin.target.os.tag == .linux and result.isGnuLibC() and query.glibc_version == null) {
+ const str_section_off = header.shoff + @as(u64, header.shentsize) * @as(u64, header.shstrndx);
+ try file_reader.seekTo(str_section_off);
+ const shstr = try elf.takeSectionHeader(&file_reader.interface, header.is_64, header.endian);
+ var strtab_buf: [4096]u8 = undefined;
+ const shstrtab = strtab_buf[0..@min(shstr.sh_size, strtab_buf.len)];
+ try file_reader.seekTo(shstr.sh_offset);
+ try file_reader.interface.readSliceAll(shstrtab);
+ const dynstr: ?struct { offset: u64, size: u64 } = find_dyn_str: {
+ var it = header.iterateSectionHeaders(&file_reader.interface);
+ while (it.next()) |shdr| {
+ const end = mem.findScalarPos(u8, shstrtab, shdr.sh_name, 0) orelse continue;
+ const sh_name = shstrtab[shdr.sh_name..end :0];
+ if (mem.eql(u8, sh_name, ".dynstr")) break :find_dyn_str .{
+ .offset = shdr.sh_offset,
+ .size = shdr.sh_size,
+ };
+ } else break :find_dyn_str null;
+ };
if (dynstr) |ds| {
if (rpath_offset) |rpoff| {
if (rpoff > ds.size) return error.InvalidElfFile;
const rpoff_file = ds.offset + rpoff;
const rp_max_size = ds.size - rpoff;
- const strtab_len = @min(rp_max_size, strtab_buf.len);
- const strtab_read_len = try preadAtLeast(file, &strtab_buf, rpoff_file, strtab_len);
- const strtab = strtab_buf[0..strtab_read_len];
+ try file_reader.seekTo(rpoff_file);
+ const rpath_list = try file_reader.interface.takeSentinel(0);
+ if (rpath_list.len > rp_max_size) return error.StreamTooLong;
- const rpath_list = mem.sliceTo(strtab, 0);
var it = mem.tokenizeScalar(u8, rpath_list, ':');
while (it.next()) |rpath| {
if (glibcVerFromRPath(rpath)) |ver| {
@@ -845,7 +764,7 @@ test glibcVerFromLinkName {
try std.testing.expectError(error.InvalidGnuLibCVersion, glibcVerFromLinkName("ld-2.37.4.5.so", "ld-"));
}
-fn glibcVerFromRPath(rpath: []const u8) !std.SemanticVersion {
+fn glibcVerFromRPath(io: Io, rpath: []const u8) !std.SemanticVersion {
var dir = fs.cwd().openDir(rpath, .{}) catch |err| switch (err) {
error.NameTooLong => unreachable,
error.InvalidUtf8 => unreachable, // WASI only
@@ -879,7 +798,7 @@ fn glibcVerFromRPath(rpath: []const u8) !std.SemanticVersion {
// .dynstr section, and finding the max version number of symbols
// that start with "GLIBC_2.".
const glibc_so_basename = "libc.so.6";
- var f = dir.openFile(glibc_so_basename, .{}) catch |err| switch (err) {
+ var file = dir.openFile(glibc_so_basename, .{}) catch |err| switch (err) {
error.NameTooLong => unreachable,
error.InvalidUtf8 => unreachable, // WASI only
error.InvalidWtf8 => unreachable, // Windows only
@@ -913,16 +832,20 @@ fn glibcVerFromRPath(rpath: []const u8) !std.SemanticVersion {
error.Unexpected,
=> |e| return e,
};
- defer f.close();
+ defer file.close();
- return glibcVerFromSoFile(f) catch |err| switch (err) {
+ // Empirically, glibc 2.34 libc.so .dynstr section is 32441 bytes on my system.
+ var buffer: [8000]u8 = undefined;
+ var file_reader: Io.File.Reader = .initAdapted(file, io, &buffer);
+
+ return glibcVerFromSoFile(&file_reader) catch |err| switch (err) {
error.InvalidElfMagic,
error.InvalidElfEndian,
error.InvalidElfClass,
error.InvalidElfFile,
error.InvalidElfVersion,
error.InvalidGnuLibCVersion,
- error.UnexpectedEndOfFile,
+ error.EndOfStream,
=> return error.GLibCNotFound,
error.SystemResources,
@@ -934,88 +857,34 @@ fn glibcVerFromRPath(rpath: []const u8) !std.SemanticVersion {
};
}
-fn glibcVerFromSoFile(file: fs.File) !std.SemanticVersion {
- var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 align(@alignOf(elf.Elf64_Ehdr)) = undefined;
- _ = try preadAtLeast(file, &hdr_buf, 0, hdr_buf.len);
- const hdr32: *elf.Elf32_Ehdr = @ptrCast(&hdr_buf);
- const hdr64: *elf.Elf64_Ehdr = @ptrCast(&hdr_buf);
- if (!mem.eql(u8, hdr32.e_ident[0..4], elf.MAGIC)) return error.InvalidElfMagic;
- const elf_endian: std.builtin.Endian = switch (hdr32.e_ident[elf.EI.DATA]) {
- elf.ELFDATA2LSB => .little,
- elf.ELFDATA2MSB => .big,
- else => return error.InvalidElfEndian,
- };
- const need_bswap = elf_endian != native_endian;
- if (hdr32.e_ident[elf.EI.VERSION] != 1) return error.InvalidElfVersion;
-
- const is_64 = switch (hdr32.e_ident[elf.EI.CLASS]) {
- elf.ELFCLASS32 => false,
- elf.ELFCLASS64 => true,
- else => return error.InvalidElfClass,
+fn glibcVerFromSoFile(file_reader: *Io.File.Reader) !std.SemanticVersion {
+ const header = try elf.Header.read(&file_reader.interface);
+ const str_section_off = header.shoff + @as(u64, header.shentsize) * @as(u64, header.shstrndx);
+ try file_reader.seekTo(str_section_off);
+ const shstr = try elf.takeSectionHeader(&file_reader.interface, header.is_64, header.endian);
+ var strtab_buf: [4096]u8 = undefined;
+ const shstrtab = strtab_buf[0..@min(shstr.sh_size, strtab_buf.len)];
+ try file_reader.seekTo(shstr.sh_offset);
+ try file_reader.interface.readSliceAll(shstrtab);
+ const dynstr: struct { offset: u64, size: u64 } = find_dyn_str: {
+ var it = header.iterateSectionHeaders(&file_reader.interface);
+ while (it.next()) |shdr| {
+ const end = mem.findScalarPos(u8, shstrtab, shdr.sh_name, 0) orelse continue;
+ const sh_name = shstrtab[shdr.sh_name..end :0];
+ if (mem.eql(u8, sh_name, ".dynstr")) break :find_dyn_str .{
+ .offset = shdr.sh_offset,
+ .size = shdr.sh_size,
+ };
+ } else return error.InvalidGnuLibCVersion;
};
- const shstrndx = elfInt(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx);
- var shoff = elfInt(is_64, need_bswap, hdr32.e_shoff, hdr64.e_shoff);
- const shentsize = elfInt(is_64, need_bswap, hdr32.e_shentsize, hdr64.e_shentsize);
- const str_section_off = shoff + @as(u64, shentsize) * @as(u64, shstrndx);
- var sh_buf: [16 * @sizeOf(elf.Elf64_Shdr)]u8 align(@alignOf(elf.Elf64_Shdr)) = undefined;
- if (sh_buf.len < shentsize) return error.InvalidElfFile;
-
- _ = try preadAtLeast(file, &sh_buf, str_section_off, shentsize);
- const shstr32: *elf.Elf32_Shdr = @ptrCast(@alignCast(&sh_buf));
- const shstr64: *elf.Elf64_Shdr = @ptrCast(@alignCast(&sh_buf));
- const shstrtab_off = elfInt(is_64, need_bswap, shstr32.sh_offset, shstr64.sh_offset);
- const shstrtab_size = elfInt(is_64, need_bswap, shstr32.sh_size, shstr64.sh_size);
- var strtab_buf: [4096:0]u8 = undefined;
- const shstrtab_len = @min(shstrtab_size, strtab_buf.len);
- const shstrtab_read_len = try preadAtLeast(file, &strtab_buf, shstrtab_off, shstrtab_len);
- const shstrtab = strtab_buf[0..shstrtab_read_len];
- const shnum = elfInt(is_64, need_bswap, hdr32.e_shnum, hdr64.e_shnum);
- var sh_i: u16 = 0;
- const dynstr: struct { offset: u64, size: u64 } = find_dyn_str: while (sh_i < shnum) {
- // Reserve some bytes so that we can deref the 64-bit struct fields
- // even when the ELF file is 32-bits.
- const sh_reserve: usize = @sizeOf(elf.Elf64_Shdr) - @sizeOf(elf.Elf32_Shdr);
- const sh_read_byte_len = try preadAtLeast(
- file,
- sh_buf[0 .. sh_buf.len - sh_reserve],
- shoff,
- shentsize,
- );
- var sh_buf_i: usize = 0;
- while (sh_buf_i < sh_read_byte_len and sh_i < shnum) : ({
- sh_i += 1;
- shoff += shentsize;
- sh_buf_i += shentsize;
- }) {
- const sh32: *elf.Elf32_Shdr = @ptrCast(@alignCast(&sh_buf[sh_buf_i]));
- const sh64: *elf.Elf64_Shdr = @ptrCast(@alignCast(&sh_buf[sh_buf_i]));
- const sh_name_off = elfInt(is_64, need_bswap, sh32.sh_name, sh64.sh_name);
- const sh_name = mem.sliceTo(shstrtab[sh_name_off..], 0);
- if (mem.eql(u8, sh_name, ".dynstr")) {
- break :find_dyn_str .{
- .offset = elfInt(is_64, need_bswap, sh32.sh_offset, sh64.sh_offset),
- .size = elfInt(is_64, need_bswap, sh32.sh_size, sh64.sh_size),
- };
- }
- }
- } else return error.InvalidGnuLibCVersion;
// Here we loop over all the strings in the dynstr string table, assuming that any
// strings that start with "GLIBC_2." indicate the existence of such a glibc version,
// and furthermore, that the system-installed glibc is at minimum that version.
-
- // Empirically, glibc 2.34 libc.so .dynstr section is 32441 bytes on my system.
- // Here I use double this value plus some headroom. This makes it only need
- // a single read syscall here.
- var buf: [80000]u8 = undefined;
- if (buf.len < dynstr.size) return error.InvalidGnuLibCVersion;
-
- const dynstr_size: usize = @intCast(dynstr.size);
- const dynstr_bytes = buf[0..dynstr_size];
- _ = try preadAtLeast(file, dynstr_bytes, dynstr.offset, dynstr_bytes.len);
- var it = mem.splitScalar(u8, dynstr_bytes, 0);
var max_ver: std.SemanticVersion = .{ .major = 2, .minor = 2, .patch = 5 };
- while (it.next()) |s| {
+
+ try file_reader.seekTo(dynstr.offset);
+ while (file_reader.interface.takeSentinel(0)) |s| {
if (mem.startsWith(u8, s, "GLIBC_2.")) {
const chopped = s["GLIBC_".len..];
const ver = Target.Query.parseVersion(chopped) catch |err| switch (err) {
@@ -1028,6 +897,7 @@ fn glibcVerFromSoFile(file: fs.File) !std.SemanticVersion {
}
}
}
+
return max_ver;
}
@@ -1044,11 +914,7 @@ fn glibcVerFromSoFile(file: fs.File) !std.SemanticVersion {
/// answer to these questions, or if there is a shebang line, then it chases the referenced
/// file recursively. If that does not provide the answer, then the function falls back to
/// defaults.
-fn detectAbiAndDynamicLinker(
- cpu: Target.Cpu,
- os: Target.Os,
- query: Target.Query,
-) DetectError!Target {
+fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Target.Query) !Target {
const native_target_has_ld = comptime Target.DynamicLinker.kind(builtin.os.tag) != .none;
const is_linux = builtin.target.os.tag == .linux;
const is_illumos = builtin.target.os.tag == .illumos;
@@ -1111,49 +977,52 @@ fn detectAbiAndDynamicLinker(
const ld_info_list = ld_info_list_buffer[0..ld_info_list_len];
+ var file_reader: Io.File.Reader = undefined;
+ // According to `man 2 execve`:
+ //
+ // The kernel imposes a maximum length on the text
+ // that follows the "#!" characters at the start of a script;
+ // characters beyond the limit are ignored.
+ // Before Linux 5.1, the limit is 127 characters.
+ // Since Linux 5.1, the limit is 255 characters.
+ //
+ // Tests show that bash and zsh consider 255 as total limit,
+ // *including* "#!" characters and ignoring newline.
+ // For safety, we set max length as 255 + \n (1).
+ const max_shebang_line_size = 256;
+ var file_reader_buffer: [4096]u8 = undefined;
+ comptime assert(file_reader_buffer.len >= max_shebang_line_size);
+
// Best case scenario: the executable is dynamically linked, and we can iterate
// over our own shared objects and find a dynamic linker.
- const elf_file = elf_file: {
- // This block looks for a shebang line in /usr/bin/env,
- // if it finds one, then instead of using /usr/bin/env as the ELF file to examine, it uses the file it references instead,
- // doing the same logic recursively in case it finds another shebang line.
+ const header = elf_file: {
+ // This block looks for a shebang line in "/usr/bin/env". If it finds
+ // one, then instead of using "/usr/bin/env" as the ELF file to examine,
+ // it uses the file it references instead, doing the same logic
+ // recursively in case it finds another shebang line.
var file_name: []const u8 = switch (os.tag) {
- // Since /usr/bin/env is hard-coded into the shebang line of many portable scripts, it's a
- // reasonably reliable path to start with.
+ // Since /usr/bin/env is hard-coded into the shebang line of many
+ // portable scripts, it's a reasonably reliable path to start with.
else => "/usr/bin/env",
// Haiku does not have a /usr root directory.
.haiku => "/bin/env",
};
- // According to `man 2 execve`:
- //
- // The kernel imposes a maximum length on the text
- // that follows the "#!" characters at the start of a script;
- // characters beyond the limit are ignored.
- // Before Linux 5.1, the limit is 127 characters.
- // Since Linux 5.1, the limit is 255 characters.
- //
- // Tests show that bash and zsh consider 255 as total limit,
- // *including* "#!" characters and ignoring newline.
- // For safety, we set max length as 255 + \n (1).
- var buffer: [255 + 1]u8 = undefined;
while (true) {
- // Interpreter path can be relative on Linux, but
- // for simplicity we are asserting it is an absolute path.
const file = fs.openFileAbsolute(file_name, .{}) catch |err| switch (err) {
- error.NoSpaceLeft => unreachable,
- error.NameTooLong => unreachable,
- error.PathAlreadyExists => unreachable,
- error.SharingViolation => unreachable,
- error.InvalidUtf8 => unreachable, // WASI only
- error.InvalidWtf8 => unreachable, // Windows only
- error.BadPathName => unreachable,
- error.PipeBusy => unreachable,
- error.FileLocksNotSupported => unreachable,
- error.WouldBlock => unreachable,
- error.FileBusy => unreachable, // opened without write permissions
- error.AntivirusInterference => unreachable, // Windows-only error
+ error.NoSpaceLeft => return error.Unexpected,
+ error.NameTooLong => return error.Unexpected,
+ error.PathAlreadyExists => return error.Unexpected,
+ error.SharingViolation => return error.Unexpected,
+ error.InvalidUtf8 => return error.Unexpected, // WASI only
+ error.InvalidWtf8 => return error.Unexpected, // Windows only
+ error.BadPathName => return error.Unexpected,
+ error.PipeBusy => return error.Unexpected,
+ error.FileLocksNotSupported => return error.Unexpected,
+ error.WouldBlock => return error.Unexpected,
+ error.FileBusy => return error.Unexpected, // opened without write permissions
+ error.AntivirusInterference => return error.Unexpected, // Windows-only error
error.IsDir,
error.NotDir,
@@ -1164,66 +1033,58 @@ fn detectAbiAndDynamicLinker(
error.NetworkNotFound,
error.FileTooBig,
error.Unexpected,
- => |e| {
- std.log.warn("Encountered error: {s}, falling back to default ABI and dynamic linker.", .{@errorName(e)});
- return defaultAbiAndDynamicLinker(cpu, os, query);
- },
+ => return error.UnableToOpenElfFile,
else => |e| return e,
};
var is_elf_file = false;
- defer if (is_elf_file == false) file.close();
-
- // Shortest working interpreter path is "#!/i" (4)
- // (interpreter is "/i", assuming all paths are absolute, like in above comment).
- // ELF magic number length is also 4.
- //
- // If file is shorter than that, it is definitely not ELF file
- // nor file with "shebang" line.
- const min_len: usize = 4;
-
- const len = preadAtLeast(file, &buffer, 0, min_len) catch |err| switch (err) {
- error.UnexpectedEndOfFile,
- error.UnableToReadElfFile,
- error.ProcessNotFound,
- => return defaultAbiAndDynamicLinker(cpu, os, query),
+ defer if (!is_elf_file) file.close();
+
+ file_reader = .initAdapted(file, io, &file_reader_buffer);
+ file_name = undefined; // it aliases file_reader_buffer
+
+ const header = elf.Header.read(&file_reader.interface) catch |hdr_err| switch (hdr_err) {
+ error.EndOfStream,
+ error.InvalidElfMagic,
+ => {
+ const shebang_line = file_reader.interface.takeSentinel('\n') catch |err| switch (err) {
+ error.ReadFailed => return file_reader.err.?,
+ // It's neither an ELF file nor file with shebang line.
+ error.EndOfStream, error.StreamTooLong => return error.UnhelpfulFile,
+ };
+ if (!mem.startsWith(u8, shebang_line, "#!")) return error.UnhelpfulFile;
+ // We detected shebang, now parse entire line.
+
+ // Trim leading "#!", spaces and tabs.
+ const trimmed_line = mem.trimStart(u8, shebang_line[2..], &.{ ' ', '\t' });
+
+ // This line can have:
+ // * Interpreter path only,
+ // * Interpreter path and arguments, all separated by space, tab or NUL character.
+ // And optionally newline at the end.
+ const path_maybe_args = mem.trimEnd(u8, trimmed_line, "\n");
+
+ // Separate path and args.
+ const path_end = mem.indexOfAny(u8, path_maybe_args, &.{ ' ', '\t', 0 }) orelse path_maybe_args.len;
+ const unvalidated_path = path_maybe_args[0..path_end];
+ file_name = if (fs.path.isAbsolute(unvalidated_path)) unvalidated_path else return error.RelativeShebang;
+ continue;
+ },
- else => |e| return e,
+ error.InvalidElfVersion,
+ error.InvalidElfClass,
+ error.InvalidElfEndian,
+ => return error.InvalidElfFile,
+
+ error.ReadFailed => return file_reader.err.?,
};
- const content = buffer[0..len];
-
- if (mem.eql(u8, content[0..4], std.elf.MAGIC)) {
- // It is very likely ELF file!
- is_elf_file = true;
- break :elf_file file;
- } else if (mem.eql(u8, content[0..2], "#!")) {
- // We detected shebang, now parse entire line.
-
- // Trim leading "#!", spaces and tabs.
- const trimmed_line = mem.trimStart(u8, content[2..], &.{ ' ', '\t' });
-
- // This line can have:
- // * Interpreter path only,
- // * Interpreter path and arguments, all separated by space, tab or NUL character.
- // And optionally newline at the end.
- const path_maybe_args = mem.trimEnd(u8, trimmed_line, "\n");
-
- // Separate path and args.
- const path_end = mem.indexOfAny(u8, path_maybe_args, &.{ ' ', '\t', 0 }) orelse path_maybe_args.len;
-
- file_name = path_maybe_args[0..path_end];
- continue;
- } else {
- // Not a ELF file, not a shell script with "shebang line", invalid duck.
- return defaultAbiAndDynamicLinker(cpu, os, query);
- }
+ is_elf_file = true;
+ break :elf_file header;
}
};
- defer elf_file.close();
+ defer file_reader.file.close(io);
- // TODO: inline this function and combine the buffer we already read above to find
- // the possible shebang line with the buffer we use for the ELF header.
- return abiAndDynamicLinkerFromFile(elf_file, cpu, os, ld_info_list, query) catch |err| switch (err) {
+ return abiAndDynamicLinkerFromFile(&file_reader, &header, cpu, os, ld_info_list, query) catch |err| switch (err) {
error.FileSystem,
error.SystemResources,
error.SymLinkLoop,
@@ -1232,6 +1093,8 @@ fn detectAbiAndDynamicLinker(
error.ProcessNotFound,
=> |e| return e,
+ error.ReadFailed => return file_reader.err.?,
+
error.UnableToReadElfFile,
error.InvalidElfClass,
error.InvalidElfVersion,
@@ -1239,12 +1102,12 @@ fn detectAbiAndDynamicLinker(
error.InvalidElfFile,
error.InvalidElfMagic,
error.Unexpected,
- error.UnexpectedEndOfFile,
+ error.EndOfStream,
error.NameTooLong,
error.StaticElfFile,
// Finally, we fall back on the standard path.
=> |e| {
- std.log.warn("Encountered error: {s}, falling back to default ABI and dynamic linker.", .{@errorName(e)});
+ std.log.warn("encountered {t}; falling back to default ABI and dynamic linker", .{e});
return defaultAbiAndDynamicLinker(cpu, os, query);
},
};
@@ -1269,59 +1132,6 @@ const LdInfo = struct {
abi: Target.Abi,
};
-fn preadAtLeast(file: fs.File, buf: []u8, offset: u64, min_read_len: usize) !usize {
- var i: usize = 0;
- while (i < min_read_len) {
- const len = file.pread(buf[i..], offset + i) catch |err| switch (err) {
- error.OperationAborted => unreachable, // Windows-only
- error.WouldBlock => unreachable, // Did not request blocking mode
- error.Canceled => unreachable, // timerfd is unseekable
- error.NotOpenForReading => unreachable,
- error.SystemResources => return error.SystemResources,
- error.IsDir => return error.UnableToReadElfFile,
- error.BrokenPipe => return error.UnableToReadElfFile,
- error.Unseekable => return error.UnableToReadElfFile,
- error.ConnectionResetByPeer => return error.UnableToReadElfFile,
- error.ConnectionTimedOut => return error.UnableToReadElfFile,
- error.SocketUnconnected => return error.UnableToReadElfFile,
- error.Unexpected => return error.Unexpected,
- error.InputOutput => return error.FileSystem,
- error.AccessDenied => return error.Unexpected,
- error.ProcessNotFound => return error.ProcessNotFound,
- error.LockViolation => return error.UnableToReadElfFile,
- };
- if (len == 0) return error.UnexpectedEndOfFile;
- i += len;
- }
- return i;
-}
-
-fn elfInt(is_64: bool, need_bswap: bool, int_32: anytype, int_64: anytype) @TypeOf(int_64) {
- if (is_64) {
- if (need_bswap) {
- return @byteSwap(int_64);
- } else {
- return int_64;
- }
- } else {
- if (need_bswap) {
- return @byteSwap(int_32);
- } else {
- return int_32;
- }
- }
-}
-
-const builtin = @import("builtin");
-const std = @import("../std.zig");
-const mem = std.mem;
-const elf = std.elf;
-const fs = std.fs;
-const assert = std.debug.assert;
-const Target = std.Target;
-const native_endian = builtin.cpu.arch.endian();
-const posix = std.posix;
-
test {
_ = NativePaths;
lib/std/Build.zig
@@ -1,5 +1,7 @@
-const std = @import("std.zig");
const builtin = @import("builtin");
+
+const std = @import("std.zig");
+const Io = std.Io;
const fs = std.fs;
const mem = std.mem;
const debug = std.debug;
@@ -110,6 +112,7 @@ pub const ReleaseMode = enum {
/// Shared state among all Build instances.
/// Settings that are here rather than in Build are not configurable per-package.
pub const Graph = struct {
+ io: Io,
arena: Allocator,
system_library_options: std.StringArrayHashMapUnmanaged(SystemLibraryMode) = .empty,
system_package_mode: bool = false,
@@ -2666,9 +2669,10 @@ pub fn resolveTargetQuery(b: *Build, query: Target.Query) ResolvedTarget {
// Hot path. This is faster than querying the native CPU and OS again.
return b.graph.host;
}
+ const io = b.graph.io;
return .{
.query = query,
- .result = std.zig.system.resolveTargetQuery(query) catch
+ .result = std.zig.system.resolveTargetQuery(io, query) catch
@panic("unable to resolve target query"),
};
}
lib/std/elf.zig
@@ -1,9 +1,11 @@
//! Executable and Linkable Format.
const std = @import("std.zig");
+const Io = std.Io;
const math = std.math;
const mem = std.mem;
const assert = std.debug.assert;
+const Endian = std.builtin.Endian;
const native_endian = @import("builtin").target.cpu.arch.endian();
pub const AT_NULL = 0;
@@ -568,7 +570,7 @@ pub const ET = enum(u16) {
/// All integers are native endian.
pub const Header = struct {
is_64: bool,
- endian: std.builtin.Endian,
+ endian: Endian,
os_abi: OSABI,
/// The meaning of this value depends on `os_abi`.
abi_version: u8,
@@ -583,48 +585,76 @@ pub const Header = struct {
shnum: u16,
shstrndx: u16,
- pub fn iterateProgramHeaders(h: Header, file_reader: *std.fs.File.Reader) ProgramHeaderIterator {
+ pub fn iterateProgramHeaders(h: *const Header, file_reader: *Io.File.Reader) ProgramHeaderIterator {
return .{
- .elf_header = h,
+ .is_64 = h.is_64,
+ .endian = h.endian,
+ .phnum = h.phnum,
+ .phoff = h.phoff,
.file_reader = file_reader,
};
}
- pub fn iterateProgramHeadersBuffer(h: Header, buf: []const u8) ProgramHeaderBufferIterator {
+ pub fn iterateProgramHeadersBuffer(h: *const Header, buf: []const u8) ProgramHeaderBufferIterator {
return .{
- .elf_header = h,
+ .is_64 = h.is_64,
+ .endian = h.endian,
+ .phnum = h.phnum,
+ .phoff = h.phoff,
.buf = buf,
};
}
- pub fn iterateSectionHeaders(h: Header, file_reader: *std.fs.File.Reader) SectionHeaderIterator {
+ pub fn iterateSectionHeaders(h: *const Header, file_reader: *Io.File.Reader) SectionHeaderIterator {
return .{
- .elf_header = h,
+ .is_64 = h.is_64,
+ .endian = h.endian,
+ .shnum = h.shnum,
+ .shoff = h.shoff,
.file_reader = file_reader,
};
}
- pub fn iterateSectionHeadersBuffer(h: Header, buf: []const u8) SectionHeaderBufferIterator {
+ pub fn iterateSectionHeadersBuffer(h: *const Header, buf: []const u8) SectionHeaderBufferIterator {
return .{
- .elf_header = h,
+ .is_64 = h.is_64,
+ .endian = h.endian,
+ .shnum = h.shnum,
+ .shoff = h.shoff,
.buf = buf,
};
}
- pub const ReadError = std.Io.Reader.Error || error{
+ pub fn iterateDynamicSection(
+ h: *const Header,
+ file_reader: *Io.File.Reader,
+ offset: u64,
+ size: u64,
+ ) DynamicSectionIterator {
+ return .{
+ .is_64 = h.is_64,
+ .endian = h.endian,
+ .offset = offset,
+ .end_offset = offset + size,
+ .file_reader = file_reader,
+ };
+ }
+
+ pub const ReadError = Io.Reader.Error || error{
InvalidElfMagic,
InvalidElfVersion,
InvalidElfClass,
InvalidElfEndian,
};
- pub fn read(r: *std.Io.Reader) ReadError!Header {
+ /// If this function fails, seek position of `r` is unchanged.
+ pub fn read(r: *Io.Reader) ReadError!Header {
const buf = try r.peek(@sizeOf(Elf64_Ehdr));
if (!mem.eql(u8, buf[0..4], MAGIC)) return error.InvalidElfMagic;
if (buf[EI.VERSION] != 1) return error.InvalidElfVersion;
- const endian: std.builtin.Endian = switch (buf[EI.DATA]) {
+ const endian: Endian = switch (buf[EI.DATA]) {
ELFDATA2LSB => .little,
ELFDATA2MSB => .big,
else => return error.InvalidElfEndian,
@@ -637,7 +667,7 @@ pub const Header = struct {
};
}
- pub fn init(hdr: anytype, endian: std.builtin.Endian) Header {
+ pub fn init(hdr: anytype, endian: Endian) Header {
// Converting integers to exhaustive enums using `@enumFromInt` could cause a panic.
comptime assert(!@typeInfo(OSABI).@"enum".is_exhaustive);
return .{
@@ -664,46 +694,54 @@ pub const Header = struct {
};
pub const ProgramHeaderIterator = struct {
- elf_header: Header,
- file_reader: *std.fs.File.Reader,
+ is_64: bool,
+ endian: Endian,
+ phnum: u16,
+ phoff: u64,
+
+ file_reader: *Io.File.Reader,
index: usize = 0,
pub fn next(it: *ProgramHeaderIterator) !?Elf64_Phdr {
- if (it.index >= it.elf_header.phnum) return null;
+ if (it.index >= it.phnum) return null;
defer it.index += 1;
- const size: u64 = if (it.elf_header.is_64) @sizeOf(Elf64_Phdr) else @sizeOf(Elf32_Phdr);
- const offset = it.elf_header.phoff + size * it.index;
+ const size: u64 = if (it.is_64) @sizeOf(Elf64_Phdr) else @sizeOf(Elf32_Phdr);
+ const offset = it.phoff + size * it.index;
try it.file_reader.seekTo(offset);
- return takePhdr(&it.file_reader.interface, it.elf_header);
+ return takeProgramHeader(&it.file_reader.interface, it.is_64, it.endian);
}
};
pub const ProgramHeaderBufferIterator = struct {
- elf_header: Header,
+ is_64: bool,
+ endian: Endian,
+ phnum: u16,
+ phoff: u64,
+
buf: []const u8,
index: usize = 0,
pub fn next(it: *ProgramHeaderBufferIterator) !?Elf64_Phdr {
- if (it.index >= it.elf_header.phnum) return null;
+ if (it.index >= it.phnum) return null;
defer it.index += 1;
- const size: u64 = if (it.elf_header.is_64) @sizeOf(Elf64_Phdr) else @sizeOf(Elf32_Phdr);
- const offset = it.elf_header.phoff + size * it.index;
- var reader = std.Io.Reader.fixed(it.buf[offset..]);
+ const size: u64 = if (it.is_64) @sizeOf(Elf64_Phdr) else @sizeOf(Elf32_Phdr);
+ const offset = it.phoff + size * it.index;
+ var reader = Io.Reader.fixed(it.buf[offset..]);
- return takePhdr(&reader, it.elf_header);
+ return takeProgramHeader(&reader, it.is_64, it.endian);
}
};
-fn takePhdr(reader: *std.Io.Reader, elf_header: Header) !?Elf64_Phdr {
- if (elf_header.is_64) {
- const phdr = try reader.takeStruct(Elf64_Phdr, elf_header.endian);
+pub fn takeProgramHeader(reader: *Io.Reader, is_64: bool, endian: Endian) !Elf64_Phdr {
+ if (is_64) {
+ const phdr = try reader.takeStruct(Elf64_Phdr, endian);
return phdr;
}
- const phdr = try reader.takeStruct(Elf32_Phdr, elf_header.endian);
+ const phdr = try reader.takeStruct(Elf32_Phdr, endian);
return .{
.p_type = phdr.p_type,
.p_offset = phdr.p_offset,
@@ -717,47 +755,55 @@ fn takePhdr(reader: *std.Io.Reader, elf_header: Header) !?Elf64_Phdr {
}
pub const SectionHeaderIterator = struct {
- elf_header: Header,
- file_reader: *std.fs.File.Reader,
+ is_64: bool,
+ endian: Endian,
+ shnum: u16,
+ shoff: u64,
+
+ file_reader: *Io.File.Reader,
index: usize = 0,
pub fn next(it: *SectionHeaderIterator) !?Elf64_Shdr {
- if (it.index >= it.elf_header.shnum) return null;
+ if (it.index >= it.shnum) return null;
defer it.index += 1;
- const size: u64 = if (it.elf_header.is_64) @sizeOf(Elf64_Shdr) else @sizeOf(Elf32_Shdr);
- const offset = it.elf_header.shoff + size * it.index;
+ const size: u64 = if (it.is_64) @sizeOf(Elf64_Shdr) else @sizeOf(Elf32_Shdr);
+ const offset = it.shoff + size * it.index;
try it.file_reader.seekTo(offset);
- return takeShdr(&it.file_reader.interface, it.elf_header);
+ return takeSectionHeader(&it.file_reader.interface, it.is_64, it.endian);
}
};
pub const SectionHeaderBufferIterator = struct {
- elf_header: Header,
+ is_64: bool,
+ endian: Endian,
+ shnum: u16,
+ shoff: u64,
+
buf: []const u8,
index: usize = 0,
pub fn next(it: *SectionHeaderBufferIterator) !?Elf64_Shdr {
- if (it.index >= it.elf_header.shnum) return null;
+ if (it.index >= it.shnum) return null;
defer it.index += 1;
- const size: u64 = if (it.elf_header.is_64) @sizeOf(Elf64_Shdr) else @sizeOf(Elf32_Shdr);
- const offset = it.elf_header.shoff + size * it.index;
+ const size: u64 = if (it.is_64) @sizeOf(Elf64_Shdr) else @sizeOf(Elf32_Shdr);
+ const offset = it.shoff + size * it.index;
if (offset > it.buf.len) return error.EndOfStream;
- var reader = std.Io.Reader.fixed(it.buf[@intCast(offset)..]);
+ var reader = Io.Reader.fixed(it.buf[@intCast(offset)..]);
- return takeShdr(&reader, it.elf_header);
+ return takeSectionHeader(&reader, it.is_64, it.endian);
}
};
-fn takeShdr(reader: *std.Io.Reader, elf_header: Header) !?Elf64_Shdr {
- if (elf_header.is_64) {
- const shdr = try reader.takeStruct(Elf64_Shdr, elf_header.endian);
+pub fn takeSectionHeader(reader: *Io.Reader, is_64: bool, endian: Endian) !Elf64_Shdr {
+ if (is_64) {
+ const shdr = try reader.takeStruct(Elf64_Shdr, endian);
return shdr;
}
- const shdr = try reader.takeStruct(Elf32_Shdr, elf_header.endian);
+ const shdr = try reader.takeStruct(Elf32_Shdr, endian);
return .{
.sh_name = shdr.sh_name,
.sh_type = shdr.sh_type,
@@ -772,6 +818,36 @@ fn takeShdr(reader: *std.Io.Reader, elf_header: Header) !?Elf64_Shdr {
};
}
+pub const DynamicSectionIterator = struct {
+ is_64: bool,
+ endian: Endian,
+ offset: u64,
+ end_offset: u64,
+
+ file_reader: *Io.File.Reader,
+
+ pub fn next(it: *SectionHeaderIterator) !?Elf64_Dyn {
+ if (it.offset >= it.end_offset) return null;
+ const size: u64 = if (it.is_64) @sizeOf(Elf64_Dyn) else @sizeOf(Elf32_Dyn);
+ defer it.offset += size;
+ try it.file_reader.seekTo(it.offset);
+ return takeDynamicSection(&it.file_reader.interface, it.is_64, it.endian);
+ }
+};
+
+pub fn takeDynamicSection(reader: *Io.Reader, is_64: bool, endian: Endian) !Elf64_Dyn {
+ if (is_64) {
+ const dyn = try reader.takeStruct(Elf64_Dyn, endian);
+ return dyn;
+ }
+
+ const dyn = try reader.takeStruct(Elf32_Dyn, endian);
+ return .{
+ .d_tag = dyn.d_tag,
+ .d_val = dyn.d_val,
+ };
+}
+
pub const EI = struct {
pub const CLASS = 4;
pub const DATA = 5;
lib/std/Io.zig
@@ -738,9 +738,9 @@ pub const Timestamp = struct {
/// * On Linux, corresponds `CLOCK_MONOTONIC`.
/// * On macOS, corresponds to `CLOCK_UPTIME_RAW`.
awake,
- /// Identical to `awake` except it expresses intent to include time
- /// that the system is suspended, however, it may be implemented
- /// identically to `awake`.
+ /// Identical to `awake` except it expresses intent to **include time
+ /// that the system is suspended**, however, due to limitations it may
+ /// behave identically to `awake`.
///
/// * On Linux, corresponds `CLOCK_BOOTTIME`.
/// * On macOS, corresponds to `CLOCK_MONOTONIC_RAW`.
lib/std/zig.zig
@@ -6,6 +6,7 @@ const std = @import("std.zig");
const tokenizer = @import("zig/tokenizer.zig");
const assert = std.debug.assert;
const Allocator = std.mem.Allocator;
+const Io = std.Io;
const Writer = std.Io.Writer;
pub const ErrorBundle = @import("zig/ErrorBundle.zig");
@@ -52,9 +53,9 @@ pub const Color = enum {
/// Assume stderr is a terminal.
on,
- pub fn get_tty_conf(color: Color) std.Io.tty.Config {
+ pub fn get_tty_conf(color: Color) Io.tty.Config {
return switch (color) {
- .auto => std.Io.tty.detectConfig(std.fs.File.stderr()),
+ .auto => Io.tty.detectConfig(std.fs.File.stderr()),
.on => .escape_codes,
.off => .no_color,
};
@@ -323,7 +324,7 @@ pub const BuildId = union(enum) {
try std.testing.expectError(error.InvalidBuildIdStyle, parse("yaddaxxx"));
}
- pub fn format(id: BuildId, writer: *std.Io.Writer) std.Io.Writer.Error!void {
+ pub fn format(id: BuildId, writer: *Writer) Writer.Error!void {
switch (id) {
.none, .fast, .uuid, .sha1, .md5 => {
try writer.writeAll(@tagName(id));
@@ -620,8 +621,8 @@ pub fn putAstErrorsIntoBundle(
try wip_errors.addZirErrorMessages(zir, tree, tree.source, path);
}
-pub fn resolveTargetQueryOrFatal(target_query: std.Target.Query) std.Target {
- return std.zig.system.resolveTargetQuery(target_query) catch |err|
+pub fn resolveTargetQueryOrFatal(io: Io, target_query: std.Target.Query) std.Target {
+ return std.zig.system.resolveTargetQuery(io, target_query) catch |err|
std.process.fatal("unable to resolve target: {s}", .{@errorName(err)});
}
src/main.zig
@@ -1,5 +1,8 @@
-const std = @import("std");
const builtin = @import("builtin");
+const native_os = builtin.os.tag;
+
+const std = @import("std");
+const Io = std.Io;
const assert = std.debug.assert;
const fs = std.fs;
const mem = std.mem;
@@ -10,7 +13,6 @@ const Color = std.zig.Color;
const warn = std.log.warn;
const ThreadPool = std.Thread.Pool;
const cleanExit = std.process.cleanExit;
-const native_os = builtin.os.tag;
const Cache = std.Build.Cache;
const Path = std.Build.Cache.Path;
const Directory = std.Build.Cache.Directory;
@@ -245,26 +247,30 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
}
}
+ var threaded: Io.Threaded = .init(gpa);
+ defer threaded.deinit();
+ const io = threaded.io();
+
const cmd = args[1];
const cmd_args = args[2..];
if (mem.eql(u8, cmd, "build-exe")) {
dev.check(.build_exe_command);
- return buildOutputType(gpa, arena, args, .{ .build = .Exe });
+ return buildOutputType(gpa, arena, io, args, .{ .build = .Exe });
} else if (mem.eql(u8, cmd, "build-lib")) {
dev.check(.build_lib_command);
- return buildOutputType(gpa, arena, args, .{ .build = .Lib });
+ return buildOutputType(gpa, arena, io, args, .{ .build = .Lib });
} else if (mem.eql(u8, cmd, "build-obj")) {
dev.check(.build_obj_command);
- return buildOutputType(gpa, arena, args, .{ .build = .Obj });
+ return buildOutputType(gpa, arena, io, args, .{ .build = .Obj });
} else if (mem.eql(u8, cmd, "test")) {
dev.check(.test_command);
- return buildOutputType(gpa, arena, args, .zig_test);
+ return buildOutputType(gpa, arena, io, args, .zig_test);
} else if (mem.eql(u8, cmd, "test-obj")) {
dev.check(.test_command);
- return buildOutputType(gpa, arena, args, .zig_test_obj);
+ return buildOutputType(gpa, arena, io, args, .zig_test_obj);
} else if (mem.eql(u8, cmd, "run")) {
dev.check(.run_command);
- return buildOutputType(gpa, arena, args, .run);
+ return buildOutputType(gpa, arena, io, args, .run);
} else if (mem.eql(u8, cmd, "dlltool") or
mem.eql(u8, cmd, "ranlib") or
mem.eql(u8, cmd, "lib") or
@@ -274,7 +280,7 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
return process.exit(try llvmArMain(arena, args));
} else if (mem.eql(u8, cmd, "build")) {
dev.check(.build_command);
- return cmdBuild(gpa, arena, cmd_args);
+ return cmdBuild(gpa, arena, io, cmd_args);
} else if (mem.eql(u8, cmd, "clang") or
mem.eql(u8, cmd, "-cc1") or mem.eql(u8, cmd, "-cc1as"))
{
@@ -288,16 +294,16 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
return process.exit(try lldMain(arena, args, true));
} else if (mem.eql(u8, cmd, "cc")) {
dev.check(.cc_command);
- return buildOutputType(gpa, arena, args, .cc);
+ return buildOutputType(gpa, arena, io, args, .cc);
} else if (mem.eql(u8, cmd, "c++")) {
dev.check(.cc_command);
- return buildOutputType(gpa, arena, args, .cpp);
+ return buildOutputType(gpa, arena, io, args, .cpp);
} else if (mem.eql(u8, cmd, "translate-c")) {
dev.check(.translate_c_command);
- return buildOutputType(gpa, arena, args, .translate_c);
+ return buildOutputType(gpa, arena, io, args, .translate_c);
} else if (mem.eql(u8, cmd, "rc")) {
const use_server = cmd_args.len > 0 and std.mem.eql(u8, cmd_args[0], "--zig-integration");
- return jitCmd(gpa, arena, cmd_args, .{
+ return jitCmd(gpa, arena, io, cmd_args, .{
.cmd_name = "resinator",
.root_src_path = "resinator/main.zig",
.depend_on_aro = true,
@@ -308,20 +314,20 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
dev.check(.fmt_command);
return @import("fmt.zig").run(gpa, arena, cmd_args);
} else if (mem.eql(u8, cmd, "objcopy")) {
- return jitCmd(gpa, arena, cmd_args, .{
+ return jitCmd(gpa, arena, io, cmd_args, .{
.cmd_name = "objcopy",
.root_src_path = "objcopy.zig",
});
} else if (mem.eql(u8, cmd, "fetch")) {
return cmdFetch(gpa, arena, cmd_args);
} else if (mem.eql(u8, cmd, "libc")) {
- return jitCmd(gpa, arena, cmd_args, .{
+ return jitCmd(gpa, arena, io, cmd_args, .{
.cmd_name = "libc",
.root_src_path = "libc.zig",
.prepend_zig_lib_dir_path = true,
});
} else if (mem.eql(u8, cmd, "std")) {
- return jitCmd(gpa, arena, cmd_args, .{
+ return jitCmd(gpa, arena, io, cmd_args, .{
.cmd_name = "std",
.root_src_path = "std-docs.zig",
.prepend_zig_lib_dir_path = true,
@@ -332,7 +338,7 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
return cmdInit(gpa, arena, cmd_args);
} else if (mem.eql(u8, cmd, "targets")) {
dev.check(.targets_command);
- const host = std.zig.resolveTargetQueryOrFatal(.{});
+ const host = std.zig.resolveTargetQueryOrFatal(io, .{});
var stdout_writer = fs.File.stdout().writer(&stdout_buffer);
try @import("print_targets.zig").cmdTargets(arena, cmd_args, &stdout_writer.interface, &host);
return stdout_writer.interface.flush();
@@ -351,7 +357,7 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
);
return stdout_writer.interface.flush();
} else if (mem.eql(u8, cmd, "reduce")) {
- return jitCmd(gpa, arena, cmd_args, .{
+ return jitCmd(gpa, arena, io, cmd_args, .{
.cmd_name = "reduce",
.root_src_path = "reduce.zig",
});
@@ -364,7 +370,7 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
} else if (mem.eql(u8, cmd, "ast-check")) {
return cmdAstCheck(arena, cmd_args);
} else if (mem.eql(u8, cmd, "detect-cpu")) {
- return cmdDetectCpu(cmd_args);
+ return cmdDetectCpu(io, cmd_args);
} else if (build_options.enable_debug_extensions and mem.eql(u8, cmd, "changelist")) {
return cmdChangelist(arena, cmd_args);
} else if (build_options.enable_debug_extensions and mem.eql(u8, cmd, "dump-zir")) {
@@ -792,6 +798,7 @@ const CliModule = struct {
fn buildOutputType(
gpa: Allocator,
arena: Allocator,
+ io: Io,
all_args: []const []const u8,
arg_mode: ArgMode,
) !void {
@@ -3017,7 +3024,7 @@ fn buildOutputType(
create_module.opts.emit_bin = emit_bin != .no;
create_module.opts.any_c_source_files = create_module.c_source_files.items.len != 0;
- const main_mod = try createModule(gpa, arena, &create_module, 0, null, color);
+ const main_mod = try createModule(gpa, arena, io, &create_module, 0, null, color);
for (create_module.modules.keys(), create_module.modules.values()) |key, cli_mod| {
if (cli_mod.resolved == null)
fatal("module '{s}' declared but not used", .{key});
@@ -3545,6 +3552,7 @@ fn buildOutputType(
var stdin_reader = fs.File.stdin().reader(&stdin_buffer);
var stdout_writer = fs.File.stdout().writer(&stdout_buffer);
try serve(
+ io,
comp,
&stdin_reader.interface,
&stdout_writer.interface,
@@ -3571,6 +3579,7 @@ fn buildOutputType(
var output = conn.stream.writer(&stdout_buffer);
try serve(
+ io,
comp,
input.interface(),
&output.interface,
@@ -3646,6 +3655,7 @@ fn buildOutputType(
comp,
gpa,
arena,
+ io,
test_exec_args.items,
self_exe_path,
arg_mode,
@@ -3704,6 +3714,7 @@ const CreateModule = struct {
fn createModule(
gpa: Allocator,
arena: Allocator,
+ io: Io,
create_module: *CreateModule,
index: usize,
parent: ?*Package.Module,
@@ -3777,7 +3788,7 @@ fn createModule(
}
const target_query = std.zig.parseTargetQueryOrReportFatalError(arena, target_parse_options);
- const target = std.zig.resolveTargetQueryOrFatal(target_query);
+ const target = std.zig.resolveTargetQueryOrFatal(io, target_query);
break :t .{
.result = target,
.is_native_os = target_query.isNativeOs(),
@@ -4022,7 +4033,7 @@ fn createModule(
for (cli_mod.deps) |dep| {
const dep_index = create_module.modules.getIndex(dep.value) orelse
fatal("module '{s}' depends on non-existent module '{s}'", .{ name, dep.key });
- const dep_mod = try createModule(gpa, arena, create_module, dep_index, mod, color);
+ const dep_mod = try createModule(gpa, arena, io, create_module, dep_index, mod, color);
try mod.deps.put(arena, dep.key, dep_mod);
}
@@ -4038,9 +4049,10 @@ fn saveState(comp: *Compilation, incremental: bool) void {
}
fn serve(
+ io: Io,
comp: *Compilation,
- in: *std.Io.Reader,
- out: *std.Io.Writer,
+ in: *Io.Reader,
+ out: *Io.Writer,
test_exec_args: []const ?[]const u8,
self_exe_path: ?[]const u8,
arg_mode: ArgMode,
@@ -4090,7 +4102,7 @@ fn serve(
defer arena_instance.deinit();
const arena = arena_instance.allocator();
var output: Compilation.CImportResult = undefined;
- try cmdTranslateC(comp, arena, &output, file_system_inputs, main_progress_node);
+ try cmdTranslateC(io, comp, arena, &output, file_system_inputs, main_progress_node);
defer output.deinit(gpa);
if (file_system_inputs.items.len != 0) {
@@ -4126,6 +4138,7 @@ fn serve(
// comp,
// gpa,
// arena,
+ // io,
// test_exec_args,
// self_exe_path.?,
// arg_mode,
@@ -4280,6 +4293,7 @@ fn runOrTest(
comp: *Compilation,
gpa: Allocator,
arena: Allocator,
+ io: Io,
test_exec_args: []const ?[]const u8,
self_exe_path: []const u8,
arg_mode: ArgMode,
@@ -4334,7 +4348,7 @@ fn runOrTest(
std.debug.lockStdErr();
const err = process.execve(gpa, argv.items, &env_map);
std.debug.unlockStdErr();
- try warnAboutForeignBinaries(arena, arg_mode, target, link_libc);
+ try warnAboutForeignBinaries(io, arena, arg_mode, target, link_libc);
const cmd = try std.mem.join(arena, " ", argv.items);
fatal("the following command failed to execve with '{s}':\n{s}", .{ @errorName(err), cmd });
} else if (process.can_spawn) {
@@ -4355,7 +4369,7 @@ fn runOrTest(
break :t child.spawnAndWait();
};
const term = term_result catch |err| {
- try warnAboutForeignBinaries(arena, arg_mode, target, link_libc);
+ try warnAboutForeignBinaries(io, arena, arg_mode, target, link_libc);
const cmd = try std.mem.join(arena, " ", argv.items);
fatal("the following command failed with '{s}':\n{s}", .{ @errorName(err), cmd });
};
@@ -4594,11 +4608,12 @@ fn cmdTranslateC(
pub fn translateC(
gpa: Allocator,
arena: Allocator,
+ io: Io,
argv: []const []const u8,
prog_node: std.Progress.Node,
capture: ?*[]u8,
) !void {
- try jitCmd(gpa, arena, argv, .{
+ try jitCmd(gpa, arena, io, argv, .{
.cmd_name = "translate-c",
.root_src_path = "translate-c/main.zig",
.depend_on_aro = true,
@@ -4755,7 +4770,7 @@ test sanitizeExampleName {
try std.testing.expectEqualStrings("test_project", try sanitizeExampleName(arena, "test project"));
}
-fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
+fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) !void {
dev.check(.build_command);
var build_file: ?[]const u8 = null;
@@ -4983,7 +4998,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
.arch_os_abi = triple,
});
break :t .{
- .result = std.zig.resolveTargetQueryOrFatal(target_query),
+ .result = std.zig.resolveTargetQueryOrFatal(io, target_query),
.is_native_os = false,
.is_native_abi = false,
.is_explicit_dynamic_linker = false,
@@ -4991,7 +5006,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
}
}
break :t .{
- .result = std.zig.resolveTargetQueryOrFatal(.{}),
+ .result = std.zig.resolveTargetQueryOrFatal(io, .{}),
.is_native_os = true,
.is_native_abi = true,
.is_explicit_dynamic_linker = false,
@@ -5400,6 +5415,7 @@ const JitCmdOptions = struct {
fn jitCmd(
gpa: Allocator,
arena: Allocator,
+ io: Io,
args: []const []const u8,
options: JitCmdOptions,
) !void {
@@ -5412,7 +5428,7 @@ fn jitCmd(
const target_query: std.Target.Query = .{};
const resolved_target: Package.Module.ResolvedTarget = .{
- .result = std.zig.resolveTargetQueryOrFatal(target_query),
+ .result = std.zig.resolveTargetQueryOrFatal(io, target_query),
.is_native_os = true,
.is_native_abi = true,
.is_explicit_dynamic_linker = false,
@@ -6209,7 +6225,7 @@ fn cmdAstCheck(
}
}
-fn cmdDetectCpu(args: []const []const u8) !void {
+fn cmdDetectCpu(io: Io, args: []const []const u8) !void {
dev.check(.detect_cpu_command);
const detect_cpu_usage =
@@ -6254,7 +6270,7 @@ fn cmdDetectCpu(args: []const []const u8) !void {
const cpu = try detectNativeCpuWithLLVM(builtin.cpu.arch, name, features);
try printCpu(cpu);
} else {
- const host_target = std.zig.resolveTargetQueryOrFatal(.{});
+ const host_target = std.zig.resolveTargetQueryOrFatal(io, .{});
try printCpu(host_target.cpu);
}
}
@@ -6521,13 +6537,14 @@ fn prefixedIntArg(arg: []const u8, prefix: []const u8) ?u64 {
}
fn warnAboutForeignBinaries(
+ io: Io,
arena: Allocator,
arg_mode: ArgMode,
target: *const std.Target,
link_libc: bool,
) !void {
const host_query: std.Target.Query = .{};
- const host_target = std.zig.resolveTargetQueryOrFatal(host_query);
+ const host_target = std.zig.resolveTargetQueryOrFatal(io, host_query);
switch (std.zig.system.getExternalExecutor(&host_target, target, .{ .link_libc = link_libc })) {
.native => return,
@@ -7080,7 +7097,7 @@ fn cmdFetch(
try fixups.append_string_after_node.put(gpa, manifest.version_node, dependencies_text);
}
- var aw: std.Io.Writer.Allocating = .init(gpa);
+ var aw: Io.Writer.Allocating = .init(gpa);
defer aw.deinit();
try ast.render(gpa, &aw.writer, fixups);
const rendered = aw.written();