Commit 5a4e8c779a

Andrew Kelley <andrew@ziglang.org>
2020-02-18 01:26:32
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
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 {