mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 05:44:20 +00:00
std.zig.system: upgrade to std.Io.Reader
This commit is contained in:
parent
b428612a20
commit
066864a0bf
12 changed files with 428 additions and 513 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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"),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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`.
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
166
lib/std/elf.zig
166
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;
|
||||
|
|
|
|||
|
|
@ -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)});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
{
|
||||
var it = header.iterateProgramHeaders(file_reader);
|
||||
while (try it.next()) |phdr| switch (phdr.p_type) {
|
||||
elf.PT_INTERP => {
|
||||
got_dyn_section = true;
|
||||
|
||||
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_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);
|
||||
|
||||
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;
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
},
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
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,
|
||||
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 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,
|
||||
};
|
||||
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();
|
||||
defer if (!is_elf_file) 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;
|
||||
file_reader = .initAdapted(file, io, &file_reader_buffer);
|
||||
file_name = undefined; // it aliases file_reader_buffer
|
||||
|
||||
const len = preadAtLeast(file, &buffer, 0, min_len) catch |err| switch (err) {
|
||||
error.UnexpectedEndOfFile,
|
||||
error.UnableToReadElfFile,
|
||||
error.ProcessNotFound,
|
||||
=> return defaultAbiAndDynamicLinker(cpu, os, query),
|
||||
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.
|
||||
|
||||
else => |e| return e,
|
||||
// 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;
|
||||
},
|
||||
|
||||
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;
|
||||
|
||||
|
|
|
|||
89
src/main.zig
89
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();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue