Commit 500dde32d5

Andrew Kelley <andrew@ziglang.org>
2020-02-28 19:27:52
dynamic_linker becomes a field of std.zig.CrossTarget
1 parent 07f5211
lib/std/zig/cross_target.zig
@@ -40,6 +40,10 @@ pub const CrossTarget = struct {
     /// If `isGnuLibC()` is `false`, this must be `null` and is ignored.
     glibc_version: ?SemVer = null,
 
+    /// When `os_tag` is `null`, then `null` means native. Otherwise it means the standard path
+    /// based on the `os_tag`.
+    dynamic_linker: DynamicLinker = DynamicLinker{},
+
     pub const OsVersion = union(enum) {
         none: void,
         semver: SemVer,
@@ -48,6 +52,8 @@ pub const CrossTarget = struct {
 
     pub const SemVer = std.builtin.Version;
 
+    pub const DynamicLinker = Target.DynamicLinker;
+
     pub fn fromTarget(target: Target) CrossTarget {
         var result: CrossTarget = .{
             .cpu_arch = target.cpu.arch,
@@ -170,6 +176,10 @@ pub const CrossTarget = struct {
         /// parsed CPU Architecture. If native, then this will be "native". Otherwise, it will be "baseline".
         cpu_features: ?[]const u8 = null,
 
+        /// Absolute path to dynamic linker, to override the default, which is either a natively
+        /// detected path, or a standard path.
+        dynamic_linker: ?[]const u8 = null,
+
         /// If this is provided, the function will populate some information about parsing failures,
         /// so that user-friendly error messages can be delivered.
         diagnostics: ?*Diagnostics = null,
@@ -199,8 +209,9 @@ pub const CrossTarget = struct {
         var dummy_diags: ParseOptions.Diagnostics = undefined;
         const diags = args.diagnostics orelse &dummy_diags;
 
-        // Start with everything initialized to default values.
-        var result: CrossTarget = .{};
+        var result: CrossTarget = .{
+            .dynamic_linker = DynamicLinker.init(args.dynamic_linker),
+        };
 
         var it = mem.separate(args.arch_os_abi, "-");
         const arch_name = it.next().?;
@@ -446,7 +457,7 @@ pub const CrossTarget = struct {
         return self.cpu_arch == null and self.cpu_model == null and
             self.cpu_features_sub.isEmpty() and self.cpu_features_add.isEmpty() and
             self.os_tag == null and self.os_version_min == null and self.os_version_max == null and
-            self.abi == null;
+            self.abi == null and self.dynamic_linker.get() == null;
     }
 
     pub fn zigTriple(self: CrossTarget, allocator: *mem.Allocator) error{OutOfMemory}![:0]u8 {
lib/std/zig/system.zig
@@ -168,14 +168,9 @@ pub const NativePaths = struct {
 pub const NativeTargetInfo = struct {
     target: Target,
 
-    /// Contains the memory used to store the dynamic linker path. This field should
-    /// not be used directly. See `dynamicLinker` and `setDynamicLinker`. This field
-    /// exists so that this API requires no allocator.
-    dynamic_linker_buffer: [255]u8 = undefined,
+    dynamic_linker: DynamicLinker = DynamicLinker{},
 
-    /// Used to construct the dynamic linker path. This field should not be used
-    /// directly. See `dynamicLinker` and `setDynamicLinker`.
-    dynamic_linker_max: ?u8 = null,
+    pub const DynamicLinker = Target.DynamicLinker;
 
     pub const DetectError = error{
         OutOfMemory,
@@ -220,21 +215,6 @@ pub const NativeTargetInfo = struct {
         return detectAbiAndDynamicLinker(allocator, cpu, os);
     }
 
-    /// The returned memory has the same lifetime as the `NativeTargetInfo`.
-    pub fn dynamicLinker(self: *const NativeTargetInfo) ?[]const u8 {
-        const m: usize = self.dynamic_linker_max orelse return null;
-        return self.dynamic_linker_buffer[0 .. m + 1];
-    }
-
-    pub fn setDynamicLinker(self: *NativeTargetInfo, dl_or_null: ?[]const u8) void {
-        if (dl_or_null) |dl| {
-            mem.copy(u8, &self.dynamic_linker_buffer, dl);
-            self.dynamic_linker_max = @intCast(u8, dl.len - 1);
-        } else {
-            self.dynamic_linker_max = null;
-        }
-    }
-
     /// 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. If that does not provide the answer, then
@@ -273,15 +253,14 @@ pub const NativeTargetInfo = struct {
                 .os = os,
                 .abi = abi,
             };
-            const ld_info = &ld_info_list_buffer[ld_info_list_len];
-            ld_info_list_len += 1;
+            const ld = target.standardDynamicLinkerPath();
+            if (ld.get() == null) continue;
 
-            ld_info.* = .{
-                .ld_path_buffer = undefined,
-                .ld_path_max = undefined,
+            ld_info_list_buffer[ld_info_list_len] = .{
+                .ld = ld,
                 .abi = abi,
             };
-            ld_info.ld_path_max = target.standardDynamicLinkerPath(&ld_info.ld_path_buffer) orelse continue;
+            ld_info_list_len += 1;
         }
         const ld_info_list = ld_info_list_buffer[0..ld_info_list_len];
 
@@ -298,7 +277,7 @@ pub const NativeTargetInfo = struct {
             // 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.ldPath());
+                    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;
@@ -329,8 +308,8 @@ pub const NativeTargetInfo = struct {
                     .os = os_adjusted,
                     .abi = found_ld_info.abi,
                 },
+                .dynamic_linker = DynamicLinker.init(found_ld_path),
             };
-            result.setDynamicLinker(found_ld_path);
             return result;
         }
 
@@ -472,18 +451,18 @@ pub const NativeTargetInfo = struct {
                     elf.PT_INTERP => {
                         const p_offset = elfInt(is_64, need_bswap, ph32.p_offset, ph64.p_offset);
                         const p_filesz = elfInt(is_64, need_bswap, ph32.p_filesz, ph64.p_filesz);
-                        if (p_filesz > result.dynamic_linker_buffer.len) return error.NameTooLong;
-                        _ = try preadFull(env_file, result.dynamic_linker_buffer[0..p_filesz], p_offset, p_filesz);
+                        if (p_filesz > result.dynamic_linker.buffer.len) return error.NameTooLong;
+                        _ = try preadFull(env_file, result.dynamic_linker.buffer[0..p_filesz], p_offset, p_filesz);
                         // PT_INTERP includes a null byte in p_filesz.
                         const len = p_filesz - 1;
-                        // dynamic_linker_max is "max", not "len".
-                        // We know it will fit in u8 because we check against dynamic_linker_buffer.len above.
-                        result.dynamic_linker_max = @intCast(u8, len - 1);
+                        // dynamic_linker.max_byte is "max", not "len".
+                        // We know it will fit in u8 because we check against dynamic_linker.buffer.len above.
+                        result.dynamic_linker.max_byte = @intCast(u8, len - 1);
 
                         // Use it to determine ABI.
-                        const full_ld_path = result.dynamic_linker_buffer[0..len];
+                        const full_ld_path = result.dynamic_linker.buffer[0..len];
                         for (ld_info_list) |ld_info| {
-                            const standard_ld_basename = fs.path.basename(ld_info.ldPath());
+                            const standard_ld_basename = fs.path.basename(ld_info.ld.get().?);
                             if (std.mem.endsWith(u8, full_ld_path, standard_ld_basename)) {
                                 result.target.abi = ld_info.abi;
                                 break;
@@ -679,26 +658,20 @@ pub const NativeTargetInfo = struct {
     }
 
     fn defaultAbiAndDynamicLinker(cpu: Target.Cpu, os: Target.Os) !NativeTargetInfo {
-        var result: NativeTargetInfo = .{
-            .target = .{
-                .cpu = cpu,
-                .os = os,
-                .abi = Target.Abi.default(cpu.arch, os),
-            },
+        const target: Target = .{
+            .cpu = cpu,
+            .os = os,
+            .abi = Target.Abi.default(cpu.arch, os),
+        };
+        return NativeTargetInfo{
+            .target = target,
+            .dynamic_linker = target.standardDynamicLinkerPath(),
         };
-        result.dynamic_linker_max = result.target.standardDynamicLinkerPath(&result.dynamic_linker_buffer);
-        return result;
     }
 
     const LdInfo = struct {
-        ld_path_buffer: [255]u8,
-        ld_path_max: u8,
+        ld: DynamicLinker,
         abi: Target.Abi,
-
-        pub fn ldPath(self: *const LdInfo) []const u8 {
-            const m: usize = self.ld_path_max;
-            return self.ld_path_buffer[0 .. m + 1];
-        }
     };
 
     fn elfInt(is_64: bool, need_bswap: bool, int_32: var, int_64: var) @TypeOf(int_64) {
lib/std/build.zig
@@ -1177,8 +1177,6 @@ pub const LibExeObjStep = struct {
     /// that contains the path `aarch64-linux-gnu/lib/ld-linux-aarch64.so.1`.
     glibc_multi_install_dir: ?[]const u8 = null,
 
-    dynamic_linker: ?[]const u8 = null,
-
     /// Position Independent Code
     force_pic: ?bool = null,
 
@@ -1978,6 +1976,11 @@ pub const LibExeObjStep = struct {
                 }
                 try zig_args.append(mcpu_buffer.toSliceConst());
             }
+
+            if (self.target.dynamic_linker.get()) |dynamic_linker| {
+                try zig_args.append("--dynamic-linker");
+                try zig_args.append(dynamic_linker);
+            }
         }
 
         if (self.linker_script) |linker_script| {
@@ -1985,11 +1988,6 @@ pub const LibExeObjStep = struct {
             zig_args.append(builder.pathFromRoot(linker_script)) catch unreachable;
         }
 
-        if (self.dynamic_linker) |dynamic_linker| {
-            try zig_args.append("--dynamic-linker");
-            try zig_args.append(dynamic_linker);
-        }
-
         if (self.version_script) |version_script| {
             try zig_args.append("--version-script");
             try zig_args.append(builder.pathFromRoot(version_script));
lib/std/target.zig
@@ -1099,16 +1099,52 @@ pub const Target = struct {
         }
     }
 
+    pub const DynamicLinker = struct {
+        /// Contains the memory used to store the dynamic linker path. This field should
+        /// not be used directly. See `get` and `set`. This field exists so that this API requires no allocator.
+        buffer: [255]u8 = undefined,
+
+        /// Used to construct the dynamic linker path. This field should not be used
+        /// directly. See `get` and `set`.
+        max_byte: ?u8 = null,
+
+        /// Asserts that the length is less than or equal to 255 bytes.
+        pub fn init(dl_or_null: ?[]const u8) DynamicLinker {
+            var result: DynamicLinker = undefined;
+            result.set(dl_or_null);
+            return result;
+        }
+
+        /// The returned memory has the same lifetime as the `DynamicLinker`.
+        pub fn get(self: *const DynamicLinker) ?[]const u8 {
+            const m: usize = self.max_byte orelse return null;
+            return self.buffer[0 .. m + 1];
+        }
+
+        /// Asserts that the length is less than or equal to 255 bytes.
+        pub fn set(self: *DynamicLinker, dl_or_null: ?[]const u8) void {
+            if (dl_or_null) |dl| {
+                mem.copy(u8, &self.buffer, dl);
+                self.max_byte = @intCast(u8, dl.len - 1);
+            } else {
+                self.max_byte = null;
+            }
+        }
+    };
+
     /// 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) DynamicLinker {
+        var result: DynamicLinker = .{};
         const S = struct {
-            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 print(r: *DynamicLinker, comptime fmt: []const u8, args: var) DynamicLinker {
+                r.max_byte = @intCast(u8, (std.fmt.bufPrint(&r.buffer, fmt, args) catch unreachable).len - 1);
+                return r.*;
             }
-            fn copy(b: *[255]u8, s: []const u8) u8 {
-                mem.copy(u8, b, s);
-                return @intCast(u8, s.len - 1);
+            fn copy(r: *DynamicLinker, s: []const u8) DynamicLinker {
+                mem.copy(u8, &r.buffer, s);
+                r.max_byte = @intCast(u8, s.len - 1);
+                return r.*;
             }
         };
         const print = S.print;
@@ -1116,7 +1152,7 @@ pub const Target = struct {
 
         if (self.isAndroid()) {
             const suffix = if (self.cpu.arch.ptrBitWidth() == 64) "64" else "";
-            return print(buffer, "/system/bin/linker{}", .{suffix});
+            return print(&result, "/system/bin/linker{}", .{suffix});
         }
 
         if (self.isMusl()) {
@@ -1130,28 +1166,28 @@ pub const Target = struct {
                 else => |arch| @tagName(arch),
             };
             const arch_suffix = if (is_arm and self.getFloatAbi() == .hard) "hf" else "";
-            return print(buffer, "/lib/ld-musl-{}{}.so.1", .{ arch_part, arch_suffix });
+            return print(&result, "/lib/ld-musl-{}{}.so.1", .{ arch_part, arch_suffix });
         }
 
         switch (self.os.tag) {
-            .freebsd => return copy(buffer, "/libexec/ld-elf.so.1"),
-            .netbsd => return copy(buffer, "/libexec/ld.elf_so"),
-            .dragonfly => return copy(buffer, "/libexec/ld-elf.so.2"),
+            .freebsd => return copy(&result, "/libexec/ld-elf.so.1"),
+            .netbsd => return copy(&result, "/libexec/ld.elf_so"),
+            .dragonfly => return copy(&result, "/libexec/ld-elf.so.2"),
             .linux => switch (self.cpu.arch) {
                 .i386,
                 .sparc,
                 .sparcel,
-                => return copy(buffer, "/lib/ld-linux.so.2"),
+                => return copy(&result, "/lib/ld-linux.so.2"),
 
-                .aarch64 => return copy(buffer, "/lib/ld-linux-aarch64.so.1"),
-                .aarch64_be => return copy(buffer, "/lib/ld-linux-aarch64_be.so.1"),
-                .aarch64_32 => return copy(buffer, "/lib/ld-linux-aarch64_32.so.1"),
+                .aarch64 => return copy(&result, "/lib/ld-linux-aarch64.so.1"),
+                .aarch64_be => return copy(&result, "/lib/ld-linux-aarch64_be.so.1"),
+                .aarch64_32 => return copy(&result, "/lib/ld-linux-aarch64_32.so.1"),
 
                 .arm,
                 .armeb,
                 .thumb,
                 .thumbeb,
-                => return copy(buffer, switch (self.getFloatAbi()) {
+                => return copy(&result, switch (self.getFloatAbi()) {
                     .hard => "/lib/ld-linux-armhf.so.3",
                     else => "/lib/ld-linux.so.3",
                 }),
@@ -1168,20 +1204,20 @@ pub const Target = struct {
                     };
                     const is_nan_2008 = mips.featureSetHas(self.cpu.features, .nan2008);
                     const loader = if (is_nan_2008) "ld-linux-mipsn8.so.1" else "ld.so.1";
-                    return print(buffer, "/lib{}/{}", .{ lib_suffix, loader });
+                    return print(&result, "/lib{}/{}", .{ lib_suffix, loader });
                 },
 
-                .powerpc => return copy(buffer, "/lib/ld.so.1"),
-                .powerpc64, .powerpc64le => return copy(buffer, "/lib64/ld64.so.2"),
-                .s390x => return copy(buffer, "/lib64/ld64.so.1"),
-                .sparcv9 => return copy(buffer, "/lib64/ld-linux.so.2"),
-                .x86_64 => return copy(buffer, switch (self.abi) {
+                .powerpc => return copy(&result, "/lib/ld.so.1"),
+                .powerpc64, .powerpc64le => return copy(&result, "/lib64/ld64.so.2"),
+                .s390x => return copy(&result, "/lib64/ld64.so.1"),
+                .sparcv9 => return copy(&result, "/lib64/ld-linux.so.2"),
+                .x86_64 => return copy(&result, switch (self.abi) {
                     .gnux32 => "/libx32/ld-linux-x32.so.2",
                     else => "/lib64/ld-linux-x86-64.so.2",
                 }),
 
-                .riscv32 => return copy(buffer, "/lib/ld-linux-riscv32-ilp32.so.1"),
-                .riscv64 => return copy(buffer, "/lib/ld-linux-riscv64-lp64.so.1"),
+                .riscv32 => return copy(&result, "/lib/ld-linux-riscv32-ilp32.so.1"),
+                .riscv64 => return copy(&result, "/lib/ld-linux-riscv64-lp64.so.1"),
 
                 // Architectures in this list have been verified as not having a standard
                 // dynamic linker path.
@@ -1191,7 +1227,7 @@ pub const Target = struct {
                 .bpfeb,
                 .nvptx,
                 .nvptx64,
-                => return null,
+                => return result,
 
                 // TODO go over each item in this list and either move it to the above list, or
                 // implement the standard dynamic linker path code for it.
@@ -1217,7 +1253,7 @@ pub const Target = struct {
                 .lanai,
                 .renderscript32,
                 .renderscript64,
-                => return null,
+                => return result,
             },
 
             // Operating systems in this list have been verified as not having a standard
@@ -1232,7 +1268,7 @@ pub const Target = struct {
             .emscripten,
             .wasi,
             .other,
-            => return null,
+            => return result,
 
             // TODO go over each item in this list and either move it to the above list, or
             // implement the standard dynamic linker path code for it.
@@ -1259,7 +1295,7 @@ pub const Target = struct {
             .amdpal,
             .hermit,
             .hurd,
-            => return null,
+            => return result,
         }
     }
 };
src/all_types.hpp
@@ -2255,7 +2255,6 @@ struct CodeGen {
     Buf *test_name_prefix;
     Buf *zig_lib_dir;
     Buf *zig_std_dir;
-    Buf *dynamic_linker_path;
     Buf *version_script_path;
 
     const char **llvm_argv;
src/codegen.cpp
@@ -8832,19 +8832,6 @@ static void init(CodeGen *g) {
     }
 }
 
-static void detect_dynamic_linker(CodeGen *g) {
-    if (g->dynamic_linker_path != nullptr)
-        return;
-    if (!g->have_dynamic_link)
-        return;
-    if (g->out_type == OutTypeObj || (g->out_type == OutTypeLib && !g->is_dynamic))
-        return;
-
-    if (g->zig_target->dynamic_linker != nullptr) {
-        g->dynamic_linker_path = buf_create_from_str(g->zig_target->dynamic_linker);
-    }
-}
-
 static void detect_libc(CodeGen *g) {
     Error err;
 
@@ -10285,6 +10272,9 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) {
         cache_int(ch, g->zig_target->glibc_or_darwin_version->minor);
         cache_int(ch, g->zig_target->glibc_or_darwin_version->patch);
     }
+    if (g->zig_target->dynamic_linker != nullptr) {
+        cache_str(ch, g->zig_target->dynamic_linker);
+    }
     cache_int(ch, detect_subsystem(g));
     cache_bool(ch, g->strip_debug_symbols);
     cache_bool(ch, g->is_test_build);
@@ -10325,7 +10315,6 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) {
         cache_str(ch, g->libc->msvc_lib_dir);
         cache_str(ch, g->libc->kernel32_lib_dir);
     }
-    cache_buf_opt(ch, g->dynamic_linker_path);
     cache_buf_opt(ch, g->version_script_path);
 
     // gen_c_objects appends objects to g->link_objects which we want to include in the hash
@@ -10422,7 +10411,6 @@ void codegen_build_and_link(CodeGen *g) {
     g->have_err_ret_tracing = detect_err_ret_tracing(g);
     g->have_sanitize_c = detect_sanitize_c(g);
     detect_libc(g);
-    detect_dynamic_linker(g);
 
     Buf digest = BUF_INIT;
     if (g->enable_cache) {
@@ -10619,7 +10607,6 @@ CodeGen *create_child_codegen(CodeGen *parent_gen, Buf *root_src_path, OutType o
     child_gen->verbose_cc = parent_gen->verbose_cc;
     child_gen->verbose_llvm_cpu_features = parent_gen->verbose_llvm_cpu_features;
     child_gen->llvm_argv = parent_gen->llvm_argv;
-    child_gen->dynamic_linker_path = parent_gen->dynamic_linker_path;
 
     codegen_set_strip(child_gen, parent_gen->strip_debug_symbols);
     child_gen->want_pic = parent_gen->have_pic ? WantPICEnabled : WantPICDisabled;
src/link.cpp
@@ -1751,9 +1751,9 @@ static void construct_linker_job_elf(LinkJob *lj) {
         }
 
         if (g->have_dynamic_link && (is_dyn_lib || g->out_type == OutTypeExe)) {
-            assert(g->dynamic_linker_path != nullptr);
+            assert(g->zig_target->dynamic_linker != nullptr);
             lj->args.append("-dynamic-linker");
-            lj->args.append(buf_ptr(g->dynamic_linker_path));
+            lj->args.append(g->zig_target->dynamic_linker);
         }
     }
 
src/main.cpp
@@ -401,7 +401,7 @@ static int main0(int argc, char **argv) {
     bool link_eh_frame_hdr = false;
     ErrColor color = ErrColorAuto;
     CacheOpt enable_cache = CacheOptAuto;
-    Buf *dynamic_linker = nullptr;
+    const char *dynamic_linker = nullptr;
     const char *libc_txt = nullptr;
     ZigList<const char *> clang_argv = {0};
     ZigList<const char *> lib_dirs = {0};
@@ -496,7 +496,7 @@ static int main0(int argc, char **argv) {
         os_path_join(get_zig_special_dir(zig_lib_dir), buf_create_from_str("build_runner.zig"), build_runner_path);
 
         ZigTarget target;
-        if ((err = target_parse_triple(&target, "native", nullptr))) {
+        if ((err = target_parse_triple(&target, "native", nullptr, nullptr))) {
             fprintf(stderr, "Unable to get native target: %s\n", err_str(err));
             return EXIT_FAILURE;
         }
@@ -766,7 +766,7 @@ static int main0(int argc, char **argv) {
                 } else if (strcmp(arg, "--name") == 0) {
                     out_name = argv[i];
                 } else if (strcmp(arg, "--dynamic-linker") == 0) {
-                    dynamic_linker = buf_create_from_str(argv[i]);
+                    dynamic_linker = argv[i];
                 } else if (strcmp(arg, "--libc") == 0) {
                     libc_txt = argv[i];
                 } else if (strcmp(arg, "-D") == 0) {
@@ -968,7 +968,7 @@ static int main0(int argc, char **argv) {
     init_all_targets();
 
     ZigTarget target;
-    if ((err = target_parse_triple(&target, target_string, mcpu))) {
+    if ((err = target_parse_triple(&target, target_string, mcpu, dynamic_linker))) {
         fprintf(stderr, "invalid target: %s\n"
                 "See `%s targets` to display valid targets.\n", err_str(err), arg0);
         return print_error_usage(arg0);
@@ -1193,7 +1193,6 @@ static int main0(int argc, char **argv) {
 
             codegen_set_strip(g, strip);
             g->is_dynamic = is_dynamic;
-            g->dynamic_linker_path = dynamic_linker;
             g->verbose_tokenize = verbose_tokenize;
             g->verbose_ast = verbose_ast;
             g->verbose_link = verbose_link;
@@ -1320,7 +1319,7 @@ static int main0(int argc, char **argv) {
                 return main_exit(root_progress_node, EXIT_SUCCESS);
             } else if (cmd == CmdTest) {
                 ZigTarget native;
-                if ((err = target_parse_triple(&native, "native", nullptr))) {
+                if ((err = target_parse_triple(&native, "native", nullptr, nullptr))) {
                     fprintf(stderr, "Unable to get native target: %s\n", err_str(err));
                     return EXIT_FAILURE;
                 }
src/stage2.cpp
@@ -191,7 +191,9 @@ static void get_native_target(ZigTarget *target) {
     }
 }
 
-Error stage2_target_parse(struct ZigTarget *target, const char *zig_triple, const char *mcpu) {
+Error stage2_target_parse(struct ZigTarget *target, const char *zig_triple, const char *mcpu,
+        const char *dynamic_linker)
+{
     Error err;
 
     if (zig_triple == nullptr) {
@@ -249,6 +251,9 @@ Error stage2_target_parse(struct ZigTarget *target, const char *zig_triple, cons
         target->cache_hash = "\n\n";
     }
 
+    if (dynamic_linker != nullptr) {
+        target->dynamic_linker = dynamic_linker;
+    }
     return ErrorNone;
 }
 
src/stage2.h
@@ -298,7 +298,8 @@ struct ZigTarget {
 };
 
 // ABI warning
-ZIG_EXTERN_C enum Error stage2_target_parse(struct ZigTarget *target, const char *zig_triple, const char *mcpu);
+ZIG_EXTERN_C enum Error stage2_target_parse(struct ZigTarget *target, const char *zig_triple, const char *mcpu,
+        const char *dynamic_linker);
 
 
 // ABI warning
src/target.cpp
@@ -410,8 +410,8 @@ Error target_parse_abi(ZigLLVM_EnvironmentType *out_abi, const char *abi_ptr, si
     return ErrorUnknownABI;
 }
 
-Error target_parse_triple(ZigTarget *target, const char *triple, const char *mcpu) {
-    return stage2_target_parse(target, triple, mcpu);
+Error target_parse_triple(ZigTarget *target, const char *triple, const char *mcpu, const char *dynamic_linker) {
+    return stage2_target_parse(target, triple, mcpu, dynamic_linker);
 }
 
 const char *target_arch_name(ZigLLVM_ArchType arch) {
src/target.hpp
@@ -41,7 +41,7 @@ enum CIntType {
     CIntTypeCount,
 };
 
-Error target_parse_triple(ZigTarget *target, const char *triple, const char *mcpu);
+Error target_parse_triple(ZigTarget *target, const char *triple, const char *mcpu, const char *dynamic_linker);
 Error target_parse_arch(ZigLLVM_ArchType *arch, const char *arch_ptr, size_t arch_len);
 Error target_parse_os(Os *os, const char *os_ptr, size_t os_len);
 Error target_parse_abi(ZigLLVM_EnvironmentType *abi, const char *abi_ptr, size_t abi_len);
src-self-hosted/stage2.zig
@@ -651,8 +651,9 @@ export fn stage2_target_parse(
     target: *Stage2Target,
     zig_triple: ?[*:0]const u8,
     mcpu: ?[*:0]const u8,
+    dynamic_linker: ?[*:0]const u8,
 ) Error {
-    stage2TargetParse(target, zig_triple, mcpu) catch |err| switch (err) {
+    stage2TargetParse(target, zig_triple, mcpu, dynamic_linker) catch |err| switch (err) {
         error.OutOfMemory => return .OutOfMemory,
         error.UnknownArchitecture => return .UnknownArchitecture,
         error.UnknownOperatingSystem => return .UnknownOperatingSystem,
@@ -676,14 +677,17 @@ fn stage2TargetParse(
     stage1_target: *Stage2Target,
     zig_triple_oz: ?[*:0]const u8,
     mcpu_oz: ?[*:0]const u8,
+    dynamic_linker_oz: ?[*:0]const u8,
 ) !void {
     const target: CrossTarget = if (zig_triple_oz) |zig_triple_z| blk: {
         const zig_triple = mem.toSliceConst(u8, zig_triple_z);
         const mcpu = if (mcpu_oz) |mcpu_z| mem.toSliceConst(u8, mcpu_z) else null;
+        const dynamic_linker = if (dynamic_linker_oz) |dl_z| mem.toSliceConst(u8, dl_z) else null;
         var diags: CrossTarget.ParseOptions.Diagnostics = .{};
         break :blk CrossTarget.parse(.{
             .arch_os_abi = zig_triple,
             .cpu_features = mcpu,
+            .dynamic_linker = dynamic_linker,
             .diagnostics = &diags,
         }) catch |err| switch (err) {
             error.UnknownCpuModel => {
@@ -1170,7 +1174,7 @@ fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8)
         if (cross_target.os_tag == null) {
             adjusted_target.os = detected_info.target.os;
 
-            if (detected_info.dynamicLinker()) |dl| {
+            if (detected_info.dynamic_linker.get()) |dl| {
                 have_native_dl = true;
                 dynamic_linker_ptr.* = try mem.dupeZ(std.heap.c_allocator, u8, dl);
             }
@@ -1182,11 +1186,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)) |m|
-            try mem.dupeZ(std.heap.c_allocator, u8, buf[0 .. @as(usize, m) + 1])
-        else
-            null;
+        const dl = adjusted_target.standardDynamicLinkerPath();
+        dynamic_linker_ptr.* = if (dl.get()) |s| try mem.dupeZ(std.heap.c_allocator, u8, s) else null;
     }
     return adjusted_target;
 }