Commit 3ee01c14ee
Changed files (6)
doc
lib
std
doc/docgen.zig
@@ -1210,7 +1210,7 @@ fn genHtml(
var env_map = try process.getEnvMap(allocator);
try env_map.put("ZIG_DEBUG_COLOR", "1");
- const host = try std.zig.system.NativeTargetInfo.detect(allocator, .{});
+ const host = try std.zig.system.NativeTargetInfo.detect(.{});
const builtin_code = try getBuiltinCode(allocator, &env_map, zig_exe);
for (toc.nodes) |node| {
@@ -1474,7 +1474,6 @@ fn genHtml(
.arch_os_abi = triple,
});
const target_info = try std.zig.system.NativeTargetInfo.detect(
- allocator,
cross_target,
);
switch (host.getExternalExecutor(target_info, .{
lib/std/build/EmulatableRunStep.zig
@@ -158,7 +158,7 @@ fn warnAboutForeignBinaries(step: *EmulatableRunStep) void {
const host_name = builder.host.target.zigTriple(builder.allocator) catch unreachable;
const foreign_name = artifact.target.zigTriple(builder.allocator) catch unreachable;
- const target_info = std.zig.system.NativeTargetInfo.detect(builder.allocator, artifact.target) catch unreachable;
+ const target_info = std.zig.system.NativeTargetInfo.detect(artifact.target) catch unreachable;
const need_cross_glibc = artifact.target.isGnuLibC() and artifact.is_linking_libc;
switch (builder.host.getExternalExecutor(target_info, .{
.qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null,
lib/std/zig/system/NativeTargetInfo.zig
@@ -37,8 +37,7 @@ pub const DetectError = error{
/// relative to that.
/// 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, cross_target: CrossTarget) DetectError!NativeTargetInfo {
+pub fn detect(cross_target: CrossTarget) DetectError!NativeTargetInfo {
var os = cross_target.getOsTag().defaultVersionRange(cross_target.getCpuArch());
if (cross_target.os_tag == null) {
switch (builtin.target.os.tag) {
@@ -199,7 +198,7 @@ pub fn detect(allocator: Allocator, cross_target: CrossTarget) DetectError!Nativ
} orelse backup_cpu_detection: {
break :backup_cpu_detection Target.Cpu.baseline(cpu_arch);
};
- var result = try detectAbiAndDynamicLinker(allocator, cpu, os, cross_target);
+ var result = try detectAbiAndDynamicLinker(cpu, os, cross_target);
// For x86, we need to populate some CPU feature flags depending on architecture
// and mode:
// * 16bit_mode => if the abi is code16
@@ -236,13 +235,20 @@ pub fn detect(allocator: Allocator, cross_target: CrossTarget) DetectError!Nativ
return result;
}
-/// First we attempt to use the executable's own binary. If it is dynamically
-/// 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 (or the file it references in shebang). If that does not provide the answer, then
-/// we fall back to the defaults.
-/// TODO Remove the Allocator requirement from this function.
+/// In the past, this function attempted to use the executable's own binary if it was dynamically
+/// linked to answer both the C ABI question and the dynamic linker question. However, this
+/// could be problematic on a system that uses a RUNPATH for the compiler binary, locking
+/// it to an older glibc version, while system binaries such as /usr/bin/env use a newer glibc
+/// version. The problem is that libc.so.6 glibc version will match that of the system while
+/// the dynamic linker will match that of the compiler binary. Executables with these versions
+/// mismatching will fail to run.
+///
+/// Therefore, this function works the same regardless of whether the compiler binary is
+/// dynamically or statically linked. It inspects `/usr/bin/env` as an ELF file to find the
+/// 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(
- allocator: Allocator,
cpu: Target.Cpu,
os: Target.Os,
cross_target: CrossTarget,
@@ -280,8 +286,8 @@ fn detectAbiAndDynamicLinker(
const ofmt = cross_target.ofmt orelse Target.ObjectFormat.default(os.tag, cpu.arch);
for (all_abis) |abi| {
- // This may be a nonsensical parameter. We detect this with error.UnknownDynamicLinkerPath and
- // skip adding it to `ld_info_list`.
+ // This may be a nonsensical parameter. We detect this with
+ // error.UnknownDynamicLinkerPath and skip adding it to `ld_info_list`.
const target: Target = .{
.cpu = cpu,
.os = os,
@@ -301,62 +307,6 @@ fn detectAbiAndDynamicLinker(
// Best case scenario: the executable is dynamically linked, and we can iterate
// over our own shared objects and find a dynamic linker.
- self_exe: {
- const lib_paths = try std.process.getSelfExeSharedLibPaths(allocator);
- defer {
- for (lib_paths) |lib_path| {
- allocator.free(lib_path);
- }
- allocator.free(lib_paths);
- }
-
- var found_ld_info: LdInfo = undefined;
- var found_ld_path: [:0]const u8 = undefined;
-
- // 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) |ld_info| {
- const standard_ld_basename = fs.path.basename(ld_info.ld.get().?);
- if (std.mem.endsWith(u8, lib_path, standard_ld_basename)) {
- found_ld_info = ld_info;
- found_ld_path = lib_path;
- break :find_ld;
- }
- }
- } else break :self_exe;
-
- // Look for glibc version.
- var os_adjusted = os;
- if (builtin.target.os.tag == .linux and found_ld_info.abi.isGnu() and
- cross_target.glibc_version == null)
- {
- for (lib_paths) |lib_path| {
- if (std.mem.endsWith(u8, lib_path, glibc_so_basename)) {
- os_adjusted.version_range.linux.glibc = glibcVerFromSo(lib_path) catch |err| switch (err) {
- error.GnuLibCVersionUnavailable => continue,
- else => |e| return e,
- };
- break;
- }
- }
- }
-
- var result: NativeTargetInfo = .{
- .target = .{
- .cpu = cpu,
- .os = os_adjusted,
- .abi = cross_target.abi orelse found_ld_info.abi,
- .ofmt = cross_target.ofmt orelse Target.ObjectFormat.default(os_adjusted.tag, cpu.arch),
- },
- .dynamic_linker = if (cross_target.dynamic_linker.get() == null)
- DynamicLinker.init(found_ld_path)
- else
- cross_target.dynamic_linker,
- };
- return result;
- }
-
const elf_file = blk: {
// 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,
@@ -452,56 +402,6 @@ fn detectAbiAndDynamicLinker(
const glibc_so_basename = "libc.so.6";
-fn glibcVerFromSo(so_path: [:0]const u8) !std.builtin.Version {
- const file = fs.openFileAbsolute(so_path, .{}) catch |err| switch (err) {
- // Contextually impossible errors.
- error.NoSpaceLeft => unreachable,
- error.NameTooLong => unreachable,
- error.PathAlreadyExists => unreachable,
- error.SharingViolation => unreachable,
- error.InvalidUtf8 => unreachable,
- error.BadPathName => unreachable,
- error.PipeBusy => unreachable,
- error.FileLocksNotSupported => unreachable,
- error.WouldBlock => unreachable,
- error.FileBusy => unreachable, // opened without write permissions
- error.NoDevice => unreachable, // not accessing special device
- error.InvalidHandle => unreachable, // should not be in the error set
- error.DeviceBusy => unreachable, // read-only
-
- // Errors that indicate a false negative may occur if we treat this as
- // not a libc shared object.
- error.ProcessFdQuotaExceeded => return error.ProcessFdQuotaExceeded,
- error.SystemFdQuotaExceeded => return error.SystemFdQuotaExceeded,
- error.SystemResources => return error.SystemResources,
- error.Unexpected => return error.Unexpected,
-
- // Errors that indicate this file is not a libc shared object.
- error.SymLinkLoop => return error.GnuLibCVersionUnavailable,
- error.IsDir => return error.GnuLibCVersionUnavailable,
- error.AccessDenied => return error.GnuLibCVersionUnavailable,
- error.FileNotFound => return error.GnuLibCVersionUnavailable,
- error.FileTooBig => return error.GnuLibCVersionUnavailable,
- error.NotDir => return error.GnuLibCVersionUnavailable,
- };
- defer file.close();
-
- return glibcVerFromSoFile(file) catch |err| switch (err) {
- error.InvalidElfMagic => return error.GnuLibCVersionUnavailable,
- error.InvalidElfEndian => return error.GnuLibCVersionUnavailable,
- error.InvalidElfClass => return error.GnuLibCVersionUnavailable,
- error.InvalidElfFile => return error.GnuLibCVersionUnavailable,
- error.InvalidElfVersion => return error.GnuLibCVersionUnavailable,
- error.InvalidGnuLibCVersion => return error.GnuLibCVersionUnavailable,
- error.UnexpectedEndOfFile => return error.GnuLibCVersionUnavailable,
- error.UnableToReadElfFile => return error.GnuLibCVersionUnavailable,
-
- error.SystemResources => return error.SystemResources,
- error.FileSystem => return error.FileSystem,
- error.Unexpected => return error.Unexpected,
- };
-}
-
fn glibcVerFromSoFile(file: fs.File) !std.builtin.Version {
var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 align(@alignOf(elf.Elf64_Ehdr)) = undefined;
_ = try preadMin(file, &hdr_buf, 0, hdr_buf.len);
lib/std/build.zig
@@ -171,7 +171,7 @@ pub const Builder = struct {
const env_map = try allocator.create(EnvMap);
env_map.* = try process.getEnvMap(allocator);
- const host = try NativeTargetInfo.detect(allocator, .{});
+ const host = try NativeTargetInfo.detect(.{});
const self = try allocator.create(Builder);
self.* = Builder{
@@ -1798,7 +1798,7 @@ pub const LibExeObjStep = struct {
}
fn computeOutFileNames(self: *LibExeObjStep) void {
- self.target_info = NativeTargetInfo.detect(self.builder.allocator, self.target) catch
+ self.target_info = NativeTargetInfo.detect(self.target) catch
unreachable;
const target = self.target_info.target;
src/main.zig
@@ -268,7 +268,7 @@ pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
} else if (mem.eql(u8, cmd, "init-lib")) {
return cmdInit(gpa, arena, cmd_args, .Lib);
} else if (mem.eql(u8, cmd, "targets")) {
- const info = try detectNativeTargetInfo(arena, .{});
+ const info = try detectNativeTargetInfo(.{});
const stdout = io.getStdOut().writer();
return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, info.target);
} else if (mem.eql(u8, cmd, "version")) {
@@ -2267,7 +2267,7 @@ fn buildOutputType(
}
const cross_target = try parseCrossTargetOrReportFatalError(arena, target_parse_options);
- const target_info = try detectNativeTargetInfo(gpa, cross_target);
+ const target_info = try detectNativeTargetInfo(cross_target);
if (target_info.target.os.tag != .freestanding) {
if (ensure_libc_on_non_freestanding)
@@ -3283,7 +3283,7 @@ fn runOrTest(
if (std.process.can_execv and arg_mode == .run and !watch) {
// execv releases the locks; no need to destroy the Compilation here.
const err = std.process.execv(gpa, argv.items);
- try warnAboutForeignBinaries(gpa, arena, arg_mode, target_info, link_libc);
+ try warnAboutForeignBinaries(arena, arg_mode, target_info, 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 (std.process.can_spawn) {
@@ -3300,7 +3300,7 @@ fn runOrTest(
}
const term = child.spawnAndWait() catch |err| {
- try warnAboutForeignBinaries(gpa, arena, arg_mode, target_info, link_libc);
+ try warnAboutForeignBinaries(arena, arg_mode, target_info, link_libc);
const cmd = try std.mem.join(arena, " ", argv.items);
fatal("the following command failed with '{s}':\n{s}", .{ @errorName(err), cmd });
};
@@ -3914,7 +3914,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
gimmeMoreOfThoseSweetSweetFileDescriptors();
const cross_target: std.zig.CrossTarget = .{};
- const target_info = try detectNativeTargetInfo(gpa, cross_target);
+ const target_info = try detectNativeTargetInfo(cross_target);
const exe_basename = try std.zig.binNameAlloc(arena, .{
.root_name = "build",
@@ -4956,8 +4956,8 @@ test "fds" {
gimmeMoreOfThoseSweetSweetFileDescriptors();
}
-fn detectNativeTargetInfo(gpa: Allocator, cross_target: std.zig.CrossTarget) !std.zig.system.NativeTargetInfo {
- return std.zig.system.NativeTargetInfo.detect(gpa, cross_target);
+fn detectNativeTargetInfo(cross_target: std.zig.CrossTarget) !std.zig.system.NativeTargetInfo {
+ return std.zig.system.NativeTargetInfo.detect(cross_target);
}
/// Indicate that we are now terminating with a successful exit code.
@@ -5320,14 +5320,13 @@ fn parseIntSuffix(arg: []const u8, prefix_len: usize) u64 {
}
fn warnAboutForeignBinaries(
- gpa: Allocator,
arena: Allocator,
arg_mode: ArgMode,
target_info: std.zig.system.NativeTargetInfo,
link_libc: bool,
) !void {
const host_cross_target: std.zig.CrossTarget = .{};
- const host_target_info = try detectNativeTargetInfo(gpa, host_cross_target);
+ const host_target_info = try detectNativeTargetInfo(host_cross_target);
switch (host_target_info.getExternalExecutor(target_info, .{ .link_libc = link_libc })) {
.native => return,
src/test.zig
@@ -1211,7 +1211,7 @@ pub const TestContext = struct {
}
fn run(self: *TestContext) !void {
- const host = try std.zig.system.NativeTargetInfo.detect(self.gpa, .{});
+ const host = try std.zig.system.NativeTargetInfo.detect(.{});
var progress = std.Progress{};
const root_node = progress.start("compiler", self.cases.items.len);
@@ -1300,7 +1300,7 @@ pub const TestContext = struct {
global_cache_directory: Compilation.Directory,
host: std.zig.system.NativeTargetInfo,
) !void {
- const target_info = try std.zig.system.NativeTargetInfo.detect(allocator, case.target);
+ const target_info = try std.zig.system.NativeTargetInfo.detect(case.target);
const target = target_info.target;
var arena_allocator = std.heap.ArenaAllocator.init(allocator);