Skip to content

Commit 5a4e8c7

Browse files
committed
smarter detectNativeDynamicLinker logic
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`.
1 parent b53afc5 commit 5a4e8c7

File tree

1 file changed

+77
-20
lines changed

1 file changed

+77
-20
lines changed

src-self-hosted/libc_installation.zig

Lines changed: 77 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,9 @@ fn ccPrintFileName(
526526

527527
var it = std.mem.tokenize(exec_res.stdout, "\n\r");
528528
const line = it.next() orelse return error.LibCRuntimeNotFound;
529+
// When this command fails, it returns exit code 0 and duplicates the input file name.
530+
// So we detect failure by checking if the output matches exactly the input.
531+
if (std.mem.eql(u8, line, o_file)) return error.LibCRuntimeNotFound;
529532
switch (want_dirname) {
530533
.full_path => return std.mem.dupeZ(allocator, u8, line),
531534
.only_dir => {
@@ -536,44 +539,98 @@ fn ccPrintFileName(
536539
}
537540

538541
/// Caller owns returned memory.
539-
pub fn detectNativeDynamicLinker(allocator: *Allocator) error{OutOfMemory, TargetHasNoDynamicLinker, UnknownDynamicLinkerPath}![:0]u8 {
542+
pub fn detectNativeDynamicLinker(allocator: *Allocator) error{
543+
OutOfMemory,
544+
TargetHasNoDynamicLinker,
545+
UnknownDynamicLinkerPath,
546+
}![:0]u8 {
540547
if (!comptime Target.current.hasDynamicLinker()) {
541548
return error.TargetHasNoDynamicLinker;
542549
}
543550

544-
const standard_ld_path = try std.Target.current.getStandardDynamicLinkerPath(allocator);
545-
var standard_ld_path_resource: ?[:0]u8 = standard_ld_path; // Set to null to avoid freeing it.
546-
defer if (standard_ld_path_resource) |s| allocator.free(s);
551+
// The current target's ABI cannot be relied on for this. For example, we may build the zig
552+
// compiler for target riscv64-linux-musl and provide a tarball for users to download.
553+
// A user could then run that zig compiler on riscv64-linux-gnu. This use case is well-defined
554+
// and supported by Zig. But that means that we must detect the system ABI here rather than
555+
// relying on `std.Target.current`.
547556

548-
const standard_ld_basename = fs.path.basename(standard_ld_path);
557+
const LdInfo = struct {
558+
ld_path: []u8,
559+
abi: Target.Abi,
560+
};
561+
var ld_info_list = std.ArrayList(LdInfo).init(allocator);
562+
defer {
563+
for (ld_info_list.toSlice()) |ld_info| allocator.free(ld_info.ld_path);
564+
ld_info_list.deinit();
565+
}
549566

567+
const all_abis = comptime blk: {
568+
const fields = std.meta.fields(Target.Abi);
569+
var array: [fields.len]Target.Abi = undefined;
570+
inline for (fields) |field, i| {
571+
array[i] = @field(Target.Abi, field.name);
572+
}
573+
break :blk array;
574+
};
575+
for (all_abis) |abi| {
576+
// This may be a nonsensical parameter. We detect this with error.UnknownDynamicLinkerPath and
577+
// skip adding it to `ld_info_list`.
578+
const target: Target = .{
579+
.Cross = .{
580+
.arch = Target.current.getArch(),
581+
.os = Target.current.getOs(),
582+
.abi = abi,
583+
.cpu_features = Target.current.getArch().getBaselineCpuFeatures(),
584+
},
585+
};
586+
const standard_ld_path = target.getStandardDynamicLinkerPath(allocator) catch |err| switch (err) {
587+
error.OutOfMemory => return error.OutOfMemory,
588+
error.UnknownDynamicLinkerPath, error.TargetHasNoDynamicLinker => continue,
589+
};
590+
errdefer allocator.free(standard_ld_path);
591+
try ld_info_list.append(.{
592+
.ld_path = standard_ld_path,
593+
.abi = abi,
594+
});
595+
}
596+
597+
// Best case scenario: the zig compiler is dynamically linked, and we can iterate
598+
// over our own shared objects and find a dynamic linker.
550599
{
551-
// Best case scenario: the current executable is dynamically linked, and we can iterate
552-
// over our own shared objects and find a dynamic linker.
553600
const lib_paths = try std.process.getSelfExeSharedLibPaths(allocator);
554601
defer allocator.free(lib_paths);
555602

603+
// This is O(N^M) but typical case here is N=2 and M=10.
556604
for (lib_paths) |lib_path| {
557-
if (std.mem.endsWith(u8, lib_path, standard_ld_basename)) {
558-
return std.mem.dupeZ(allocator, u8, lib_path);
605+
for (ld_info_list.toSlice()) |ld_info| {
606+
const standard_ld_basename = fs.path.basename(ld_info.ld_path);
607+
if (std.mem.endsWith(u8, lib_path, standard_ld_basename)) {
608+
return std.mem.dupeZ(allocator, u8, lib_path);
609+
}
559610
}
560611
}
561612
}
562613

563614
// If Zig is statically linked, such as via distributed binary static builds, the above
564615
// trick won't work. What are we left with? Try to run the system C compiler and get
565616
// it to tell us the dynamic linker path.
566-
return ccPrintFileName(allocator, standard_ld_basename, .full_path) catch |err| switch (err) {
567-
error.OutOfMemory => return error.OutOfMemory,
568-
error.LibCRuntimeNotFound,
569-
error.CCompilerExitCode,
570-
error.CCompilerCrashed,
571-
error.UnableToSpawnCCompiler,
572-
=> {
573-
standard_ld_path_resource = null; // Prevent freeing standard_ld_path.
574-
return standard_ld_path;
575-
},
576-
};
617+
// TODO: instead of this, look at the shared libs of /usr/bin/env.
618+
for (ld_info_list.toSlice()) |ld_info| {
619+
const standard_ld_basename = fs.path.basename(ld_info.ld_path);
620+
621+
const full_ld_path = ccPrintFileName(allocator, standard_ld_basename, .full_path) catch |err| switch (err) {
622+
error.OutOfMemory => return error.OutOfMemory,
623+
error.LibCRuntimeNotFound,
624+
error.CCompilerExitCode,
625+
error.CCompilerCrashed,
626+
error.UnableToSpawnCCompiler,
627+
=> continue,
628+
};
629+
return full_ld_path;
630+
}
631+
632+
// Finally, we fall back on the standard path.
633+
return Target.current.getStandardDynamicLinkerPath(allocator);
577634
}
578635

579636
const Search = struct {

0 commit comments

Comments
 (0)