From 4616af0ca459358ffa09ba27f9daa8527a38fd35 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 25 Feb 2020 01:52:27 -0500 Subject: introduce operating system version ranges as part of the target * re-introduce `std.build.Target` which is distinct from `std.Target`. `std.build.Target` wraps `std.Target` so that it can be annotated as "the native target" or an explicitly specified target. * `std.Target.Os` is moved to `std.Target.Os.Tag`. The former is now a struct which has the tag as well as version range information. * `std.elf` gains some more ELF header constants. * `std.Target.parse` gains the ability to parse operating system version ranges as well as glibc version. * Added `std.Target.isGnuLibC()`. * self-hosted dynamic linker detection and glibc version detection. This also adds the improved logic using `/usr/bin/env` rather than invoking the system C compiler to find the dynamic linker when zig is statically linked. Related: #2084 Note: this `/usr/bin/env` code is work-in-progress. * `-target-glibc` CLI option is removed in favor of the new `-target` syntax. Example: `-target x86_64-linux-gnu.2.27` closes #1907 --- lib/std/process.zig | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'lib/std/process.zig') diff --git a/lib/std/process.zig b/lib/std/process.zig index 0dab8bb64b..118b5d9ab4 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -36,7 +36,7 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap { var result = BufMap.init(allocator); errdefer result.deinit(); - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const ptr = os.windows.peb().ProcessParameters.Environment; var i: usize = 0; @@ -61,7 +61,7 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap { try result.setMove(key, value); } return result; - } else if (builtin.os == .wasi) { + } else if (builtin.os.tag == .wasi) { var environ_count: usize = undefined; var environ_buf_size: usize = undefined; @@ -137,7 +137,7 @@ pub const GetEnvVarOwnedError = error{ /// Caller must free returned memory. pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwnedError![]u8 { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const result_w = blk: { const key_w = try std.unicode.utf8ToUtf16LeWithNull(allocator, key); defer allocator.free(key_w); @@ -338,12 +338,12 @@ pub const ArgIteratorWindows = struct { }; pub const ArgIterator = struct { - const InnerType = if (builtin.os == .windows) ArgIteratorWindows else ArgIteratorPosix; + const InnerType = if (builtin.os.tag == .windows) ArgIteratorWindows else ArgIteratorPosix; inner: InnerType, pub fn init() ArgIterator { - if (builtin.os == .wasi) { + if (builtin.os.tag == .wasi) { // TODO: Figure out a compatible interface accomodating WASI @compileError("ArgIterator is not yet supported in WASI. Use argsAlloc and argsFree instead."); } @@ -355,7 +355,7 @@ pub const ArgIterator = struct { /// You must free the returned memory when done. pub fn next(self: *ArgIterator, allocator: *Allocator) ?(NextError![]u8) { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return self.inner.next(allocator); } else { return mem.dupe(allocator, u8, self.inner.next() orelse return null); @@ -380,7 +380,7 @@ pub fn args() ArgIterator { /// Caller must call argsFree on result. pub fn argsAlloc(allocator: *mem.Allocator) ![][]u8 { - if (builtin.os == .wasi) { + if (builtin.os.tag == .wasi) { var count: usize = undefined; var buf_size: usize = undefined; @@ -445,7 +445,7 @@ pub fn argsAlloc(allocator: *mem.Allocator) ![][]u8 { } pub fn argsFree(allocator: *mem.Allocator, args_alloc: []const []u8) void { - if (builtin.os == .wasi) { + if (builtin.os.tag == .wasi) { const last_item = args_alloc[args_alloc.len - 1]; const last_byte_addr = @ptrToInt(last_item.ptr) + last_item.len + 1; // null terminated const first_item_ptr = args_alloc[0].ptr; @@ -498,7 +498,7 @@ pub const UserInfo = struct { /// POSIX function which gets a uid from username. pub fn getUserInfo(name: []const u8) !UserInfo { - return switch (builtin.os) { + return switch (builtin.os.tag) { .linux, .macosx, .watchos, .tvos, .ios, .freebsd, .netbsd => posixGetUserInfo(name), else => @compileError("Unsupported OS"), }; @@ -591,7 +591,7 @@ pub fn posixGetUserInfo(name: []const u8) !UserInfo { } pub fn getBaseAddress() usize { - switch (builtin.os) { + switch (builtin.os.tag) { .linux => { const base = os.system.getauxval(std.elf.AT_BASE); if (base != 0) { @@ -615,7 +615,7 @@ pub fn getSelfExeSharedLibPaths(allocator: *Allocator) error{OutOfMemory}![][:0] .Dynamic => {}, } const List = std.ArrayList([:0]u8); - switch (builtin.os) { + switch (builtin.os.tag) { .linux, .freebsd, .netbsd, -- cgit v1.2.3 From fd006c1c74a43827a6f1c32e289ba57cafa874be Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 27 Feb 2020 17:20:40 -0500 Subject: std.zig.system.NativeTargetInfo.detect: almost no Allocator --- lib/std/process.zig | 4 +++ lib/std/target.zig | 12 ++++----- lib/std/zig/system.zig | 62 ++++++++++++++++++++++------------------------ src-self-hosted/stage2.zig | 4 +-- 4 files changed, 42 insertions(+), 40 deletions(-) (limited to 'lib/std/process.zig') diff --git a/lib/std/process.zig b/lib/std/process.zig index 118b5d9ab4..01b9947518 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -609,6 +609,10 @@ pub fn getBaseAddress() usize { } /// Caller owns the result value and each inner slice. +/// TODO Remove the `Allocator` requirement from this API, which will remove the `Allocator` +/// requirement from `std.zig.system.NativeTargetInfo.detect`. Most likely this will require +/// introducing a new, lower-level function which takes a callback function, and then this +/// function which takes an allocator can exist on top of it. pub fn getSelfExeSharedLibPaths(allocator: *Allocator) error{OutOfMemory}![][:0]u8 { switch (builtin.link_mode) { .Static => return &[_][:0]u8{}, diff --git a/lib/std/target.zig b/lib/std/target.zig index 33019b6bbe..379ae72afe 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -1084,16 +1084,16 @@ pub const Target = struct { } } - /// The result will be a slice of `buffer`, pointing at position 0. + /// The result will be a byte index *pointing at the final byte*. In other words, length minus one. /// A return value of `null` means the concept of a dynamic linker is not meaningful for that target. - pub fn standardDynamicLinkerPath(self: Target, buffer: *[255]u8) ?[]u8 { + pub fn standardDynamicLinkerPath(self: Target, buffer: *[255]u8) ?u8 { const S = struct { - fn print(b: *[255]u8, comptime fmt: []const u8, args: var) []u8 { - return std.fmt.bufPrint(b, fmt, args) catch unreachable; + fn print(b: *[255]u8, comptime fmt: []const u8, args: var) u8 { + return @intCast(u8, (std.fmt.bufPrint(b, fmt, args) catch unreachable).len - 1); } - fn copy(b: *[255]u8, s: []const u8) []u8 { + fn copy(b: *[255]u8, s: []const u8) u8 { mem.copy(u8, b, s); - return b[0..s.len]; + return @intCast(u8, s.len - 1); } }; const print = S.print; diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index d2828a88a1..e8c95f5125 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -189,7 +189,9 @@ pub const NativeTargetInfo = struct { /// Detects the native CPU model & features, operating system & version, and C ABI & dynamic linker. /// On Linux, this is additionally responsible for detecting the native glibc version when applicable. - /// TODO Remove the allocator requirement from this. + /// Any resources this function allocates are released before returning, and so there is no + /// deinitialization method. + /// TODO Remove the Allocator requirement from this function. pub fn detect(allocator: *Allocator) DetectError!NativeTargetInfo { const arch = Target.current.cpu.arch; const os_tag = Target.current.os.tag; @@ -203,15 +205,9 @@ pub const NativeTargetInfo = struct { return detectAbiAndDynamicLinker(allocator, cpu, os); } - /// Must be the same `Allocator` passed to `detect`. - pub fn deinit(self: *NativeTargetInfo, allocator: *Allocator) void { - if (self.dynamic_linker) |dl| allocator.free(dl); - self.* = undefined; - } - /// The returned memory has the same lifetime as the `NativeTargetInfo`. pub fn dynamicLinker(self: *const NativeTargetInfo) ?[]const u8 { - const m = self.dynamic_linker_max orelse return null; + const m: usize = self.dynamic_linker_max orelse return null; return self.dynamic_linker_buffer[0 .. m + 1]; } @@ -228,13 +224,14 @@ pub const NativeTargetInfo = struct { /// linked, then it should answer both the C ABI question and the dynamic linker question. /// If it is statically linked, then we try /usr/bin/env. If that does not provide the answer, then /// we fall back to the defaults. + /// TODO Remove the Allocator requirement from this function. fn detectAbiAndDynamicLinker( allocator: *Allocator, cpu: Target.Cpu, os: Target.Os, ) DetectError!NativeTargetInfo { if (!comptime Target.current.hasDynamicLinker()) { - return defaultAbiAndDynamicLinker(allocator, cpu, os); + return defaultAbiAndDynamicLinker(cpu, os); } // The current target's ABI cannot be relied on for this. For example, we may build the zig // compiler for target riscv64-linux-musl and provide a tarball for users to download. @@ -242,15 +239,15 @@ pub const NativeTargetInfo = struct { // and supported by Zig. But that means that we must detect the system ABI here rather than // relying on `Target.current`. const LdInfo = struct { - ld_path: []u8, + ld_path_buffer: [255]u8, + ld_path_max: u8, abi: Target.Abi, - }; - var ld_info_list = std.ArrayList(LdInfo).init(allocator); - defer { - for (ld_info_list.toSlice()) |ld_info| allocator.free(ld_info.ld_path); - ld_info_list.deinit(); - } + pub fn ldPath(self: *const @This()) []const u8 { + const m: usize = self.ld_path_max; + return self.ld_path_buffer[0 .. m + 1]; + } + }; const all_abis = comptime blk: { assert(@enumToInt(Target.Abi.none) == 0); const fields = std.meta.fields(Target.Abi)[1..]; @@ -260,6 +257,9 @@ pub const NativeTargetInfo = struct { } break :blk array; }; + var ld_info_list_buffer: [all_abis.len]LdInfo = undefined; + var ld_info_list_len: usize = 0; + for (all_abis) |abi| { // This may be a nonsensical parameter. We detect this with error.UnknownDynamicLinkerPath and // skip adding it to `ld_info_list`. @@ -268,17 +268,17 @@ pub const NativeTargetInfo = struct { .os = os, .abi = abi, }; - var buf: [255]u8 = undefined; - const standard_ld_path = if (target.standardDynamicLinkerPath(&buf)) |s| - try mem.dupe(allocator, u8, s) - else - continue; - errdefer allocator.free(standard_ld_path); - try ld_info_list.append(.{ - .ld_path = standard_ld_path, + const ld_info = &ld_info_list_buffer[ld_info_list_len]; + ld_info_list_len += 1; + + ld_info.* = .{ + .ld_path_buffer = undefined, + .ld_path_max = undefined, .abi = abi, - }); + }; + ld_info.ld_path_max = target.standardDynamicLinkerPath(&ld_info.ld_path_buffer) orelse continue; } + const ld_info_list = ld_info_list_buffer[0..ld_info_list_len]; // Best case scenario: the executable is dynamically linked, and we can iterate // over our own shared objects and find a dynamic linker. @@ -292,8 +292,8 @@ pub const NativeTargetInfo = struct { // Look for dynamic linker. // This is O(N^M) but typical case here is N=2 and M=10. find_ld: for (lib_paths) |lib_path| { - for (ld_info_list.toSlice()) |ld_info| { - const standard_ld_basename = fs.path.basename(ld_info.ld_path); + for (ld_info_list) |ld_info| { + const standard_ld_basename = fs.path.basename(ld_info.ldPath()); if (std.mem.endsWith(u8, lib_path, standard_ld_basename)) { found_ld_info = ld_info; found_ld_path = lib_path; @@ -355,7 +355,7 @@ pub const NativeTargetInfo = struct { error.UnexpectedEndOfFile, error.NameTooLong, // Finally, we fall back on the standard path. - => defaultAbiAndDynamicLinker(allocator, cpu, os), + => defaultAbiAndDynamicLinker(cpu, os), }; } @@ -502,7 +502,7 @@ pub const NativeTargetInfo = struct { }; } - fn defaultAbiAndDynamicLinker(allocator: *Allocator, cpu: Target.Cpu, os: Target.Os) !NativeTargetInfo { + fn defaultAbiAndDynamicLinker(cpu: Target.Cpu, os: Target.Os) !NativeTargetInfo { var result: NativeTargetInfo = .{ .target = .{ .cpu = cpu, @@ -510,9 +510,7 @@ pub const NativeTargetInfo = struct { .abi = Target.Abi.default(cpu.arch, os), }, }; - if (result.target.standardDynamicLinkerPath(&result.dynamic_linker_buffer)) |s| { - result.dynamic_linker_max = @intCast(u8, s.len - 1); - } + result.dynamic_linker_max = result.target.standardDynamicLinkerPath(&result.dynamic_linker_buffer); return result; } }; diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig index 5358c4fedc..cd3f49edfa 100644 --- a/src-self-hosted/stage2.zig +++ b/src-self-hosted/stage2.zig @@ -1170,8 +1170,8 @@ fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8) } if (!have_native_dl) { var buf: [255]u8 = undefined; - dynamic_linker_ptr.* = if (adjusted_target.standardDynamicLinkerPath(&buf)) |s| - try mem.dupeZ(std.heap.c_allocator, u8, s) + dynamic_linker_ptr.* = if (adjusted_target.standardDynamicLinkerPath(&buf)) |m| + try mem.dupeZ(std.heap.c_allocator, u8, buf[0 .. @as(usize, m) + 1]) else null; } -- cgit v1.2.3