Commit c2898c436f

Andrew Kelley <andrew@ziglang.org>
2024-10-19 07:29:50
branch fixes
1 parent 5ca5403
lib/std/zig/target.zig
@@ -25,7 +25,7 @@ pub const available_libcs = [_]ArchOsAbi{
     .{ .arch = .thumbeb, .os = .linux, .abi = .musleabihf },
     .{ .arch = .aarch64, .os = .linux, .abi = .gnu, .glibc_min = .{ .major = 2, .minor = 17, .patch = 0 } },
     .{ .arch = .aarch64, .os = .linux, .abi = .musl },
-    .{ .arch = .aarch64, .os = .macos, .abi = .none, .os_ver = .{ .major = 11, .minor = 0, .patch = 0 } },
+    .{ .arch = .aarch64, .os = .macos, .abi = .none, .os_ver = .{ .major = 13, .minor = 0, .patch = 0 } },
     .{ .arch = .aarch64, .os = .windows, .abi = .gnu },
     .{ .arch = .aarch64_be, .os = .linux, .abi = .gnu, .glibc_min = .{ .major = 2, .minor = 17, .patch = 0 } },
     .{ .arch = .aarch64_be, .os = .linux, .abi = .musl },
@@ -74,7 +74,7 @@ pub const available_libcs = [_]ArchOsAbi{
     .{ .arch = .x86_64, .os = .linux, .abi = .gnu },
     .{ .arch = .x86_64, .os = .linux, .abi = .gnux32 },
     .{ .arch = .x86_64, .os = .linux, .abi = .musl },
-    .{ .arch = .x86_64, .os = .macos, .abi = .none, .os_ver = .{ .major = 10, .minor = 7, .patch = 0 } },
+    .{ .arch = .x86_64, .os = .macos, .abi = .none, .os_ver = .{ .major = 13, .minor = 0, .patch = 0 } },
     .{ .arch = .x86_64, .os = .windows, .abi = .gnu },
 };
 
src/link/Elf.zig
@@ -114,6 +114,10 @@ comment_merge_section_index: ?Merge.Section.Index = null,
 
 first_eflags: ?elf.Word = null,
 
+/// `--verbose-link` output.
+/// Initialized on creation, appended to as inputs are added, printed during `flush`.
+dump_argv_list: std.ArrayListUnmanaged([]const u8),
+
 const SectionIndexes = struct {
     copy_rel: ?u32 = null,
     dynamic: ?u32 = null,
@@ -338,6 +342,7 @@ pub fn createEmpty(
         .enable_new_dtags = options.enable_new_dtags,
         .print_icf_sections = options.print_icf_sections,
         .print_map = options.print_map,
+        .dump_argv_list = .empty,
     };
     if (use_llvm and comp.config.have_zcu) {
         self.llvm_object = try LlvmObject.create(arena, comp);
@@ -350,7 +355,7 @@ pub fn createEmpty(
     }
 
     // --verbose-link
-    if (comp.verbose_link) try self.dumpArgv(comp);
+    if (comp.verbose_link) try dumpArgvInit(self, arena);
 
     const is_obj = output_mode == .Obj;
     const is_obj_or_ar = is_obj or (output_mode == .Lib and link_mode == .static);
@@ -501,6 +506,7 @@ pub fn deinit(self: *Elf) void {
     self.rela_dyn.deinit(gpa);
     self.rela_plt.deinit(gpa);
     self.comdat_group_sections.deinit(gpa);
+    self.dump_argv_list.deinit(gpa);
 }
 
 pub fn getNavVAddr(self: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index, reloc_info: link.File.RelocInfo) !u64 {
@@ -753,12 +759,23 @@ pub fn allocateChunk(self: *Elf, args: struct {
 }
 
 pub fn loadInput(self: *Elf, input: link.Input) !void {
-    const gpa = self.base.comp.gpa;
-    const diags = &self.base.comp.link_diags;
+    const comp = self.base.comp;
+    const gpa = comp.gpa;
+    const diags = &comp.link_diags;
     const target = self.getTarget();
-    const debug_fmt_strip = self.base.comp.config.debug_format == .strip;
+    const debug_fmt_strip = comp.config.debug_format == .strip;
     const default_sym_version = self.default_sym_version;
 
+    if (comp.verbose_link) {
+        const argv = &self.dump_argv_list;
+        switch (input) {
+            .res => unreachable,
+            .dso_exact => |dso_exact| try argv.appendSlice(gpa, &.{ "-l", dso_exact.name }),
+            .object, .archive => |obj| try argv.append(gpa, try obj.path.toString(comp.arena)),
+            .dso => |dso| try argv.append(gpa, try dso.path.toString(comp.arena)),
+        }
+    }
+
     switch (input) {
         .res => unreachable,
         .dso_exact => @panic("TODO"),
@@ -790,6 +807,8 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
         if (use_lld) return;
     }
 
+    if (comp.verbose_link) Compilation.dump_argv(self.dump_argv_list.items);
+
     const sub_prog_node = prog_node.start("ELF Flush", 0);
     defer sub_prog_node.end();
 
@@ -965,291 +984,110 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
     if (diags.hasErrors()) return error.FlushFailure;
 }
 
-/// --verbose-link output
-fn dumpArgv(self: *Elf, comp: *Compilation) !void {
-    const gpa = self.base.comp.gpa;
-    var arena_allocator = std.heap.ArenaAllocator.init(gpa);
-    defer arena_allocator.deinit();
-    const arena = arena_allocator.allocator();
-
+fn dumpArgvInit(self: *Elf, arena: Allocator) !void {
+    const comp = self.base.comp;
+    const gpa = comp.gpa;
     const target = self.getTarget();
-    const link_mode = self.base.comp.config.link_mode;
-    const directory = self.base.emit.root_dir; // Just an alias to make it shorter to type.
-    const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path});
-    const module_obj_path: ?[]const u8 = if (self.base.zcu_object_sub_path) |path| blk: {
-        if (fs.path.dirname(full_out_path)) |dirname| {
-            break :blk try fs.path.join(arena, &.{ dirname, path });
-        } else {
-            break :blk path;
-        }
-    } else null;
+    const full_out_path = try self.base.emit.root_dir.join(arena, &[_][]const u8{self.base.emit.sub_path});
 
-    const crt_basenames = std.zig.LibCInstallation.CrtBasenames.get(.{
-        .target = target,
-        .link_libc = comp.config.link_libc,
-        .output_mode = comp.config.output_mode,
-        .link_mode = link_mode,
-        .pie = comp.config.pie,
-    });
-    const crt_paths: std.zig.LibCInstallation.CrtPaths = if (comp.libc_installation) |lci|
-        try lci.resolveCrtPaths(arena, crt_basenames, target)
-    else
-        .{};
-    const compiler_rt_path: ?[]const u8 = blk: {
-        if (comp.compiler_rt_lib) |x| break :blk try x.full_object_path.toString(arena);
-        if (comp.compiler_rt_obj) |x| break :blk try x.full_object_path.toString(arena);
-        break :blk null;
-    };
+    const argv = &self.dump_argv_list;
 
-    var argv = std.ArrayList([]const u8).init(arena);
-
-    try argv.append("zig");
+    try argv.append(gpa, "zig");
 
     if (self.base.isStaticLib()) {
-        try argv.append("ar");
+        try argv.append(gpa, "ar");
     } else {
-        try argv.append("ld");
+        try argv.append(gpa, "ld");
     }
 
     if (self.base.isObject()) {
-        try argv.append("-r");
+        try argv.append(gpa, "-r");
     }
 
-    try argv.append("-o");
-    try argv.append(full_out_path);
-
-    if (self.base.isRelocatable()) {
-        for (self.base.comp.link_inputs) |link_input| switch (link_input) {
-            .res => unreachable,
-            .dso => |dso| try argv.append(try dso.path.toString(arena)),
-            .object, .archive => |obj| try argv.append(try obj.path.toString(arena)),
-            .dso_exact => |dso_exact| {
-                assert(dso_exact.name[0] == ':');
-                try argv.appendSlice(&.{ "-l", dso_exact.name });
-            },
-        };
-
-        for (comp.c_object_table.keys()) |key| {
-            try argv.append(try key.status.success.object_path.toString(arena));
-        }
+    try argv.append(gpa, "-o");
+    try argv.append(gpa, full_out_path);
 
-        if (module_obj_path) |p| {
-            try argv.append(p);
-        }
-    } else {
+    if (!self.base.isRelocatable()) {
         if (!self.base.isStatic()) {
             if (target.dynamic_linker.get()) |path| {
-                try argv.append("-dynamic-linker");
-                try argv.append(path);
+                try argv.appendSlice(gpa, &.{ "-dynamic-linker", try arena.dupe(u8, path) });
             }
         }
 
         if (self.base.isDynLib()) {
             if (self.soname) |name| {
-                try argv.append("-soname");
-                try argv.append(name);
+                try argv.append(gpa, "-soname");
+                try argv.append(gpa, name);
             }
         }
 
         if (self.entry_name) |name| {
-            try argv.appendSlice(&.{ "--entry", name });
+            try argv.appendSlice(gpa, &.{ "--entry", name });
         }
 
         for (self.rpath_table.keys()) |rpath| {
-            try argv.appendSlice(&.{ "-rpath", rpath });
+            try argv.appendSlice(gpa, &.{ "-rpath", rpath });
         }
 
-        try argv.appendSlice(&.{
+        try argv.appendSlice(gpa, &.{
             "-z",
             try std.fmt.allocPrint(arena, "stack-size={d}", .{self.base.stack_size}),
         });
 
-        try argv.append(try std.fmt.allocPrint(arena, "--image-base={d}", .{self.image_base}));
+        try argv.append(gpa, try std.fmt.allocPrint(arena, "--image-base={d}", .{self.image_base}));
 
         if (self.base.gc_sections) {
-            try argv.append("--gc-sections");
+            try argv.append(gpa, "--gc-sections");
         }
 
         if (self.base.print_gc_sections) {
-            try argv.append("--print-gc-sections");
+            try argv.append(gpa, "--print-gc-sections");
         }
 
         if (comp.link_eh_frame_hdr) {
-            try argv.append("--eh-frame-hdr");
+            try argv.append(gpa, "--eh-frame-hdr");
         }
 
         if (comp.config.rdynamic) {
-            try argv.append("--export-dynamic");
+            try argv.append(gpa, "--export-dynamic");
         }
 
         if (self.z_notext) {
-            try argv.append("-z");
-            try argv.append("notext");
+            try argv.append(gpa, "-z");
+            try argv.append(gpa, "notext");
         }
 
         if (self.z_nocopyreloc) {
-            try argv.append("-z");
-            try argv.append("nocopyreloc");
+            try argv.append(gpa, "-z");
+            try argv.append(gpa, "nocopyreloc");
         }
 
         if (self.z_now) {
-            try argv.append("-z");
-            try argv.append("now");
+            try argv.append(gpa, "-z");
+            try argv.append(gpa, "now");
         }
 
         if (self.base.isStatic()) {
-            try argv.append("-static");
+            try argv.append(gpa, "-static");
         } else if (self.isEffectivelyDynLib()) {
-            try argv.append("-shared");
+            try argv.append(gpa, "-shared");
         }
 
         if (comp.config.pie and self.base.isExe()) {
-            try argv.append("-pie");
+            try argv.append(gpa, "-pie");
         }
 
         if (comp.config.debug_format == .strip) {
-            try argv.append("-s");
+            try argv.append(gpa, "-s");
         }
 
-        if (crt_paths.crt0) |path| try argv.append(try path.toString(arena));
-        if (crt_paths.crti) |path| try argv.append(try path.toString(arena));
-        if (crt_paths.crtbegin) |path| try argv.append(try path.toString(arena));
-
         if (comp.config.link_libc) {
-            if (self.base.comp.libc_installation) |libc_installation| {
-                try argv.append("-L");
-                try argv.append(libc_installation.crt_dir.?);
+            if (self.base.comp.libc_installation) |lci| {
+                try argv.append(gpa, "-L");
+                try argv.append(gpa, lci.crt_dir.?);
             }
         }
-
-        var whole_archive = false;
-
-        for (self.base.comp.link_inputs) |link_input| switch (link_input) {
-            .res => unreachable,
-            .dso => continue,
-            .object, .archive => |obj| {
-                if (obj.must_link and !whole_archive) {
-                    try argv.append("-whole-archive");
-                    whole_archive = true;
-                } else if (!obj.must_link and whole_archive) {
-                    try argv.append("-no-whole-archive");
-                    whole_archive = false;
-                }
-                try argv.append(try obj.path.toString(arena));
-            },
-            .dso_exact => |dso_exact| {
-                assert(dso_exact.name[0] == ':');
-                try argv.appendSlice(&.{ "-l", dso_exact.name });
-            },
-        };
-
-        if (whole_archive) {
-            try argv.append("-no-whole-archive");
-            whole_archive = false;
-        }
-
-        for (comp.c_object_table.keys()) |key| {
-            try argv.append(try key.status.success.object_path.toString(arena));
-        }
-
-        if (module_obj_path) |p| {
-            try argv.append(p);
-        }
-
-        if (comp.config.any_sanitize_thread) {
-            try argv.append(try comp.tsan_lib.?.full_object_path.toString(arena));
-        }
-
-        if (comp.config.any_fuzz) {
-            try argv.append(try comp.fuzzer_lib.?.full_object_path.toString(arena));
-        }
-
-        // libc
-        if (!comp.skip_linker_dependencies and !comp.config.link_libc) {
-            if (comp.libc_static_lib) |lib| {
-                try argv.append(try lib.full_object_path.toString(arena));
-            }
-        }
-
-        // Shared libraries.
-        // Worst-case, we need an --as-needed argument for every lib, as well
-        // as one before and one after.
-        argv.appendAssumeCapacity("--as-needed");
-        var as_needed = true;
-
-        for (self.base.comp.link_inputs) |link_input| switch (link_input) {
-            .object, .archive, .dso_exact => continue,
-            .dso => |dso| {
-                const lib_as_needed = !dso.needed;
-                switch ((@as(u2, @intFromBool(lib_as_needed)) << 1) | @intFromBool(as_needed)) {
-                    0b00, 0b11 => {},
-                    0b01 => {
-                        try argv.append("--no-as-needed");
-                        as_needed = false;
-                    },
-                    0b10 => {
-                        try argv.append("--as-needed");
-                        as_needed = true;
-                    },
-                }
-                argv.appendAssumeCapacity(try dso.path.toString(arena));
-            },
-            .res => unreachable,
-        };
-
-        if (!as_needed) {
-            argv.appendAssumeCapacity("--as-needed");
-            as_needed = true;
-        }
-
-        // libc++ dep
-        if (comp.config.link_libcpp) {
-            try argv.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena));
-            try argv.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena));
-        }
-
-        // libunwind dep
-        if (comp.config.link_libunwind) {
-            try argv.append(try comp.libunwind_static_lib.?.full_object_path.toString(arena));
-        }
-
-        // libc dep
-        if (comp.config.link_libc) {
-            if (self.base.comp.libc_installation != null) {
-                const needs_grouping = link_mode == .static;
-                if (needs_grouping) try argv.append("--start-group");
-                try argv.appendSlice(target_util.libcFullLinkFlags(target));
-                if (needs_grouping) try argv.append("--end-group");
-            } else if (target.isGnuLibC()) {
-                for (glibc.libs) |lib| {
-                    if (lib.removed_in) |rem_in| {
-                        if (target.os.version_range.linux.glibc.order(rem_in) != .lt) continue;
-                    }
-
-                    const lib_path = try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{
-                        comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
-                    });
-                    try argv.append(lib_path);
-                }
-                try argv.append(try comp.crtFileAsString(arena, "libc_nonshared.a"));
-            } else if (target.isMusl()) {
-                try argv.append(try comp.crtFileAsString(arena, switch (link_mode) {
-                    .static => "libc.a",
-                    .dynamic => "libc.so",
-                }));
-            }
-        }
-
-        // compiler-rt
-        if (compiler_rt_path) |p| {
-            try argv.append(p);
-        }
-
-        if (crt_paths.crtend) |path| try argv.append(try path.toString(arena));
-        if (crt_paths.crtn) |path| try argv.append(try path.toString(arena));
     }
-
-    Compilation.dump_argv(argv.items);
 }
 
 pub const ParseError = error{
@@ -2177,7 +2015,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
                             if (target.os.version_range.linux.glibc.order(rem_in) != .lt) continue;
                         }
 
-                        const lib_path = try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{
+                        const lib_path = try std.fmt.allocPrint(arena, "{}{c}lib{s}.so.{d}", .{
                             comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
                         });
                         try argv.append(lib_path);
src/Compilation.zig
@@ -49,6 +49,8 @@ pub const Config = @import("Compilation/Config.zig");
 gpa: Allocator,
 /// Arena-allocated memory, mostly used during initialization. However, it can
 /// be used for other things requiring the same lifetime as the `Compilation`.
+/// Not thread-safe - lock `mutex` if potentially accessing from multiple
+/// threads at once.
 arena: Allocator,
 /// Not every Compilation compiles .zig code! For example you could do `zig build-exe foo.o`.
 zcu: ?*Zcu,
@@ -1760,172 +1762,166 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
         .incremental => comp.bin_file != null,
     };
 
-    if (have_bin_emit and !comp.skip_linker_dependencies and target.ofmt != .c) {
-        if (target.isDarwin()) {
-            switch (target.abi) {
-                .none,
-                .simulator,
-                .macabi,
-                => {},
-                else => return error.LibCUnavailable,
-            }
-        }
-        // If we need to build glibc for the target, add work items for it.
-        // We go through the work queue so that building can be done in parallel.
-        // If linking against host libc installation, instead queue up jobs
-        // for loading those files in the linker.
-        if (comp.config.link_libc and is_exe_or_dyn_lib and target.ofmt != .c) {
-            if (comp.libc_installation) |lci| {
-                const basenames = LibCInstallation.CrtBasenames.get(.{
-                    .target = target,
-                    .link_libc = comp.config.link_libc,
-                    .output_mode = comp.config.output_mode,
-                    .link_mode = comp.config.link_mode,
-                    .pie = comp.config.pie,
-                });
-                const paths = try lci.resolveCrtPaths(arena, basenames, target);
+    if (have_bin_emit and target.ofmt != .c) {
+        if (!comp.skip_linker_dependencies) {
+            // If we need to build libc for the target, add work items for it.
+            // We go through the work queue so that building can be done in parallel.
+            // If linking against host libc installation, instead queue up jobs
+            // for loading those files in the linker.
+            if (comp.config.link_libc and is_exe_or_dyn_lib) {
+                if (comp.libc_installation) |lci| {
+                    const basenames = LibCInstallation.CrtBasenames.get(.{
+                        .target = target,
+                        .link_libc = comp.config.link_libc,
+                        .output_mode = comp.config.output_mode,
+                        .link_mode = comp.config.link_mode,
+                        .pie = comp.config.pie,
+                    });
+                    const paths = try lci.resolveCrtPaths(arena, basenames, target);
 
-                const fields = @typeInfo(@TypeOf(paths)).@"struct".fields;
-                try comp.link_task_queue.shared.ensureUnusedCapacity(gpa, fields.len);
-                inline for (fields) |field| {
-                    if (@field(paths, field.name)) |path| {
-                        comp.link_task_queue.shared.appendAssumeCapacity(.{ .load_object = path });
+                    const fields = @typeInfo(@TypeOf(paths)).@"struct".fields;
+                    try comp.link_task_queue.shared.ensureUnusedCapacity(gpa, fields.len);
+                    inline for (fields) |field| {
+                        if (@field(paths, field.name)) |path| {
+                            comp.link_task_queue.shared.appendAssumeCapacity(.{ .load_object = path });
+                        }
                     }
-                }
 
-                const flags = target_util.libcFullLinkFlags(target);
-                try comp.link_task_queue.shared.ensureUnusedCapacity(gpa, flags.len);
-                for (flags) |flag| {
-                    assert(mem.startsWith(u8, flag, "-l"));
-                    const lib_name = flag["-l".len..];
-                    const suffix = switch (comp.config.link_mode) {
-                        .static => target.staticLibSuffix(),
-                        .dynamic => target.dynamicLibSuffix(),
-                    };
-                    const sep = std.fs.path.sep_str;
-                    const lib_path = try std.fmt.allocPrint(arena, "{s}" ++ sep ++ "lib{s}{s}", .{
-                        lci.crt_dir.?, lib_name, suffix,
-                    });
-                    const resolved_path = Path.initCwd(lib_path);
-                    comp.link_task_queue.shared.appendAssumeCapacity(switch (comp.config.link_mode) {
-                        .static => .{ .load_archive = resolved_path },
-                        .dynamic => .{ .load_dso = resolved_path },
+                    const flags = target_util.libcFullLinkFlags(target);
+                    try comp.link_task_queue.shared.ensureUnusedCapacity(gpa, flags.len);
+                    for (flags) |flag| {
+                        assert(mem.startsWith(u8, flag, "-l"));
+                        const lib_name = flag["-l".len..];
+                        const suffix = switch (comp.config.link_mode) {
+                            .static => target.staticLibSuffix(),
+                            .dynamic => target.dynamicLibSuffix(),
+                        };
+                        const sep = std.fs.path.sep_str;
+                        const lib_path = try std.fmt.allocPrint(arena, "{s}" ++ sep ++ "lib{s}{s}", .{
+                            lci.crt_dir.?, lib_name, suffix,
+                        });
+                        const resolved_path = Path.initCwd(lib_path);
+                        comp.link_task_queue.shared.appendAssumeCapacity(switch (comp.config.link_mode) {
+                            .static => .{ .load_archive = resolved_path },
+                            .dynamic => .{ .load_dso = resolved_path },
+                        });
+                    }
+                } else if (target.isMusl() and !target.isWasm()) {
+                    if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
+
+                    if (musl.needsCrtiCrtn(target)) {
+                        try comp.queueJobs(&[_]Job{
+                            .{ .musl_crt_file = .crti_o },
+                            .{ .musl_crt_file = .crtn_o },
+                        });
+                    }
+                    try comp.queueJobs(&[_]Job{
+                        .{ .musl_crt_file = .crt1_o },
+                        .{ .musl_crt_file = .scrt1_o },
+                        .{ .musl_crt_file = .rcrt1_o },
+                        switch (comp.config.link_mode) {
+                            .static => .{ .musl_crt_file = .libc_a },
+                            .dynamic => .{ .musl_crt_file = .libc_so },
+                        },
                     });
-                }
-            } else if (target.isMusl() and !target.isWasm()) {
-                if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
-
-                if (musl.needsCrtiCrtn(target)) {
+                } else if (target.isGnuLibC()) {
+                    if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
+
+                    if (glibc.needsCrtiCrtn(target)) {
+                        try comp.queueJobs(&[_]Job{
+                            .{ .glibc_crt_file = .crti_o },
+                            .{ .glibc_crt_file = .crtn_o },
+                        });
+                    }
                     try comp.queueJobs(&[_]Job{
-                        .{ .musl_crt_file = .crti_o },
-                        .{ .musl_crt_file = .crtn_o },
+                        .{ .glibc_crt_file = .scrt1_o },
+                        .{ .glibc_crt_file = .libc_nonshared_a },
+                        .{ .glibc_shared_objects = {} },
                     });
-                }
-                try comp.queueJobs(&[_]Job{
-                    .{ .musl_crt_file = .crt1_o },
-                    .{ .musl_crt_file = .scrt1_o },
-                    .{ .musl_crt_file = .rcrt1_o },
-                    switch (comp.config.link_mode) {
-                        .static => .{ .musl_crt_file = .libc_a },
-                        .dynamic => .{ .musl_crt_file = .libc_so },
-                    },
-                });
-            } else if (target.isGnuLibC()) {
-                if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
+                } else if (target.isWasm() and target.os.tag == .wasi) {
+                    if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
 
-                if (glibc.needsCrtiCrtn(target)) {
+                    for (comp.wasi_emulated_libs) |crt_file| {
+                        try comp.queueJob(.{
+                            .wasi_libc_crt_file = crt_file,
+                        });
+                    }
                     try comp.queueJobs(&[_]Job{
-                        .{ .glibc_crt_file = .crti_o },
-                        .{ .glibc_crt_file = .crtn_o },
+                        .{ .wasi_libc_crt_file = wasi_libc.execModelCrtFile(comp.config.wasi_exec_model) },
+                        .{ .wasi_libc_crt_file = .libc_a },
                     });
-                }
-                try comp.queueJobs(&[_]Job{
-                    .{ .glibc_crt_file = .scrt1_o },
-                    .{ .glibc_crt_file = .libc_nonshared_a },
-                    .{ .glibc_shared_objects = {} },
-                });
-            } else if (target.isWasm() and target.os.tag == .wasi) {
-                if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
+                } else if (target.isMinGW()) {
+                    if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
 
-                for (comp.wasi_emulated_libs) |crt_file| {
-                    try comp.queueJob(.{
-                        .wasi_libc_crt_file = crt_file,
+                    const crt_job: Job = .{ .mingw_crt_file = if (is_dyn_lib) .dllcrt2_o else .crt2_o };
+                    try comp.queueJobs(&.{
+                        .{ .mingw_crt_file = .mingw32_lib },
+                        crt_job,
                     });
-                }
-                try comp.queueJobs(&[_]Job{
-                    .{ .wasi_libc_crt_file = wasi_libc.execModelCrtFile(comp.config.wasi_exec_model) },
-                    .{ .wasi_libc_crt_file = .libc_a },
-                });
-            } else if (target.isMinGW()) {
-                if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
 
-                const crt_job: Job = .{ .mingw_crt_file = if (is_dyn_lib) .dllcrt2_o else .crt2_o };
-                try comp.queueJobs(&.{
-                    .{ .mingw_crt_file = .mingw32_lib },
-                    crt_job,
-                });
-
-                // When linking mingw-w64 there are some import libs we always need.
-                try comp.windows_libs.ensureUnusedCapacity(gpa, mingw.always_link_libs.len);
-                for (mingw.always_link_libs) |name| comp.windows_libs.putAssumeCapacity(name, {});
-            } else {
-                return error.LibCUnavailable;
+                    // When linking mingw-w64 there are some import libs we always need.
+                    try comp.windows_libs.ensureUnusedCapacity(gpa, mingw.always_link_libs.len);
+                    for (mingw.always_link_libs) |name| comp.windows_libs.putAssumeCapacity(name, {});
+                } else if (target.isDarwin()) {
+                    switch (target.abi) {
+                        .none, .simulator, .macabi => {},
+                        else => return error.LibCUnavailable,
+                    }
+                } else if (target.os.tag == .freestanding and capable_of_building_zig_libc) {
+                    try comp.queueJob(.{ .zig_libc = {} });
+                } else {
+                    return error.LibCUnavailable;
+                }
             }
-        }
 
-        // Generate Windows import libs.
-        if (target.os.tag == .windows) {
-            const count = comp.windows_libs.count();
-            for (0..count) |i| {
-                try comp.queueJob(.{ .windows_import_lib = i });
+            // Generate Windows import libs.
+            if (target.os.tag == .windows) {
+                const count = comp.windows_libs.count();
+                for (0..count) |i| {
+                    try comp.queueJob(.{ .windows_import_lib = i });
+                }
+            }
+            if (comp.wantBuildLibUnwindFromSource()) {
+                try comp.queueJob(.{ .libunwind = {} });
+            }
+            if (build_options.have_llvm and is_exe_or_dyn_lib and comp.config.link_libcpp) {
+                try comp.queueJob(.libcxx);
+                try comp.queueJob(.libcxxabi);
+            }
+            if (build_options.have_llvm and comp.config.any_sanitize_thread) {
+                try comp.queueJob(.libtsan);
             }
-        }
-        if (comp.wantBuildLibUnwindFromSource()) {
-            try comp.queueJob(.{ .libunwind = {} });
-        }
-        if (build_options.have_llvm and is_exe_or_dyn_lib and comp.config.link_libcpp) {
-            try comp.queueJob(.libcxx);
-            try comp.queueJob(.libcxxabi);
-        }
-        if (build_options.have_llvm and comp.config.any_sanitize_thread) {
-            try comp.queueJob(.libtsan);
-        }
-
-        if (target.isMinGW() and comp.config.any_non_single_threaded) {
-            // LLD might drop some symbols as unused during LTO and GCing, therefore,
-            // we force mark them for resolution here.
 
-            const tls_index_sym = switch (target.cpu.arch) {
-                .x86 => "__tls_index",
-                else => "_tls_index",
-            };
+            if (target.isMinGW() and comp.config.any_non_single_threaded) {
+                // LLD might drop some symbols as unused during LTO and GCing, therefore,
+                // we force mark them for resolution here.
 
-            try comp.force_undefined_symbols.put(comp.gpa, tls_index_sym, {});
-        }
+                const tls_index_sym = switch (target.cpu.arch) {
+                    .x86 => "__tls_index",
+                    else => "_tls_index",
+                };
 
-        if (comp.include_compiler_rt and capable_of_building_compiler_rt) {
-            if (is_exe_or_dyn_lib) {
-                log.debug("queuing a job to build compiler_rt_lib", .{});
-                comp.job_queued_compiler_rt_lib = true;
-            } else if (output_mode != .Obj) {
-                log.debug("queuing a job to build compiler_rt_obj", .{});
-                // In this case we are making a static library, so we ask
-                // for a compiler-rt object to put in it.
-                comp.job_queued_compiler_rt_obj = true;
+                try comp.force_undefined_symbols.put(comp.gpa, tls_index_sym, {});
             }
-        }
 
-        if (comp.config.any_fuzz and capable_of_building_compiler_rt) {
-            if (is_exe_or_dyn_lib) {
-                log.debug("queuing a job to build libfuzzer", .{});
-                comp.job_queued_fuzzer_lib = true;
+            if (comp.include_compiler_rt and capable_of_building_compiler_rt) {
+                if (is_exe_or_dyn_lib) {
+                    log.debug("queuing a job to build compiler_rt_lib", .{});
+                    comp.job_queued_compiler_rt_lib = true;
+                } else if (output_mode != .Obj) {
+                    log.debug("queuing a job to build compiler_rt_obj", .{});
+                    // In this case we are making a static library, so we ask
+                    // for a compiler-rt object to put in it.
+                    comp.job_queued_compiler_rt_obj = true;
+                }
             }
-        }
 
-        if (!comp.skip_linker_dependencies and is_exe_or_dyn_lib and
-            !comp.config.link_libc and capable_of_building_zig_libc)
-        {
-            try comp.queueJob(.{ .zig_libc = {} });
+            if (comp.config.any_fuzz and capable_of_building_compiler_rt) {
+                if (is_exe_or_dyn_lib) {
+                    log.debug("queuing a job to build libfuzzer", .{});
+                    comp.job_queued_fuzzer_lib = true;
+                }
+            }
         }
 
         try comp.link_task_queue.shared.append(gpa, .load_explicitly_provided);
@@ -3521,7 +3517,7 @@ fn performAllTheWorkInner(
     defer work_queue_wait_group.wait();
 
     if (comp.bin_file) |lf| {
-        if (try comp.link_task_queue.enqueue(comp.gpa, &.{.load_explicitly_provided})) {
+        if (comp.link_task_queue.start()) {
             comp.thread_pool.spawnWg(work_queue_wait_group, link.File.flushTaskQueue, .{ lf, main_progress_node });
         }
     }
@@ -4980,7 +4976,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
         },
     };
 
-    comp.enqueueLinkTasks(&.{.{ .load_object = c_object.status.success.object_path }});
+    comp.queueLinkTasks(&.{.{ .load_object = c_object.status.success.object_path }});
 }
 
 fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32_resource_prog_node: std.Progress.Node) !void {
@@ -6114,7 +6110,7 @@ fn wantBuildLibUnwindFromSource(comp: *Compilation) bool {
     return is_exe_or_dyn_lib and comp.config.link_libunwind and ofmt != .c;
 }
 
-fn setAllocFailure(comp: *Compilation) void {
+pub fn setAllocFailure(comp: *Compilation) void {
     @branchHint(.cold);
     log.debug("memory allocation failure", .{});
     comp.alloc_failure_occurred = true;
@@ -6355,7 +6351,7 @@ fn buildOutputFromZig(
     assert(out.* == null);
     out.* = crt_file;
 
-    comp.enqueueLinkTaskMode(crt_file.full_object_path, output_mode);
+    comp.queueLinkTaskMode(crt_file.full_object_path, output_mode);
 }
 
 pub fn build_crt_file(
@@ -6463,7 +6459,7 @@ pub fn build_crt_file(
     try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node);
 
     const crt_file = try sub_compilation.toCrtFile();
-    comp.enqueueLinkTaskMode(crt_file.full_object_path, output_mode);
+    comp.queueLinkTaskMode(crt_file.full_object_path, output_mode);
 
     {
         comp.mutex.lock();
@@ -6473,8 +6469,8 @@ pub fn build_crt_file(
     }
 }
 
-pub fn enqueueLinkTaskMode(comp: *Compilation, path: Path, output_mode: std.builtin.OutputMode) void {
-    comp.enqueueLinkTasks(switch (output_mode) {
+pub fn queueLinkTaskMode(comp: *Compilation, path: Path, output_mode: std.builtin.OutputMode) void {
+    comp.queueLinkTasks(switch (output_mode) {
         .Exe => unreachable,
         .Obj => &.{.{ .load_object = path }},
         .Lib => &.{.{ .load_archive = path }},
@@ -6483,7 +6479,7 @@ pub fn enqueueLinkTaskMode(comp: *Compilation, path: Path, output_mode: std.buil
 
 /// Only valid to call during `update`. Automatically handles queuing up a
 /// linker worker task if there is not already one.
-fn enqueueLinkTasks(comp: *Compilation, tasks: []const link.File.Task) void {
+pub fn queueLinkTasks(comp: *Compilation, tasks: []const link.File.Task) void {
     const use_lld = build_options.have_llvm and comp.config.use_lld;
     if (use_lld) return;
     const target = comp.root_mod.resolved_target.result;
src/glibc.zig
@@ -6,12 +6,14 @@ const fs = std.fs;
 const path = fs.path;
 const assert = std.debug.assert;
 const Version = std.SemanticVersion;
+const Path = std.Build.Cache.Path;
 
 const Compilation = @import("Compilation.zig");
 const build_options = @import("build_options");
 const trace = @import("tracy.zig").trace;
 const Cache = std.Build.Cache;
 const Module = @import("Package/Module.zig");
+const link = @import("link.zig");
 
 pub const Lib = struct {
     name: []const u8,
@@ -717,11 +719,11 @@ fn lib_path(comp: *Compilation, arena: Allocator, sub_path: []const u8) ![]const
 
 pub const BuiltSharedObjects = struct {
     lock: Cache.Lock,
-    dir_path: []u8,
+    dir_path: Path,
 
     pub fn deinit(self: *BuiltSharedObjects, gpa: Allocator) void {
         self.lock.release();
-        gpa.free(self.dir_path);
+        gpa.free(self.dir_path.sub_path);
         self.* = undefined;
     }
 };
@@ -742,7 +744,9 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi
         return error.ZigCompilerNotBuiltWithLLVMExtensions;
     }
 
-    var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
+    const gpa = comp.gpa;
+
+    var arena_allocator = std.heap.ArenaAllocator.init(gpa);
     defer arena_allocator.deinit();
     const arena = arena_allocator.allocator();
 
@@ -751,7 +755,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi
 
     // Use the global cache directory.
     var cache: Cache = .{
-        .gpa = comp.gpa,
+        .gpa = gpa,
         .manifest_dir = try comp.global_cache_directory.handle.makeOpenPath("h", .{}),
     };
     cache.addPrefix(.{ .path = null, .handle = fs.cwd() });
@@ -772,12 +776,13 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi
     if (try man.hit()) {
         const digest = man.final();
 
-        assert(comp.glibc_so_files == null);
-        comp.glibc_so_files = BuiltSharedObjects{
+        return queueSharedObjects(comp, .{
             .lock = man.toOwnedLock(),
-            .dir_path = try comp.global_cache_directory.join(comp.gpa, &.{ "o", &digest }),
-        };
-        return;
+            .dir_path = .{
+                .root_dir = comp.global_cache_directory,
+                .sub_path = try gpa.dupe(u8, "o" ++ fs.path.sep_str ++ digest),
+            },
+        });
     }
 
     const digest = man.final();
@@ -790,8 +795,8 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi
     defer o_directory.handle.close();
 
     const abilists_contents = man.files.keys()[abilists_index].contents.?;
-    const metadata = try loadMetaData(comp.gpa, abilists_contents);
-    defer metadata.destroy(comp.gpa);
+    const metadata = try loadMetaData(gpa, abilists_contents);
+    defer metadata.destroy(gpa);
 
     const target_targ_index = for (metadata.all_targets, 0..) |targ, i| {
         if (targ.arch == target.cpu.arch and
@@ -835,7 +840,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi
         map_contents.deinit(); // The most recent allocation of an arena can be freed :)
     }
 
-    var stubs_asm = std.ArrayList(u8).init(comp.gpa);
+    var stubs_asm = std.ArrayList(u8).init(gpa);
     defer stubs_asm.deinit();
 
     for (libs, 0..) |lib, lib_i| {
@@ -1195,7 +1200,6 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi
         var lib_name_buf: [32]u8 = undefined; // Larger than each of the names "c", "pthread", etc.
         const asm_file_basename = std.fmt.bufPrint(&lib_name_buf, "{s}.s", .{lib.name}) catch unreachable;
         try o_directory.handle.writeFile(.{ .sub_path = asm_file_basename, .data = stubs_asm.items });
-
         try buildSharedLib(comp, arena, comp.global_cache_directory, o_directory, asm_file_basename, lib, prog_node);
     }
 
@@ -1203,11 +1207,44 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi
         log.warn("failed to write cache manifest for glibc stubs: {s}", .{@errorName(err)});
     };
 
-    assert(comp.glibc_so_files == null);
-    comp.glibc_so_files = .{
+    return queueSharedObjects(comp, .{
         .lock = man.toOwnedLock(),
-        .dir_path = try comp.global_cache_directory.join(comp.gpa, &.{ "o", &digest }),
-    };
+        .dir_path = .{
+            .root_dir = comp.global_cache_directory,
+            .sub_path = try gpa.dupe(u8, "o" ++ fs.path.sep_str ++ digest),
+        },
+    });
+}
+
+fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) void {
+    const target_version = comp.getTarget().os.version_range.linux.glibc;
+
+    assert(comp.glibc_so_files == null);
+    comp.glibc_so_files = so_files;
+
+    var task_buffer: [libs.len]link.File.Task = undefined;
+    var task_buffer_i: usize = 0;
+
+    {
+        comp.mutex.lock(); // protect comp.arena
+        defer comp.mutex.unlock();
+
+        for (libs) |lib| {
+            if (lib.removed_in) |rem_in| {
+                if (target_version.order(rem_in) != .lt) continue;
+            }
+            const so_path: Path = .{
+                .root_dir = so_files.dir_path.root_dir,
+                .sub_path = std.fmt.allocPrint(comp.arena, "{s}{c}lib{s}.so.{d}", .{
+                    so_files.dir_path.sub_path, fs.path.sep, lib.name, lib.sover,
+                }) catch return comp.setAllocFailure(),
+            };
+            task_buffer[task_buffer_i] = .{ .load_dso = so_path };
+            task_buffer_i += 1;
+        }
+    }
+
+    comp.queueLinkTasks(task_buffer[0..task_buffer_i]);
 }
 
 fn buildSharedLib(
src/libcxx.zig
@@ -357,7 +357,7 @@ pub fn buildLibCXX(comp: *Compilation, prog_node: std.Progress.Node) BuildError!
     assert(comp.libcxx_static_lib == null);
     const crt_file = try sub_compilation.toCrtFile();
     comp.libcxx_static_lib = crt_file;
-    comp.enqueueLinkTaskMode(crt_file.full_object_path, output_mode);
+    comp.queueLinkTaskMode(crt_file.full_object_path, output_mode);
 }
 
 pub fn buildLibCXXABI(comp: *Compilation, prog_node: std.Progress.Node) BuildError!void {
@@ -588,7 +588,7 @@ pub fn buildLibCXXABI(comp: *Compilation, prog_node: std.Progress.Node) BuildErr
     assert(comp.libcxxabi_static_lib == null);
     const crt_file = try sub_compilation.toCrtFile();
     comp.libcxxabi_static_lib = crt_file;
-    comp.enqueueLinkTaskMode(crt_file.full_object_path, output_mode);
+    comp.queueLinkTaskMode(crt_file.full_object_path, output_mode);
 }
 
 pub fn hardeningModeFlag(optimize_mode: std.builtin.OptimizeMode) []const u8 {
src/libtsan.zig
@@ -343,7 +343,7 @@ pub fn buildTsan(comp: *Compilation, prog_node: std.Progress.Node) BuildError!vo
     };
 
     const crt_file = try sub_compilation.toCrtFile();
-    comp.enqueueLinkTaskMode(crt_file.full_object_path, output_mode);
+    comp.queueLinkTaskMode(crt_file.full_object_path, output_mode);
     assert(comp.tsan_lib == null);
     comp.tsan_lib = crt_file;
 }
src/libunwind.zig
@@ -200,7 +200,7 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: std.Progress.Node) BuildErr
     };
 
     const crt_file = try sub_compilation.toCrtFile();
-    comp.enqueueLinkTaskMode(crt_file.full_object_path, output_mode);
+    comp.queueLinkTaskMode(crt_file.full_object_path, output_mode);
     assert(comp.libunwind_static_lib == null);
     comp.libunwind_static_lib = crt_file;
 }
src/link.zig
@@ -1349,12 +1349,14 @@ pub const File = struct {
     /// Does all the tasks in the queue. Runs in exactly one separate thread
     /// from the rest of compilation. All tasks performed here are
     /// single-threaded with respect to one another.
-    pub fn flushTaskQueue(base: *File, prog_node: std.Progress.Node) void {
+    pub fn flushTaskQueue(base: *File, parent_prog_node: std.Progress.Node) void {
         const comp = base.comp;
         base.task_queue_safety.lock();
         defer base.task_queue_safety.unlock();
+        const prog_node = parent_prog_node.start("Parse Linker Inputs", 0);
+        defer prog_node.end();
         while (comp.link_task_queue.check()) |tasks| {
-            for (tasks) |task| doTask(base, prog_node, task);
+            for (tasks) |task| doTask(base, task);
         }
     }
 
@@ -1374,16 +1376,11 @@ pub const File = struct {
         load_input: Input,
     };
 
-    fn doTask(base: *File, parent_prog_node: std.Progress.Node, task: Task) void {
+    fn doTask(base: *File, task: Task) void {
         const comp = base.comp;
         switch (task) {
             .load_explicitly_provided => {
-                const prog_node = parent_prog_node.start("Linker Parse Input", comp.link_inputs.len);
-                defer prog_node.end();
-
                 for (comp.link_inputs) |input| {
-                    const sub_node = prog_node.start(input.taskName(), 0);
-                    defer sub_node.end();
                     base.loadInput(input) catch |err| switch (err) {
                         error.LinkFailure => return, // error reported via link_diags
                         else => |e| {
@@ -1397,33 +1394,18 @@ pub const File = struct {
                 }
             },
             .load_object => |path| {
-                const prog_node = parent_prog_node.start("Linker Parse Object", 0);
-                defer prog_node.end();
-                const sub_node = prog_node.start(path.basename(), 0);
-                defer sub_node.end();
-
                 base.openLoadObject(path) catch |err| switch (err) {
                     error.LinkFailure => return, // error reported via link_diags
                     else => |e| comp.link_diags.addParseError(path, "failed to parse object: {s}", .{@errorName(e)}),
                 };
             },
             .load_archive => |path| {
-                const prog_node = parent_prog_node.start("Linker Parse Archive", 0);
-                defer prog_node.end();
-                const sub_node = prog_node.start(path.basename(), 0);
-                defer sub_node.end();
-
                 base.openLoadArchive(path) catch |err| switch (err) {
                     error.LinkFailure => return, // error reported via link_diags
                     else => |e| comp.link_diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(e)}),
                 };
             },
             .load_dso => |path| {
-                const prog_node = parent_prog_node.start("Linker Parse Shared Library", 0);
-                defer prog_node.end();
-                const sub_node = prog_node.start(path.basename(), 0);
-                defer sub_node.end();
-
                 base.openLoadDso(path, .{
                     .preferred_mode = .dynamic,
                     .search_strategy = .paths_first,
@@ -1433,10 +1415,6 @@ pub const File = struct {
                 };
             },
             .load_input => |input| {
-                const prog_node = parent_prog_node.start("Linker Parse Input", 0);
-                defer prog_node.end();
-                const sub_node = prog_node.start(input.taskName(), 0);
-                defer sub_node.end();
                 base.loadInput(input) catch |err| switch (err) {
                     error.LinkFailure => return, // error reported via link_diags
                     else => |e| {
src/musl.zig
@@ -281,7 +281,7 @@ pub fn buildCrtFile(comp: *Compilation, in_crt_file: CrtFile, prog_node: std.Pro
             errdefer comp.gpa.free(basename);
 
             const crt_file = try sub_compilation.toCrtFile();
-            comp.enqueueLinkTaskMode(crt_file.full_object_path, output_mode);
+            comp.queueLinkTaskMode(crt_file.full_object_path, output_mode);
             {
                 comp.mutex.lock();
                 defer comp.mutex.unlock();
src/ThreadSafeQueue.zig
@@ -59,5 +59,13 @@ pub fn ThreadSafeQueue(comptime T: type) type {
             self.state = .run;
             return was_waiting;
         }
+
+        /// Safe only to call exactly once when initially starting the worker.
+        pub fn start(self: *Self) bool {
+            assert(self.state == .wait);
+            if (self.shared.items.len == 0) return false;
+            self.state = .run;
+            return true;
+        }
     };
 }