Commit 5a4e8c779a
Changed files (1)
src-self-hosted
src-self-hosted/libc_installation.zig
@@ -526,6 +526,9 @@ fn ccPrintFileName(
var it = std.mem.tokenize(exec_res.stdout, "\n\r");
const line = it.next() orelse return error.LibCRuntimeNotFound;
+ // When this command fails, it returns exit code 0 and duplicates the input file name.
+ // So we detect failure by checking if the output matches exactly the input.
+ if (std.mem.eql(u8, line, o_file)) return error.LibCRuntimeNotFound;
switch (want_dirname) {
.full_path => return std.mem.dupeZ(allocator, u8, line),
.only_dir => {
@@ -536,26 +539,74 @@ fn ccPrintFileName(
}
/// Caller owns returned memory.
-pub fn detectNativeDynamicLinker(allocator: *Allocator) error{OutOfMemory, TargetHasNoDynamicLinker, UnknownDynamicLinkerPath}![:0]u8 {
+pub fn detectNativeDynamicLinker(allocator: *Allocator) error{
+ OutOfMemory,
+ TargetHasNoDynamicLinker,
+ UnknownDynamicLinkerPath,
+}![:0]u8 {
if (!comptime Target.current.hasDynamicLinker()) {
return error.TargetHasNoDynamicLinker;
}
- const standard_ld_path = try std.Target.current.getStandardDynamicLinkerPath(allocator);
- var standard_ld_path_resource: ?[:0]u8 = standard_ld_path; // Set to null to avoid freeing it.
- defer if (standard_ld_path_resource) |s| allocator.free(s);
+ // 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.
+ // A user could then run that zig compiler on riscv64-linux-gnu. This use case is well-defined
+ // and supported by Zig. But that means that we must detect the system ABI here rather than
+ // relying on `std.Target.current`.
- const standard_ld_basename = fs.path.basename(standard_ld_path);
+ const LdInfo = struct {
+ ld_path: []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();
+ }
+ const all_abis = comptime blk: {
+ const fields = std.meta.fields(Target.Abi);
+ var array: [fields.len]Target.Abi = undefined;
+ inline for (fields) |field, i| {
+ array[i] = @field(Target.Abi, field.name);
+ }
+ break :blk array;
+ };
+ for (all_abis) |abi| {
+ // This may be a nonsensical parameter. We detect this with error.UnknownDynamicLinkerPath and
+ // skip adding it to `ld_info_list`.
+ const target: Target = .{
+ .Cross = .{
+ .arch = Target.current.getArch(),
+ .os = Target.current.getOs(),
+ .abi = abi,
+ .cpu_features = Target.current.getArch().getBaselineCpuFeatures(),
+ },
+ };
+ const standard_ld_path = target.getStandardDynamicLinkerPath(allocator) catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ error.UnknownDynamicLinkerPath, error.TargetHasNoDynamicLinker => continue,
+ };
+ errdefer allocator.free(standard_ld_path);
+ try ld_info_list.append(.{
+ .ld_path = standard_ld_path,
+ .abi = abi,
+ });
+ }
+
+ // Best case scenario: the zig compiler is dynamically linked, and we can iterate
+ // over our own shared objects and find a dynamic linker.
{
- // Best case scenario: the current executable is dynamically linked, and we can iterate
- // over our own shared objects and find a dynamic linker.
const lib_paths = try std.process.getSelfExeSharedLibPaths(allocator);
defer allocator.free(lib_paths);
+ // This is O(N^M) but typical case here is N=2 and M=10.
for (lib_paths) |lib_path| {
- if (std.mem.endsWith(u8, lib_path, standard_ld_basename)) {
- return std.mem.dupeZ(allocator, u8, lib_path);
+ for (ld_info_list.toSlice()) |ld_info| {
+ const standard_ld_basename = fs.path.basename(ld_info.ld_path);
+ if (std.mem.endsWith(u8, lib_path, standard_ld_basename)) {
+ return std.mem.dupeZ(allocator, u8, lib_path);
+ }
}
}
}
@@ -563,17 +614,23 @@ pub fn detectNativeDynamicLinker(allocator: *Allocator) error{OutOfMemory, Targe
// If Zig is statically linked, such as via distributed binary static builds, the above
// trick won't work. What are we left with? Try to run the system C compiler and get
// it to tell us the dynamic linker path.
- return ccPrintFileName(allocator, standard_ld_basename, .full_path) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- error.LibCRuntimeNotFound,
- error.CCompilerExitCode,
- error.CCompilerCrashed,
- error.UnableToSpawnCCompiler,
- => {
- standard_ld_path_resource = null; // Prevent freeing standard_ld_path.
- return standard_ld_path;
- },
- };
+ // TODO: instead of this, look at the shared libs of /usr/bin/env.
+ for (ld_info_list.toSlice()) |ld_info| {
+ const standard_ld_basename = fs.path.basename(ld_info.ld_path);
+
+ const full_ld_path = ccPrintFileName(allocator, standard_ld_basename, .full_path) catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ error.LibCRuntimeNotFound,
+ error.CCompilerExitCode,
+ error.CCompilerCrashed,
+ error.UnableToSpawnCCompiler,
+ => continue,
+ };
+ return full_ld_path;
+ }
+
+ // Finally, we fall back on the standard path.
+ return Target.current.getStandardDynamicLinkerPath(allocator);
}
const Search = struct {