Commit 2b84592858

Jakub Konka <kubkon@jakubkonka.com>
2024-07-20 06:41:30
macho: run more things in parallel
1 parent 5d9fd5b
src/arch/x86_64/Emit.zig
@@ -163,12 +163,12 @@ pub fn emitMir(emit: *Emit) Error!void {
                     const zo = macho_file.getZigObject().?;
                     const atom = zo.symbols.items[data.atom_index].getAtom(macho_file).?;
                     const sym = &zo.symbols.items[data.sym_index];
-                    if (sym.flags.needs_zig_got and !is_obj_or_static_lib) {
+                    if (sym.getSectionFlags().needs_zig_got and !is_obj_or_static_lib) {
                         _ = try sym.getOrCreateZigGotEntry(data.sym_index, macho_file);
                     }
-                    const @"type": link.File.MachO.Relocation.Type = if (sym.flags.needs_zig_got and !is_obj_or_static_lib)
+                    const @"type": link.File.MachO.Relocation.Type = if (sym.getSectionFlags().needs_zig_got and !is_obj_or_static_lib)
                         .zig_got_load
-                    else if (sym.flags.needs_got)
+                    else if (sym.getSectionFlags().needs_got)
                         // TODO: it is possible to emit .got_load here that can potentially be relaxed
                         // however this requires always to use a MOVQ mnemonic
                         .got
src/arch/x86_64/Lower.zig
@@ -451,7 +451,7 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)
                                 break :op .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) };
                             },
                             .mov => {
-                                if (is_obj_or_static_lib and macho_sym.flags.needs_zig_got) emit_mnemonic = .lea;
+                                if (is_obj_or_static_lib and macho_sym.getSectionFlags().needs_zig_got) emit_mnemonic = .lea;
                                 break :op .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) };
                             },
                             else => unreachable,
src/link/MachO/dyld_info/bind.zig
@@ -10,10 +10,7 @@ pub const Entry = struct {
             if (entry.target.eql(other.target)) {
                 return entry.offset < other.offset;
             }
-            if (entry.target.file == other.target.file) {
-                return entry.target.index < other.target.index;
-            }
-            return entry.target.file < other.target.file;
+            return entry.target.lessThan(other.target);
         }
         return entry.segment_id < other.segment_id;
     }
@@ -47,7 +44,7 @@ pub const Bind = struct {
             const file = macho_file.getFile(index).?;
             for (file.getAtoms()) |atom_index| {
                 const atom = file.getAtom(atom_index) orelse continue;
-                if (!atom.flags.alive) continue;
+                if (!atom.isAlive()) continue;
                 if (atom.getInputSection(macho_file).isZerofill()) continue;
                 const atom_addr = atom.getAddress(macho_file);
                 const relocs = atom.getRelocs(macho_file);
@@ -299,7 +296,7 @@ pub const WeakBind = struct {
             const file = macho_file.getFile(index).?;
             for (file.getAtoms()) |atom_index| {
                 const atom = file.getAtom(atom_index) orelse continue;
-                if (!atom.flags.alive) continue;
+                if (!atom.isAlive()) continue;
                 if (atom.getInputSection(macho_file).isZerofill()) continue;
                 const atom_addr = atom.getAddress(macho_file);
                 const relocs = atom.getRelocs(macho_file);
src/link/MachO/dyld_info/Rebase.zig
@@ -35,7 +35,7 @@ pub fn updateSize(rebase: *Rebase, macho_file: *MachO) !void {
         const file = macho_file.getFile(index).?;
         for (file.getAtoms()) |atom_index| {
             const atom = file.getAtom(atom_index) orelse continue;
-            if (!atom.flags.alive) continue;
+            if (!atom.isAlive()) continue;
             if (atom.getInputSection(macho_file).isZerofill()) continue;
             const atom_addr = atom.getAddress(macho_file);
             const seg_id = macho_file.sections.items(.segment_id)[atom.out_n_sect];
src/link/MachO/dyld_info/Trie.zig
@@ -102,7 +102,7 @@ pub fn updateSize(self: *Trie, macho_file: *MachO) !void {
         if (ref.getFile(macho_file) == null) continue;
         const sym = ref.getSymbol(macho_file).?;
         if (!sym.flags.@"export") continue;
-        if (sym.getAtom(macho_file)) |atom| if (!atom.flags.alive) continue;
+        if (sym.getAtom(macho_file)) |atom| if (!atom.isAlive()) continue;
         var flags: u64 = if (sym.flags.abs)
             macho.EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
         else if (sym.flags.tlv)
@@ -111,8 +111,8 @@ pub fn updateSize(self: *Trie, macho_file: *MachO) !void {
             macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR;
         if (sym.flags.weak) {
             flags |= macho.EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION;
-            macho_file.weak_defines = true;
-            macho_file.binds_to_weak = true;
+            macho_file.weak_defines.store(true, .seq_cst);
+            macho_file.binds_to_weak.store(true, .seq_cst);
         }
         try self.put(gpa, .{
             .name = sym.getName(macho_file),
src/link/MachO/Atom.zig
@@ -26,7 +26,11 @@ off: u64 = 0,
 /// Index of this atom in the linker's atoms table.
 atom_index: Index = 0,
 
-flags: Flags = .{},
+/// Specifies whether this atom is alive or has been garbage collected.
+alive: AtomicBool = AtomicBool.init(true),
+
+/// Specifies if this atom has been visited during garbage collection.
+visited: AtomicBool = AtomicBool.init(false),
 
 /// Points to the previous and next neighbors, based on the `text_offset`.
 /// This can be used to find, for example, the capacity of this `TextBlock`.
@@ -98,6 +102,14 @@ pub fn markUnwindRecordsDead(self: Atom, macho_file: *MachO) void {
     }
 }
 
+pub fn isAlive(self: Atom) bool {
+    return self.alive.load(.seq_cst);
+}
+
+pub fn setAlive(self: *Atom, alive: bool) void {
+    _ = self.alive.swap(alive, .seq_cst);
+}
+
 pub fn getThunk(self: Atom, macho_file: *MachO) *Thunk {
     const extra = self.getExtra(macho_file);
     return macho_file.getThunk(extra.thunk);
@@ -350,7 +362,7 @@ pub fn allocate(self: *Atom, macho_file: *MachO) !void {
         _ = free_list.swapRemove(i);
     }
 
-    self.flags.alive = true;
+    self.setAlive(true);
 }
 
 pub fn shrink(self: *Atom, macho_file: *MachO) void {
@@ -444,7 +456,7 @@ pub fn freeRelocs(self: *Atom, macho_file: *MachO) void {
 pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
     const tracy = trace(@src());
     defer tracy.end();
-    assert(self.flags.alive);
+    assert(self.isAlive());
 
     const relocs = self.getRelocs(macho_file);
 
@@ -455,12 +467,12 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
             .branch => {
                 const symbol = rel.getTargetSymbol(self, macho_file);
                 if (symbol.flags.import or (symbol.flags.@"export" and symbol.flags.weak) or symbol.flags.interposable) {
-                    symbol.flags.stubs = true;
+                    symbol.setSectionFlags(.{ .stubs = true });
                     if (symbol.flags.weak) {
-                        macho_file.binds_to_weak = true;
+                        macho_file.binds_to_weak.store(true, .seq_cst);
                     }
                 } else if (mem.startsWith(u8, symbol.getName(macho_file), "_objc_msgSend$")) {
-                    symbol.flags.objc_stubs = true;
+                    symbol.setSectionFlags(.{ .objc_stubs = true });
                 }
             },
 
@@ -474,19 +486,19 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
                     symbol.flags.interposable or
                     macho_file.getTarget().cpu.arch == .aarch64) // TODO relax on arm64
                 {
-                    symbol.flags.needs_got = true;
+                    symbol.setSectionFlags(.{ .needs_got = true });
                     if (symbol.flags.weak) {
-                        macho_file.binds_to_weak = true;
+                        macho_file.binds_to_weak.store(true, .seq_cst);
                     }
                 }
             },
 
             .zig_got_load => {
-                assert(rel.getTargetSymbol(self, macho_file).flags.has_zig_got);
+                assert(rel.getTargetSymbol(self, macho_file).getSectionFlags().has_zig_got);
             },
 
             .got => {
-                rel.getTargetSymbol(self, macho_file).flags.needs_got = true;
+                rel.getTargetSymbol(self, macho_file).setSectionFlags(.{ .needs_got = true });
             },
 
             .tlv,
@@ -502,9 +514,9 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
                     );
                 }
                 if (symbol.flags.import or (symbol.flags.@"export" and symbol.flags.weak) or symbol.flags.interposable) {
-                    symbol.flags.tlv_ptr = true;
+                    symbol.setSectionFlags(.{ .tlv_ptr = true });
                     if (symbol.flags.weak) {
-                        macho_file.binds_to_weak = true;
+                        macho_file.binds_to_weak.store(true, .seq_cst);
                     }
                 }
             },
@@ -514,17 +526,17 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
                     if (rel.tag == .@"extern") {
                         const symbol = rel.getTargetSymbol(self, macho_file);
                         if (symbol.isTlvInit(macho_file)) {
-                            macho_file.has_tlv = true;
+                            macho_file.has_tlv.store(true, .seq_cst);
                             continue;
                         }
                         if (symbol.flags.import) {
                             if (symbol.flags.weak) {
-                                macho_file.binds_to_weak = true;
+                                macho_file.binds_to_weak.store(true, .seq_cst);
                             }
                             continue;
                         }
                         if (symbol.flags.@"export" and symbol.flags.weak) {
-                            macho_file.binds_to_weak = true;
+                            macho_file.binds_to_weak.store(true, .seq_cst);
                         }
                     }
                 }
@@ -548,6 +560,8 @@ fn reportUndefSymbol(self: Atom, rel: Relocation, macho_file: *MachO) !bool {
     const file = self.getFile(macho_file);
     const ref = file.getSymbolRef(rel.target, macho_file);
     if (ref.getFile(macho_file) == null) {
+        macho_file.undefs_mutex.lock();
+        defer macho_file.undefs_mutex.unlock();
         const gpa = macho_file.base.comp.gpa;
         const gop = try macho_file.undefs.getOrPut(gpa, file.getGlobals()[rel.target]);
         if (!gop.found_existing) {
@@ -724,7 +738,7 @@ fn resolveRelocInner(
             assert(rel.tag == .@"extern");
             assert(rel.meta.length == 2);
             assert(rel.meta.pcrel);
-            if (rel.getTargetSymbol(self, macho_file).flags.has_got) {
+            if (rel.getTargetSymbol(self, macho_file).getSectionFlags().has_got) {
                 try writer.writeInt(i32, @intCast(G + A - P), .little);
             } else {
                 try x86_64.relaxGotLoad(self, code[rel_offset - 3 ..], rel, macho_file);
@@ -748,7 +762,7 @@ fn resolveRelocInner(
             assert(rel.meta.length == 2);
             assert(rel.meta.pcrel);
             const sym = rel.getTargetSymbol(self, macho_file);
-            if (sym.flags.tlv_ptr) {
+            if (sym.getSectionFlags().tlv_ptr) {
                 const S_: i64 = @intCast(sym.getTlvPtrAddress(macho_file));
                 try writer.writeInt(i32, @intCast(S_ + A - P), .little);
             } else {
@@ -776,7 +790,7 @@ fn resolveRelocInner(
                 const target = switch (rel.type) {
                     .page => S + A,
                     .got_load_page => G + A,
-                    .tlvp_page => if (sym.flags.tlv_ptr) blk: {
+                    .tlvp_page => if (sym.getSectionFlags().tlv_ptr) blk: {
                         const S_: i64 = @intCast(sym.getTlvPtrAddress(macho_file));
                         break :blk S_ + A;
                     } else S + A,
@@ -831,7 +845,7 @@ fn resolveRelocInner(
 
             const sym = rel.getTargetSymbol(self, macho_file);
             const target = target: {
-                const target = if (sym.flags.tlv_ptr) blk: {
+                const target = if (sym.getSectionFlags().tlv_ptr) blk: {
                     const S_: i64 = @intCast(sym.getTlvPtrAddress(macho_file));
                     break :blk S_ + A;
                 } else S + A;
@@ -869,7 +883,7 @@ fn resolveRelocInner(
                 }
             };
 
-            var inst = if (sym.flags.tlv_ptr) aarch64.Instruction{
+            var inst = if (sym.getSectionFlags().tlv_ptr) aarch64.Instruction{
                 .load_store_register = .{
                     .rt = reg_info.rd,
                     .rn = reg_info.rn,
@@ -1142,7 +1156,7 @@ fn format2(
         atom.out_n_sect,                atom.alignment,                  atom.size,
         atom.getRelocs(macho_file).len, atom.getExtra(macho_file).thunk,
     });
-    if (!atom.flags.alive) try writer.writeAll(" : [*]");
+    if (!atom.isAlive()) try writer.writeAll(" : [*]");
     if (atom.getUnwindRecords(macho_file).len > 0) {
         try writer.writeAll(" : unwind{ ");
         const extra = atom.getExtra(macho_file);
@@ -1158,14 +1172,6 @@ fn format2(
 
 pub const Index = u32;
 
-pub const Flags = packed struct {
-    /// Specifies whether this atom is alive or has been garbage collected.
-    alive: bool = true,
-
-    /// Specifies if this atom has been visited during garbage collection.
-    visited: bool = false,
-};
-
 pub const Extra = struct {
     /// Index of the range extension thunk of this atom.
     thunk: u32 = 0,
@@ -1209,6 +1215,7 @@ const trace = @import("../../tracy.zig").trace;
 
 const Allocator = mem.Allocator;
 const Atom = @This();
+const AtomicBool = std.atomic.Value(bool);
 const File = @import("file.zig").File;
 const MachO = @import("../MachO.zig");
 const Object = @import("Object.zig");
src/link/MachO/dead_strip.zig
@@ -82,9 +82,8 @@ fn markSymbol(sym: *Symbol, roots: *std.ArrayList(*Atom), macho_file: *MachO) !v
 }
 
 fn markAtom(atom: *Atom) bool {
-    const already_visited = atom.flags.visited;
-    atom.flags.visited = true;
-    return atom.flags.alive and !already_visited;
+    const already_visited = atom.visited.swap(true, .seq_cst);
+    return atom.isAlive() and !already_visited;
 }
 
 fn mark(roots: []*Atom, objects: []const File.Index, macho_file: *MachO) void {
@@ -105,7 +104,7 @@ fn mark(roots: []*Atom, objects: []const File.Index, macho_file: *MachO) void {
                     !(mem.eql(u8, isec.sectName(), "__eh_frame") or
                     mem.eql(u8, isec.sectName(), "__compact_unwind") or
                     isec.attrs() & macho.S_ATTR_DEBUG != 0) and
-                    !atom.flags.alive and refersLive(atom, macho_file))
+                    !atom.isAlive() and refersLive(atom, macho_file))
                 {
                     markLive(atom, macho_file);
                     loop = true;
@@ -116,8 +115,8 @@ fn mark(roots: []*Atom, objects: []const File.Index, macho_file: *MachO) void {
 }
 
 fn markLive(atom: *Atom, macho_file: *MachO) void {
-    assert(atom.flags.visited);
-    atom.flags.alive = true;
+    assert(atom.visited.load(.seq_cst));
+    atom.setAlive(true);
     track_live_log.debug("{}marking live atom({d},{s})", .{
         track_live_level,
         atom.atom_index,
@@ -170,7 +169,7 @@ fn refersLive(atom: *Atom, macho_file: *MachO) bool {
             },
         };
         if (target_atom) |ta| {
-            if (ta.flags.alive) return true;
+            if (ta.isAlive()) return true;
         }
     }
     return false;
@@ -181,9 +180,10 @@ fn prune(objects: []const File.Index, macho_file: *MachO) void {
         const file = macho_file.getFile(index).?;
         for (file.getAtoms()) |atom_index| {
             const atom = file.getAtom(atom_index) orelse continue;
-            if (atom.flags.alive and !atom.flags.visited) {
-                atom.flags.alive = false;
-                atom.markUnwindRecordsDead(macho_file);
+            if (!atom.visited.load(.seq_cst)) {
+                if (atom.alive.cmpxchgStrong(true, false, .seq_cst, .seq_cst) == null) {
+                    atom.markUnwindRecordsDead(macho_file);
+                }
             }
         }
     }
src/link/MachO/file.zig
@@ -37,11 +37,10 @@ pub const File = union(enum) {
     }
 
     pub fn scanRelocs(file: File, macho_file: *MachO) !void {
-        switch (file) {
+        return switch (file) {
             .dylib => unreachable,
-            .internal => |x| x.scanRelocs(macho_file),
             inline else => |x| x.scanRelocs(macho_file),
-        }
+        };
     }
 
     /// Encodes symbol rank so that the following ordering applies:
@@ -182,19 +181,19 @@ pub const File = union(enum) {
             if (ref.getFile(macho_file) == null) continue;
             if (ref.file != file.getIndex()) continue;
             const sym = ref.getSymbol(macho_file).?;
-            if (sym.flags.needs_got) {
+            if (sym.getSectionFlags().needs_got) {
                 log.debug("'{s}' needs GOT", .{sym.getName(macho_file)});
                 try macho_file.got.addSymbol(ref, macho_file);
             }
-            if (sym.flags.stubs) {
+            if (sym.getSectionFlags().stubs) {
                 log.debug("'{s}' needs STUBS", .{sym.getName(macho_file)});
                 try macho_file.stubs.addSymbol(ref, macho_file);
             }
-            if (sym.flags.tlv_ptr) {
+            if (sym.getSectionFlags().tlv_ptr) {
                 log.debug("'{s}' needs TLV pointer", .{sym.getName(macho_file)});
                 try macho_file.tlv_ptr.addSymbol(ref, macho_file);
             }
-            if (sym.flags.objc_stubs) {
+            if (sym.getSectionFlags().objc_stubs) {
                 log.debug("'{s}' needs OBJC STUBS", .{sym.getName(macho_file)});
                 try macho_file.objc_stubs.addSymbol(ref, macho_file);
             }
@@ -268,6 +267,9 @@ pub const File = union(enum) {
             const ref_file = ref.getFile(macho_file) orelse continue;
             if (ref_file.getIndex() == file.getIndex()) continue;
 
+            macho_file.dupes_mutex.lock();
+            defer macho_file.dupes_mutex.unlock();
+
             const gop = try macho_file.dupes.getOrPut(gpa, file.getGlobals()[i]);
             if (!gop.found_existing) {
                 gop.value_ptr.* = .{};
@@ -281,7 +283,7 @@ pub const File = union(enum) {
         defer tracy.end();
         for (file.getAtoms()) |atom_index| {
             const atom = file.getAtom(atom_index) orelse continue;
-            if (!atom.flags.alive) continue;
+            if (!atom.isAlive()) continue;
             atom.out_n_sect = try Atom.initOutputSection(atom.getInputSection(macho_file), macho_file);
         }
     }
@@ -295,7 +297,7 @@ pub const File = union(enum) {
 
     pub fn writeAtoms(file: File, macho_file: *MachO) !void {
         return switch (file) {
-            .dylib, .zig_object => unreachable,
+            .dylib => unreachable,
             inline else => |x| x.writeAtoms(macho_file),
         };
     }
src/link/MachO/InternalObject.zig
@@ -389,7 +389,7 @@ pub fn resolveObjcMsgSendSymbols(self: *InternalObject, macho_file: *MachO) !voi
         };
         sym.nlist_idx = nlist_idx;
         sym.extra = try self.addSymbolExtra(gpa, .{ .objc_selrefs = selrefs_index });
-        sym.flags.objc_stubs = true;
+        sym.setSectionFlags(.{ .objc_stubs = true });
 
         const idx = ref.getFile(macho_file).?.object.globals.items[ref.index];
         try self.globals.append(gpa, idx);
@@ -427,7 +427,7 @@ pub fn resolveLiterals(self: *InternalObject, lp: *MachO.LiteralPool, macho_file
             const lp_sym = lp.getSymbol(res.index, macho_file);
             const lp_atom = lp_sym.getAtom(macho_file).?;
             lp_atom.alignment = lp_atom.alignment.max(atom.alignment);
-            atom.flags.alive = false;
+            atom.setAlive(false);
         }
         atom.addExtra(.{ .literal_pool_index = res.index }, macho_file);
     }
@@ -439,7 +439,7 @@ pub fn dedupLiterals(self: *InternalObject, lp: MachO.LiteralPool, macho_file: *
 
     for (self.getAtoms()) |atom_index| {
         const atom = self.getAtom(atom_index) orelse continue;
-        if (!atom.flags.alive) continue;
+        if (!atom.isAlive()) continue;
 
         const relocs = blk: {
             const extra = atom.getExtra(macho_file);
@@ -464,7 +464,7 @@ pub fn dedupLiterals(self: *InternalObject, lp: MachO.LiteralPool, macho_file: *
     }
 
     for (self.symbols.items) |*sym| {
-        if (!sym.flags.objc_stubs) continue;
+        if (!sym.getSectionFlags().objc_stubs) continue;
         const extra = sym.getExtra(macho_file);
         const file = sym.getFile(macho_file).?;
         if (file.getIndex() != self.index) continue;
@@ -490,20 +490,20 @@ pub fn scanRelocs(self: *InternalObject, macho_file: *MachO) void {
     if (self.getEntryRef(macho_file)) |ref| {
         if (ref.getFile(macho_file) != null) {
             const sym = ref.getSymbol(macho_file).?;
-            if (sym.flags.import) sym.flags.stubs = true;
+            if (sym.flags.import) sym.setSectionFlags(.{ .stubs = true });
         }
     }
     if (self.getDyldStubBinderRef(macho_file)) |ref| {
         if (ref.getFile(macho_file) != null) {
             const sym = ref.getSymbol(macho_file).?;
-            sym.flags.needs_got = true;
+            sym.setSectionFlags(.{ .needs_got = true });
         }
     }
     if (self.getObjcMsgSendRef(macho_file)) |ref| {
         if (ref.getFile(macho_file) != null) {
             const sym = ref.getSymbol(macho_file).?;
             // TODO is it always needed, or only if we are synthesising fast stubs
-            sym.flags.needs_got = true;
+            sym.setSectionFlags(.{ .needs_got = true });
         }
     }
 }
@@ -570,7 +570,7 @@ pub fn writeAtoms(self: *InternalObject, macho_file: *MachO) !void {
 
     for (self.getAtoms()) |atom_index| {
         const atom = self.getAtom(atom_index) orelse continue;
-        if (!atom.flags.alive) continue;
+        if (!atom.isAlive()) continue;
         const sect = atom.getInputSection(macho_file);
         if (sect.isZerofill()) continue;
         const off = std.math.cast(usize, atom.value) orelse return error.Overflow;
src/link/MachO/Object.zig
@@ -263,7 +263,7 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
             mem.eql(u8, isec.sectName(), "__compact_unwind") or
             isec.attrs() & macho.S_ATTR_DEBUG != 0)
         {
-            atom.flags.alive = false;
+            atom.setAlive(false);
         }
     }
 
@@ -645,7 +645,7 @@ pub fn resolveLiterals(self: *Object, lp: *MachO.LiteralPool, macho_file: *MachO
                     const lp_sym = lp.getSymbol(res.index, macho_file);
                     const lp_atom = lp_sym.getAtom(macho_file).?;
                     lp_atom.alignment = lp_atom.alignment.max(atom.alignment);
-                    atom.flags.alive = false;
+                    atom.setAlive(false);
                 }
                 atom.addExtra(.{ .literal_pool_index = res.index }, macho_file);
             }
@@ -683,7 +683,7 @@ pub fn resolveLiterals(self: *Object, lp: *MachO.LiteralPool, macho_file: *MachO
                     const lp_sym = lp.getSymbol(res.index, macho_file);
                     const lp_atom = lp_sym.getAtom(macho_file).?;
                     lp_atom.alignment = lp_atom.alignment.max(atom.alignment);
-                    atom.flags.alive = false;
+                    atom.setAlive(false);
                 }
                 atom.addExtra(.{ .literal_pool_index = res.index }, macho_file);
             }
@@ -697,7 +697,7 @@ pub fn dedupLiterals(self: *Object, lp: MachO.LiteralPool, macho_file: *MachO) v
 
     for (self.getAtoms()) |atom_index| {
         const atom = self.getAtom(atom_index) orelse continue;
-        if (!atom.flags.alive) continue;
+        if (!atom.isAlive()) continue;
 
         const relocs = blk: {
             const extra = atom.getExtra(macho_file);
@@ -990,7 +990,7 @@ fn initRelocs(self: *Object, file: File.Handle, cpu_arch: std.Target.Cpu.Arch, m
         var next_reloc: u32 = 0;
         for (subsections.items) |subsection| {
             const atom = self.getAtom(subsection.atom).?;
-            if (!atom.flags.alive) continue;
+            if (!atom.isAlive()) continue;
             if (next_reloc >= relocs.items.len) break;
             const end_addr = atom.off + atom.size;
             const rel_index = next_reloc;
@@ -1483,7 +1483,7 @@ pub fn resolveSymbols(self: *Object, macho_file: *MachO) !void {
         if (!nlist.ext()) continue;
         if (nlist.sect()) {
             const atom = self.getAtom(atom_index).?;
-            if (!atom.flags.alive) continue;
+            if (!atom.isAlive()) continue;
         }
 
         const gop = try macho_file.resolver.getOrPut(gpa, .{
@@ -1552,7 +1552,7 @@ pub fn scanRelocs(self: *Object, macho_file: *MachO) !void {
 
     for (self.getAtoms()) |atom_index| {
         const atom = self.getAtom(atom_index) orelse continue;
-        if (!atom.flags.alive) continue;
+        if (!atom.isAlive()) continue;
         const sect = atom.getInputSection(macho_file);
         if (sect.isZerofill()) continue;
         try atom.scanRelocs(macho_file);
@@ -1563,10 +1563,10 @@ pub fn scanRelocs(self: *Object, macho_file: *MachO) !void {
         if (!rec.alive) continue;
         if (rec.getFde(macho_file)) |fde| {
             if (fde.getCie(macho_file).getPersonality(macho_file)) |sym| {
-                sym.flags.needs_got = true;
+                sym.setSectionFlags(.{ .needs_got = true });
             }
         } else if (rec.getPersonality(macho_file)) |sym| {
-            sym.flags.needs_got = true;
+            sym.setSectionFlags(.{ .needs_got = true });
         }
     }
 }
@@ -1745,7 +1745,7 @@ pub fn calcSymtabSize(self: *Object, macho_file: *MachO) void {
         const ref = self.getSymbolRef(@intCast(i), macho_file);
         const file = ref.getFile(macho_file) orelse continue;
         if (file.getIndex() != self.index) continue;
-        if (sym.getAtom(macho_file)) |atom| if (!atom.flags.alive) continue;
+        if (sym.getAtom(macho_file)) |atom| if (!atom.isAlive()) continue;
         if (sym.isSymbolStab(macho_file)) continue;
         const name = sym.getName(macho_file);
         if (name.len == 0) continue;
@@ -1854,7 +1854,7 @@ pub fn writeAtoms(self: *Object, macho_file: *MachO) !void {
     }
     for (self.getAtoms()) |atom_index| {
         const atom = self.getAtom(atom_index) orelse continue;
-        if (!atom.flags.alive) continue;
+        if (!atom.isAlive()) continue;
         const sect = atom.getInputSection(macho_file);
         if (sect.isZerofill()) continue;
         const value = math.cast(usize, atom.value) orelse return error.Overflow;
@@ -1893,7 +1893,7 @@ pub fn writeAtomsRelocatable(self: *Object, macho_file: *MachO) !void {
     }
     for (self.getAtoms()) |atom_index| {
         const atom = self.getAtom(atom_index) orelse continue;
-        if (!atom.flags.alive) continue;
+        if (!atom.isAlive()) continue;
         const sect = atom.getInputSection(macho_file);
         if (sect.isZerofill()) continue;
         const value = math.cast(usize, atom.value) orelse return error.Overflow;
src/link/MachO/relocatable.zig
@@ -261,7 +261,7 @@ fn initOutputSections(macho_file: *MachO) !void {
         const file = macho_file.getFile(index).?;
         for (file.getAtoms()) |atom_index| {
             const atom = file.getAtom(atom_index) orelse continue;
-            if (!atom.flags.alive) continue;
+            if (!atom.isAlive()) continue;
             atom.out_n_sect = try Atom.initOutputSection(atom.getInputSection(macho_file), macho_file);
         }
     }
src/link/MachO/Symbol.zig
@@ -23,6 +23,8 @@ nlist_idx: u32 = 0,
 /// Misc flags for the symbol packaged as packed struct for compression.
 flags: Flags = .{},
 
+sect_flags: std.atomic.Value(u8) = std.atomic.Value(u8).init(0),
+
 visibility: Visibility = .local,
 
 extra: u32 = 0,
@@ -69,6 +71,14 @@ pub fn getOutputSectionIndex(symbol: Symbol, macho_file: *MachO) u8 {
     return symbol.out_n_sect;
 }
 
+pub fn getSectionFlags(symbol: Symbol) SectionFlags {
+    return @bitCast(symbol.sect_flags.load(.seq_cst));
+}
+
+pub fn setSectionFlags(symbol: *Symbol, flags: SectionFlags) void {
+    _ = symbol.sect_flags.fetchOr(@bitCast(flags), .seq_cst);
+}
+
 pub fn getFile(symbol: Symbol, macho_file: *MachO) ?File {
     return macho_file.getFile(symbol.file);
 }
@@ -116,9 +126,9 @@ pub fn getAddress(symbol: Symbol, opts: struct {
     stubs: bool = true,
 }, macho_file: *MachO) u64 {
     if (opts.stubs) {
-        if (symbol.flags.stubs) {
+        if (symbol.getSectionFlags().stubs) {
             return symbol.getStubsAddress(macho_file);
-        } else if (symbol.flags.objc_stubs) {
+        } else if (symbol.getSectionFlags().objc_stubs) {
             return symbol.getObjcStubsAddress(macho_file);
         }
     }
@@ -127,25 +137,25 @@ pub fn getAddress(symbol: Symbol, opts: struct {
 }
 
 pub fn getGotAddress(symbol: Symbol, macho_file: *MachO) u64 {
-    if (!symbol.flags.has_got) return 0;
+    if (!symbol.getSectionFlags().has_got) return 0;
     const extra = symbol.getExtra(macho_file);
     return macho_file.got.getAddress(extra.got, macho_file);
 }
 
 pub fn getStubsAddress(symbol: Symbol, macho_file: *MachO) u64 {
-    if (!symbol.flags.stubs) return 0;
+    if (!symbol.getSectionFlags().stubs) return 0;
     const extra = symbol.getExtra(macho_file);
     return macho_file.stubs.getAddress(extra.stubs, macho_file);
 }
 
 pub fn getObjcStubsAddress(symbol: Symbol, macho_file: *MachO) u64 {
-    if (!symbol.flags.objc_stubs) return 0;
+    if (!symbol.getSectionFlags().objc_stubs) return 0;
     const extra = symbol.getExtra(macho_file);
     return macho_file.objc_stubs.getAddress(extra.objc_stubs, macho_file);
 }
 
 pub fn getObjcSelrefsAddress(symbol: Symbol, macho_file: *MachO) u64 {
-    if (!symbol.flags.objc_stubs) return 0;
+    if (!symbol.getSectionFlags().objc_stubs) return 0;
     const extra = symbol.getExtra(macho_file);
     const file = symbol.getFile(macho_file).?;
     return switch (file) {
@@ -155,7 +165,7 @@ pub fn getObjcSelrefsAddress(symbol: Symbol, macho_file: *MachO) u64 {
 }
 
 pub fn getTlvPtrAddress(symbol: Symbol, macho_file: *MachO) u64 {
-    if (!symbol.flags.tlv_ptr) return 0;
+    if (!symbol.getSectionFlags().tlv_ptr) return 0;
     const extra = symbol.getExtra(macho_file);
     return macho_file.tlv_ptr.getAddress(extra.tlv_ptr, macho_file);
 }
@@ -167,14 +177,14 @@ const GetOrCreateZigGotEntryResult = struct {
 
 pub fn getOrCreateZigGotEntry(symbol: *Symbol, symbol_index: Index, macho_file: *MachO) !GetOrCreateZigGotEntryResult {
     assert(!macho_file.base.isRelocatable());
-    assert(symbol.flags.needs_zig_got);
-    if (symbol.flags.has_zig_got) return .{ .found_existing = true, .index = symbol.getExtra(macho_file).zig_got };
+    assert(symbol.getSectionFlags().needs_zig_got);
+    if (symbol.getSectionFlags().has_zig_got) return .{ .found_existing = true, .index = symbol.getExtra(macho_file).zig_got };
     const index = try macho_file.zig_got.addSymbol(symbol_index, macho_file);
     return .{ .found_existing = false, .index = index };
 }
 
 pub fn getZigGotAddress(symbol: Symbol, macho_file: *MachO) u64 {
-    if (!symbol.flags.has_zig_got) return 0;
+    if (!symbol.getSectionFlags().has_zig_got) return 0;
     const extras = symbol.getExtra(macho_file);
     return macho_file.zig_got.entryAddress(extras.zig_got, macho_file);
 }
@@ -384,7 +394,9 @@ pub const Flags = packed struct {
 
     /// Whether the symbol makes into the output symtab or not.
     output_symtab: bool = false,
+};
 
+pub const SectionFlags = packed struct(u8) {
     /// Whether the symbol contains __got indirection.
     needs_got: bool = false,
     has_got: bool = false,
@@ -401,6 +413,8 @@ pub const Flags = packed struct {
 
     /// Whether the symbol contains __objc_stubs indirection.
     objc_stubs: bool = false,
+
+    _: u1 = 0,
 };
 
 pub const Visibility = enum {
src/link/MachO/synthetic.zig
@@ -24,8 +24,8 @@ pub const ZigGotSection = struct {
         const entry = &zig_got.entries.items[index];
         entry.* = sym_index;
         const symbol = &zo.symbols.items[sym_index];
-        assert(symbol.flags.needs_zig_got);
-        symbol.flags.has_zig_got = true;
+        assert(symbol.getSectionFlags().needs_zig_got);
+        symbol.setSectionFlags(.{ .has_zig_got = true });
         symbol.addExtra(.{ .zig_got = index }, macho_file);
         return index;
     }
@@ -121,7 +121,7 @@ pub const GotSection = struct {
         const entry = try got.symbols.addOne(gpa);
         entry.* = ref;
         const symbol = ref.getSymbol(macho_file).?;
-        symbol.flags.has_got = true;
+        symbol.setSectionFlags(.{ .has_got = true });
         symbol.addExtra(.{ .got = index }, macho_file);
     }
 
@@ -689,7 +689,7 @@ pub const DataInCode = struct {
                     dices[next_dice].offset < end_off) : (next_dice += 1)
                 {}
 
-                if (atom.flags.alive) for (dices[start_dice..next_dice]) |d| {
+                if (atom.isAlive()) for (dices[start_dice..next_dice]) |d| {
                     dice.entries.appendAssumeCapacity(.{
                         .atom_ref = .{ .index = atom_index, .file = index },
                         .offset = @intCast(d.offset - start_off),
src/link/MachO/thunks.zig
@@ -17,7 +17,7 @@ pub fn createThunks(sect_id: u8, macho_file: *MachO) !void {
     while (i < atoms.len) {
         const start = i;
         const start_atom = atoms[start].getAtom(macho_file).?;
-        assert(start_atom.flags.alive);
+        assert(start_atom.isAlive());
         start_atom.value = advance(header, start_atom.size, start_atom.alignment);
         i += 1;
 
@@ -25,7 +25,7 @@ pub fn createThunks(sect_id: u8, macho_file: *MachO) !void {
             header.size - start_atom.value < max_allowed_distance) : (i += 1)
         {
             const atom = atoms[i].getAtom(macho_file).?;
-            assert(atom.flags.alive);
+            assert(atom.isAlive());
             atom.value = advance(header, atom.size, atom.alignment);
         }
 
@@ -71,7 +71,7 @@ fn scanRelocs(thunk_index: Thunk.Index, gpa: Allocator, atoms: []const MachO.Ref
 
 fn isReachable(atom: *const Atom, rel: Relocation, macho_file: *MachO) bool {
     const target = rel.getTargetSymbol(atom.*, macho_file);
-    if (target.flags.stubs or target.flags.objc_stubs) return false;
+    if (target.getSectionFlags().stubs or target.getSectionFlags().objc_stubs) return false;
     if (atom.out_n_sect != target.getOutputSectionIndex(macho_file)) return false;
     const target_atom = target.getAtom(macho_file).?;
     if (target_atom.value == @as(u64, @bitCast(@as(i64, -1)))) return false;
src/link/MachO/UnwindInfo.zig
@@ -53,7 +53,7 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {
     for (macho_file.sections.items(.atoms)) |atoms| {
         for (atoms.items) |ref| {
             const atom = ref.getAtom(macho_file) orelse continue;
-            if (!atom.flags.alive) continue;
+            if (!atom.isAlive()) continue;
             const recs = atom.getUnwindRecords(macho_file);
             const file = atom.getFile(macho_file);
             try info.records.ensureUnusedCapacity(gpa, recs.len);
src/link/MachO/ZigObject.zig
@@ -245,7 +245,7 @@ pub fn resolveSymbols(self: *ZigObject, macho_file: *MachO) !void {
         if (!nlist.ext()) continue;
         if (nlist.sect()) {
             const atom = self.getAtom(atom_index).?;
-            if (!atom.flags.alive) continue;
+            if (!atom.isAlive()) continue;
         }
 
         const gop = try macho_file.resolver.getOrPut(gpa, .{
@@ -391,7 +391,7 @@ pub fn claimUnresolved(self: *ZigObject, macho_file: *MachO) void {
 pub fn scanRelocs(self: *ZigObject, macho_file: *MachO) !void {
     for (self.getAtoms()) |atom_index| {
         const atom = self.getAtom(atom_index) orelse continue;
-        if (!atom.flags.alive) continue;
+        if (!atom.isAlive()) continue;
         const sect = atom.getInputSection(macho_file);
         if (sect.isZerofill()) continue;
         try atom.scanRelocs(macho_file);
@@ -403,7 +403,7 @@ pub fn resolveRelocs(self: *ZigObject, macho_file: *MachO) !void {
     var has_error = false;
     for (self.getAtoms()) |atom_index| {
         const atom = self.getAtom(atom_index) orelse continue;
-        if (!atom.flags.alive) continue;
+        if (!atom.isAlive()) continue;
         const sect = &macho_file.sections.items(.header)[atom.out_n_sect];
         if (sect.isZerofill()) continue;
         if (!macho_file.isZigSection(atom.out_n_sect)) continue; // Non-Zig sections are handled separately
@@ -450,7 +450,7 @@ pub fn resolveRelocs(self: *ZigObject, macho_file: *MachO) !void {
 pub fn calcNumRelocs(self: *ZigObject, macho_file: *MachO) void {
     for (self.getAtoms()) |atom_index| {
         const atom = self.getAtom(atom_index) orelse continue;
-        if (!atom.flags.alive) continue;
+        if (!atom.isAlive()) continue;
         const header = &macho_file.sections.items(.header)[atom.out_n_sect];
         if (header.isZerofill()) continue;
         if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue;
@@ -465,7 +465,7 @@ pub fn writeRelocs(self: *ZigObject, macho_file: *MachO) !void {
 
     for (self.getAtoms()) |atom_index| {
         const atom = self.getAtom(atom_index) orelse continue;
-        if (!atom.flags.alive) continue;
+        if (!atom.isAlive()) continue;
         const header = macho_file.sections.items(.header)[atom.out_n_sect];
         const relocs = macho_file.sections.items(.relocs)[atom.out_n_sect].items;
         if (header.isZerofill()) continue;
@@ -505,7 +505,7 @@ pub fn writeAtomsRelocatable(self: *ZigObject, macho_file: *MachO) !void {
 
     for (self.getAtoms()) |atom_index| {
         const atom = self.getAtom(atom_index) orelse continue;
-        if (!atom.flags.alive) continue;
+        if (!atom.isAlive()) continue;
         const sect = atom.getInputSection(macho_file);
         if (sect.isZerofill()) continue;
         if (macho_file.isZigSection(atom.out_n_sect)) continue;
@@ -529,7 +529,7 @@ pub fn writeAtoms(self: *ZigObject, macho_file: *MachO) !void {
 
     for (self.getAtoms()) |atom_index| {
         const atom = self.getAtom(atom_index) orelse continue;
-        if (!atom.flags.alive) continue;
+        if (!atom.isAlive()) continue;
         const sect = atom.getInputSection(macho_file);
         if (sect.isZerofill()) continue;
         if (macho_file.isZigSection(atom.out_n_sect)) continue;
@@ -549,7 +549,7 @@ pub fn calcSymtabSize(self: *ZigObject, macho_file: *MachO) void {
         const ref = self.getSymbolRef(@intCast(i), macho_file);
         const file = ref.getFile(macho_file) orelse continue;
         if (file.getIndex() != self.index) continue;
-        if (sym.getAtom(macho_file)) |atom| if (!atom.flags.alive) continue;
+        if (sym.getAtom(macho_file)) |atom| if (!atom.isAlive()) continue;
         sym.flags.output_symtab = true;
         if (sym.isLocal()) {
             sym.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, macho_file);
@@ -914,7 +914,7 @@ pub fn updateDecl(
         const lib_name = variable.lib_name.toSlice(&mod.intern_pool);
         const index = try self.getGlobalSymbol(macho_file, name, lib_name);
         const sym = &self.symbols.items[index];
-        sym.flags.needs_got = true;
+        sym.setSectionFlags(.{ .needs_got = true });
         return;
     }
 
@@ -993,7 +993,7 @@ fn updateDeclCode(
     const sym_name = try std.fmt.allocPrintZ(gpa, "_{s}", .{decl.fqn.toSlice(ip)});
     defer gpa.free(sym_name);
     sym.name = try self.strtab.insert(gpa, sym_name);
-    atom.flags.alive = true;
+    atom.setAlive(true);
     atom.name = sym.name;
     nlist.n_strx = sym.name;
     nlist.n_type = macho.N_SECT;
@@ -1018,7 +1018,7 @@ fn updateDeclCode(
 
                 if (!macho_file.base.isRelocatable()) {
                     log.debug("  (updating offset table entry)", .{});
-                    assert(sym.flags.has_zig_got);
+                    assert(sym.getSectionFlags().has_zig_got);
                     const extra = sym.getExtra(macho_file);
                     try macho_file.zig_got.writeOne(macho_file, extra.zig_got);
                 }
@@ -1034,7 +1034,7 @@ fn updateDeclCode(
         errdefer self.freeDeclMetadata(macho_file, sym_index);
 
         sym.value = 0;
-        sym.flags.needs_zig_got = true;
+        sym.setSectionFlags(.{ .needs_zig_got = true });
         nlist.n_value = 0;
 
         if (!macho_file.base.isRelocatable()) {
@@ -1098,7 +1098,7 @@ fn createTlvInitializer(
     const atom = sym.getAtom(macho_file).?;
     sym.out_n_sect = sect_index;
     atom.out_n_sect = sect_index;
-    atom.flags.alive = true;
+    atom.setAlive(true);
     atom.alignment = alignment;
     atom.size = code.len;
     nlist.n_sect = sect_index + 1;
@@ -1143,7 +1143,7 @@ fn createTlvDescriptor(
 
     sym.value = 0;
     sym.name = try self.strtab.insert(gpa, name);
-    atom.flags.alive = true;
+    atom.setAlive(true);
     atom.name = sym.name;
     nlist.n_strx = sym.name;
     nlist.n_sect = sect_index + 1;
@@ -1317,7 +1317,7 @@ fn lowerConst(
     self.symtab.items(.size)[sym.nlist_idx] = code.len;
 
     const atom = sym.getAtom(macho_file).?;
-    atom.flags.alive = true;
+    atom.setAlive(true);
     atom.alignment = required_alignment;
     atom.size = code.len;
     atom.out_n_sect = output_section_index;
@@ -1490,7 +1490,7 @@ fn updateLazySymbol(
     self.symtab.items(.size)[sym.nlist_idx] = code.len;
 
     const atom = sym.getAtom(macho_file).?;
-    atom.flags.alive = true;
+    atom.setAlive(true);
     atom.name = name_str_index;
     atom.alignment = required_alignment;
     atom.size = code.len;
@@ -1500,7 +1500,7 @@ fn updateLazySymbol(
     errdefer self.freeDeclMetadata(macho_file, symbol_index);
 
     sym.value = 0;
-    sym.flags.needs_zig_got = true;
+    sym.setSectionFlags(.{ .needs_zig_got = true });
     nlist.n_value = 0;
 
     if (!macho_file.base.isRelocatable()) {
@@ -1576,7 +1576,7 @@ pub fn getOrCreateMetadataForDecl(
         if (isThreadlocal(macho_file, decl_index)) {
             sym.flags.tlv = true;
         } else {
-            sym.flags.needs_zig_got = true;
+            sym.setSectionFlags(.{ .needs_zig_got = true });
         }
         gop.value_ptr.* = .{ .symbol_index = sym_index };
     }
@@ -1611,7 +1611,7 @@ pub fn getOrCreateMetadataForLazySymbol(
         .unused => {
             const symbol_index = try self.newSymbolWithAtom(gpa, 0, macho_file);
             const sym = &self.symbols.items[symbol_index];
-            sym.flags.needs_zig_got = true;
+            sym.setSectionFlags(.{ .needs_zig_got = true });
             metadata.symbol_index.* = symbol_index;
         },
         .pending_flush => return metadata.symbol_index.*,
src/link/MachO.zig
@@ -25,8 +25,10 @@ sections: std.MultiArrayList(Section) = .{},
 resolver: SymbolResolver = .{},
 /// This table will be populated after `scanRelocs` has run.
 /// Key is symbol index.
-undefs: std.AutoHashMapUnmanaged(SymbolResolver.Index, std.ArrayListUnmanaged(Ref)) = .{},
-dupes: std.AutoHashMapUnmanaged(SymbolResolver.Index, std.ArrayListUnmanaged(File.Index)) = .{},
+undefs: std.AutoArrayHashMapUnmanaged(SymbolResolver.Index, std.ArrayListUnmanaged(Ref)) = .{},
+undefs_mutex: std.Thread.Mutex = .{},
+dupes: std.AutoArrayHashMapUnmanaged(SymbolResolver.Index, std.ArrayListUnmanaged(File.Index)) = .{},
+dupes_mutex: std.Thread.Mutex = .{},
 
 dyld_info_cmd: macho.dyld_info_command = .{},
 symtab_cmd: macho.symtab_command = .{},
@@ -93,9 +95,9 @@ debug_str_sect_index: ?u8 = null,
 debug_aranges_sect_index: ?u8 = null,
 debug_line_sect_index: ?u8 = null,
 
-has_tlv: bool = false,
-binds_to_weak: bool = false,
-weak_defines: bool = false,
+has_tlv: AtomicBool = AtomicBool.init(false),
+binds_to_weak: AtomicBool = AtomicBool.init(false),
+weak_defines: AtomicBool = AtomicBool.init(false),
 has_errors: AtomicBool = AtomicBool.init(false),
 
 /// Options
@@ -306,20 +308,15 @@ pub fn deinit(self: *MachO) void {
     self.sections.deinit(gpa);
 
     self.resolver.deinit(gpa);
-    {
-        var it = self.undefs.iterator();
-        while (it.next()) |entry| {
-            entry.value_ptr.deinit(gpa);
-        }
-        self.undefs.deinit(gpa);
+
+    for (self.undefs.values()) |*val| {
+        val.deinit(gpa);
     }
-    {
-        var it = self.dupes.iterator();
-        while (it.next()) |entry| {
-            entry.value_ptr.deinit(gpa);
-        }
-        self.dupes.deinit(gpa);
+    self.undefs.deinit(gpa);
+    for (self.dupes.values()) |*val| {
+        val.deinit(gpa);
     }
+    self.dupes.deinit(gpa);
 
     self.symtab.deinit(gpa);
     self.strtab.deinit(gpa);
@@ -553,12 +550,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
             else => |e| return e,
         };
     }
-    self.writeSectionsAndUpdateLinkeditSizes() catch |err| {
-        switch (err) {
-            error.ResolveFailed => return error.FlushFailure,
-            else => |e| return e,
-        }
-    };
+    try self.writeSectionsAndUpdateLinkeditSizes();
 
     try self.writeSectionsToFile();
     try self.allocateLinkeditSegment();
@@ -907,25 +899,25 @@ pub fn parseInputFiles(self: *MachO) !void {
         defer wg.wait();
 
         for (self.objects.items) |index| {
-            tp.spawnWg(&wg, parseInputFileWorker, .{ self, index });
+            tp.spawnWg(&wg, parseInputFileWorker, .{ self, self.getFile(index).? });
         }
         for (self.dylibs.items) |index| {
-            tp.spawnWg(&wg, parseInputFileWorker, .{ self, index });
+            tp.spawnWg(&wg, parseInputFileWorker, .{ self, self.getFile(index).? });
         }
     }
 
     if (self.has_errors.swap(false, .seq_cst)) return error.FlushFailure;
 }
 
-fn parseInputFileWorker(self: *MachO, index: File.Index) void {
-    self.getFile(index).?.parse(self) catch |err| {
+fn parseInputFileWorker(self: *MachO, file: File) void {
+    file.parse(self) catch |err| {
         switch (err) {
             error.MalformedObject,
             error.MalformedDylib,
             error.InvalidCpuArch,
             error.InvalidTarget,
             => {}, // already reported
-            else => |e| self.reportParseError2(index, "unexpected error: parsing input file failed with error {s}", .{@errorName(e)}) catch {},
+            else => |e| self.reportParseError2(file.getIndex(), "unexpected error: parsing input file failed with error {s}", .{@errorName(e)}) catch {},
         }
         _ = self.has_errors.swap(true, .seq_cst);
     };
@@ -1286,13 +1278,50 @@ fn markLive(self: *MachO) void {
 }
 
 fn convertTentativeDefsAndResolveSpecialSymbols(self: *MachO) !void {
-    for (self.objects.items) |index| {
-        try self.getFile(index).?.object.convertTentativeDefinitions(self);
-    }
-    if (self.getInternalObject()) |obj| {
-        try obj.resolveBoundarySymbols(self);
-        try obj.resolveObjcMsgSendSymbols(self);
+    const tp = self.base.comp.thread_pool;
+    var wg: WaitGroup = .{};
+    {
+        wg.reset();
+        defer wg.wait();
+        for (self.objects.items) |index| {
+            tp.spawnWg(&wg, convertTentativeDefinitionsWorker, .{ self, self.getFile(index).?.object });
+        }
+        if (self.getInternalObject()) |obj| {
+            tp.spawnWg(&wg, resolveSpecialSymbolsWorker, .{ self, obj });
+        }
     }
+    if (self.has_errors.swap(false, .seq_cst)) return error.FlushFailure;
+}
+
+fn convertTentativeDefinitionsWorker(self: *MachO, object: *Object) void {
+    const tracy = trace(@src());
+    defer tracy.end();
+    object.convertTentativeDefinitions(self) catch |err| {
+        self.reportParseError2(
+            object.index,
+            "unexpected error occurred while converting tentative symbols into defined symbols: {s}",
+            .{@errorName(err)},
+        ) catch {};
+        _ = self.has_errors.swap(true, .seq_cst);
+    };
+}
+
+fn resolveSpecialSymbolsWorker(self: *MachO, obj: *InternalObject) void {
+    const tracy = trace(@src());
+    defer tracy.end();
+    obj.resolveBoundarySymbols(self) catch |err| {
+        self.reportUnexpectedError("unexpected error occurred while resolving boundary symbols: {s}", .{
+            @errorName(err),
+        }) catch {};
+        _ = self.has_errors.swap(true, .seq_cst);
+        return;
+    };
+    obj.resolveObjcMsgSendSymbols(self) catch |err| {
+        self.reportUnexpectedError("unexpected error occurred while resolving ObjC msgsend stubs: {s}", .{
+            @errorName(err),
+        }) catch {};
+        _ = self.has_errors.swap(true, .seq_cst);
+    };
 }
 
 pub fn dedupLiterals(self: *MachO) !void {
@@ -1313,14 +1342,20 @@ pub fn dedupLiterals(self: *MachO) !void {
         try object.resolveLiterals(&lp, self);
     }
 
-    if (self.getZigObject()) |zo| {
-        zo.dedupLiterals(lp, self);
-    }
-    for (self.objects.items) |index| {
-        self.getFile(index).?.object.dedupLiterals(lp, self);
-    }
-    if (self.getInternalObject()) |object| {
-        object.dedupLiterals(lp, self);
+    const tp = self.base.comp.thread_pool;
+    var wg: WaitGroup = .{};
+    {
+        wg.reset();
+        defer wg.wait();
+        if (self.getZigObject()) |zo| {
+            tp.spawnWg(&wg, File.dedupLiterals, .{ zo.asFile(), lp, self });
+        }
+        for (self.objects.items) |index| {
+            tp.spawnWg(&wg, File.dedupLiterals, .{ self.getFile(index).?, lp, self });
+        }
+        if (self.getInternalObject()) |object| {
+            tp.spawnWg(&wg, File.dedupLiterals, .{ object.asFile(), lp, self });
+        }
     }
 }
 
@@ -1334,18 +1369,41 @@ fn claimUnresolved(self: *MachO) void {
 }
 
 fn checkDuplicates(self: *MachO) !void {
-    if (self.getZigObject()) |zo| {
-        try zo.asFile().checkDuplicates(self);
-    }
-    for (self.objects.items) |index| {
-        try self.getFile(index).?.checkDuplicates(self);
-    }
-    if (self.getInternalObject()) |obj| {
-        try obj.asFile().checkDuplicates(self);
+    const tracy = trace(@src());
+    defer tracy.end();
+
+    const tp = self.base.comp.thread_pool;
+    var wg: WaitGroup = .{};
+    {
+        wg.reset();
+        defer wg.wait();
+        if (self.getZigObject()) |zo| {
+            tp.spawnWg(&wg, checkDuplicatesWorker, .{ self, zo.asFile() });
+        }
+        for (self.objects.items) |index| {
+            tp.spawnWg(&wg, checkDuplicatesWorker, .{ self, self.getFile(index).? });
+        }
+        if (self.getInternalObject()) |obj| {
+            tp.spawnWg(&wg, checkDuplicatesWorker, .{ self, obj.asFile() });
+        }
     }
+
+    if (self.has_errors.swap(false, .seq_cst)) return error.FlushFailure;
+
     try self.reportDuplicates();
 }
 
+fn checkDuplicatesWorker(self: *MachO, file: File) void {
+    const tracy = trace(@src());
+    defer tracy.end();
+    file.checkDuplicates(self) catch |err| {
+        self.reportParseError2(file.getIndex(), "failed to check for duplicate definitions: {s}", .{
+            @errorName(err),
+        }) catch {};
+        _ = self.has_errors.swap(true, .seq_cst);
+    };
+}
+
 fn markImportsAndExports(self: *MachO) void {
     const tracy = trace(@src());
     defer tracy.end();
@@ -1384,16 +1442,26 @@ fn scanRelocs(self: *MachO) !void {
     const tracy = trace(@src());
     defer tracy.end();
 
-    if (self.getZigObject()) |zo| {
-        try zo.scanRelocs(self);
-    }
-    for (self.objects.items) |index| {
-        try self.getFile(index).?.object.scanRelocs(self);
-    }
-    if (self.getInternalObject()) |obj| {
-        obj.scanRelocs(self);
+    const tp = self.base.comp.thread_pool;
+    var wg: WaitGroup = .{};
+
+    {
+        wg.reset();
+        defer wg.wait();
+
+        if (self.getZigObject()) |zo| {
+            tp.spawnWg(&wg, scanRelocsWorker, .{ self, zo.asFile() });
+        }
+        for (self.objects.items) |index| {
+            tp.spawnWg(&wg, scanRelocsWorker, .{ self, self.getFile(index).? });
+        }
+        if (self.getInternalObject()) |obj| {
+            tp.spawnWg(&wg, scanRelocsWorker, .{ self, obj.asFile() });
+        }
     }
 
+    if (self.has_errors.swap(false, .seq_cst)) return error.FlushFailure;
+
     try self.reportUndefs();
 
     if (self.getZigObject()) |zo| {
@@ -1410,25 +1478,61 @@ fn scanRelocs(self: *MachO) !void {
     }
 }
 
+fn scanRelocsWorker(self: *MachO, file: File) void {
+    file.scanRelocs(self) catch |err| {
+        self.reportParseError2(file.getIndex(), "failed to scan relocations: {s}", .{
+            @errorName(err),
+        }) catch {};
+        _ = self.has_errors.swap(true, .seq_cst);
+    };
+}
+
+fn sortGlobalSymbolsByName(self: *MachO, symbols: []SymbolResolver.Index) void {
+    const lessThan = struct {
+        fn lessThan(ctx: *MachO, lhs: SymbolResolver.Index, rhs: SymbolResolver.Index) bool {
+            const lhs_name = ctx.resolver.keys.items[lhs - 1].getName(ctx);
+            const rhs_name = ctx.resolver.keys.items[rhs - 1].getName(ctx);
+            return mem.order(u8, lhs_name, rhs_name) == .lt;
+        }
+    }.lessThan;
+    mem.sort(SymbolResolver.Index, symbols, self, lessThan);
+}
+
 fn reportUndefs(self: *MachO) !void {
     const tracy = trace(@src());
     defer tracy.end();
 
     if (self.undefined_treatment == .suppress or
         self.undefined_treatment == .dynamic_lookup) return;
+    if (self.undefs.keys().len == 0) return; // Nothing to do
 
+    const gpa = self.base.comp.gpa;
     const max_notes = 4;
 
-    var has_undefs = false;
-    var it = self.undefs.iterator();
-    while (it.next()) |entry| {
-        const undef_sym = self.resolver.keys.items[entry.key_ptr.* - 1];
-        const notes = entry.value_ptr.*;
+    // We will sort by name, and then by file to ensure deterministic output.
+    var keys = try std.ArrayList(SymbolResolver.Index).initCapacity(gpa, self.undefs.keys().len);
+    defer keys.deinit();
+    keys.appendSliceAssumeCapacity(self.undefs.keys());
+    self.sortGlobalSymbolsByName(keys.items);
+
+    const refLessThan = struct {
+        fn lessThan(ctx: void, lhs: Ref, rhs: Ref) bool {
+            _ = ctx;
+            return lhs.lessThan(rhs);
+        }
+    }.lessThan;
+
+    for (self.undefs.values()) |*refs| {
+        mem.sort(Ref, refs.items, {}, refLessThan);
+    }
+
+    for (keys.items) |key| {
+        const undef_sym = self.resolver.keys.items[key - 1];
+        const notes = self.undefs.get(key).?;
         const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes);
 
         var err = try self.base.addErrorWithNotes(nnotes);
         try err.addMsg("undefined symbol: {s}", .{undef_sym.getName(self)});
-        has_undefs = true;
 
         var inote: usize = 0;
         while (inote < @min(notes.items.len, max_notes)) : (inote += 1) {
@@ -1443,7 +1547,8 @@ fn reportUndefs(self: *MachO) !void {
             try err.addNote("referenced {d} more times", .{remaining});
         }
     }
-    if (has_undefs) return error.HasUndefinedSymbols;
+
+    return error.HasUndefinedSymbols;
 }
 
 fn initOutputSections(self: *MachO) !void {
@@ -1679,7 +1784,7 @@ pub fn sortSections(self: *MachO) !void {
     if (self.getZigObject()) |zo| {
         for (zo.getAtoms()) |atom_index| {
             const atom = zo.getAtom(atom_index) orelse continue;
-            if (!atom.flags.alive) continue;
+            if (!atom.isAlive()) continue;
             atom.out_n_sect = backlinks[atom.out_n_sect];
         }
     }
@@ -1688,7 +1793,7 @@ pub fn sortSections(self: *MachO) !void {
         const file = self.getFile(index).?;
         for (file.getAtoms()) |atom_index| {
             const atom = file.getAtom(atom_index) orelse continue;
-            if (!atom.flags.alive) continue;
+            if (!atom.isAlive()) continue;
             atom.out_n_sect = backlinks[atom.out_n_sect];
         }
     }
@@ -1696,7 +1801,7 @@ pub fn sortSections(self: *MachO) !void {
     if (self.getInternalObject()) |object| {
         for (object.getAtoms()) |atom_index| {
             const atom = object.getAtom(atom_index) orelse continue;
-            if (!atom.flags.alive) continue;
+            if (!atom.isAlive()) continue;
             atom.out_n_sect = backlinks[atom.out_n_sect];
         }
     }
@@ -1737,7 +1842,7 @@ pub fn addAtomsToSections(self: *MachO) !void {
     if (self.getZigObject()) |zo| {
         for (zo.getAtoms()) |atom_index| {
             const atom = zo.getAtom(atom_index) orelse continue;
-            if (!atom.flags.alive) continue;
+            if (!atom.isAlive()) continue;
             if (self.isZigSection(atom.out_n_sect)) continue;
             const atoms = &self.sections.items(.atoms)[atom.out_n_sect];
             try atoms.append(gpa, .{ .index = atom_index, .file = zo.index });
@@ -1747,7 +1852,7 @@ pub fn addAtomsToSections(self: *MachO) !void {
         const file = self.getFile(index).?;
         for (file.getAtoms()) |atom_index| {
             const atom = file.getAtom(atom_index) orelse continue;
-            if (!atom.flags.alive) continue;
+            if (!atom.isAlive()) continue;
             const atoms = &self.sections.items(.atoms)[atom.out_n_sect];
             try atoms.append(gpa, .{ .index = atom_index, .file = index });
         }
@@ -1755,7 +1860,7 @@ pub fn addAtomsToSections(self: *MachO) !void {
     if (self.getInternalObject()) |object| {
         for (object.getAtoms()) |atom_index| {
             const atom = object.getAtom(atom_index) orelse continue;
-            if (!atom.flags.alive) continue;
+            if (!atom.isAlive()) continue;
             const atoms = &self.sections.items(.atoms)[atom.out_n_sect];
             try atoms.append(gpa, .{ .index = atom_index, .file = object.index });
         }
@@ -1774,46 +1879,43 @@ fn calcSectionSizes(self: *MachO) !void {
         header.@"align" = 3;
     }
 
-    const slice = self.sections.slice();
-    for (slice.items(.header), slice.items(.atoms)) |*header, atoms| {
-        if (atoms.items.len == 0) continue;
-        if (self.requiresThunks() and header.isCode()) continue;
-
-        for (atoms.items) |ref| {
-            const atom = ref.getAtom(self).?;
-            const atom_alignment = atom.alignment.toByteUnits() orelse 1;
-            const offset = mem.alignForward(u64, header.size, atom_alignment);
-            const padding = offset - header.size;
-            atom.value = offset;
-            header.size += padding + atom.size;
-            header.@"align" = @max(header.@"align", atom.alignment.toLog2Units());
-        }
-    }
-
-    if (self.requiresThunks()) {
+    const tp = self.base.comp.thread_pool;
+    var wg: WaitGroup = .{};
+    {
+        wg.reset();
+        defer wg.wait();
+        const slice = self.sections.slice();
         for (slice.items(.header), slice.items(.atoms), 0..) |header, atoms, i| {
-            if (!header.isCode()) continue;
             if (atoms.items.len == 0) continue;
+            if (self.requiresThunks() and header.isCode()) continue;
+            tp.spawnWg(&wg, calcSectionSizeWorker, .{ self, @as(u8, @intCast(i)) });
+        }
 
-            // Create jump/branch range extenders if needed.
-            try thunks.createThunks(@intCast(i), self);
+        if (self.requiresThunks()) {
+            for (slice.items(.header), slice.items(.atoms), 0..) |header, atoms, i| {
+                if (!header.isCode()) continue;
+                if (atoms.items.len == 0) continue;
+                tp.spawnWg(&wg, createThunksWorker, .{ self, @as(u8, @intCast(i)) });
+            }
         }
-    }
 
-    // At this point, we can also calculate symtab and data-in-code linkedit section sizes
-    if (self.getZigObject()) |zo| {
-        zo.asFile().calcSymtabSize(self);
-    }
-    for (self.objects.items) |index| {
-        self.getFile(index).?.calcSymtabSize(self);
-    }
-    for (self.dylibs.items) |index| {
-        self.getFile(index).?.calcSymtabSize(self);
-    }
-    if (self.getInternalObject()) |obj| {
-        obj.asFile().calcSymtabSize(self);
+        // At this point, we can also calculate symtab and data-in-code linkedit section sizes
+        if (self.getZigObject()) |zo| {
+            tp.spawnWg(&wg, File.calcSymtabSize, .{ zo.asFile(), self });
+        }
+        for (self.objects.items) |index| {
+            tp.spawnWg(&wg, File.calcSymtabSize, .{ self.getFile(index).?, self });
+        }
+        for (self.dylibs.items) |index| {
+            tp.spawnWg(&wg, File.calcSymtabSize, .{ self.getFile(index).?, self });
+        }
+        if (self.getInternalObject()) |obj| {
+            tp.spawnWg(&wg, File.calcSymtabSize, .{ obj.asFile(), self });
+        }
     }
 
+    if (self.has_errors.swap(false, .seq_cst)) return error.FlushFailure;
+
     try self.calcSymtabSize();
 
     if (self.got_sect_index) |idx| {
@@ -1861,6 +1963,49 @@ fn calcSectionSizes(self: *MachO) !void {
     }
 }
 
+fn calcSectionSizeWorker(self: *MachO, sect_id: u8) void {
+    const tracy = trace(@src());
+    defer tracy.end();
+    const doWork = struct {
+        fn doWork(macho_file: *MachO, header: *macho.section_64, atoms: []const Ref) !void {
+            for (atoms) |ref| {
+                const atom = ref.getAtom(macho_file).?;
+                const atom_alignment = atom.alignment.toByteUnits() orelse 1;
+                const offset = mem.alignForward(u64, header.size, atom_alignment);
+                const padding = offset - header.size;
+                atom.value = offset;
+                header.size += padding + atom.size;
+                header.@"align" = @max(header.@"align", atom.alignment.toLog2Units());
+            }
+        }
+    }.doWork;
+    const slice = self.sections.slice();
+    const header = &slice.items(.header)[sect_id];
+    const atoms = slice.items(.atoms)[sect_id].items;
+    doWork(self, header, atoms) catch |err| {
+        self.reportUnexpectedError("failed to calculate size of section '{s},{s}': {s}", .{
+            header.segName(),
+            header.sectName(),
+            @errorName(err),
+        }) catch {};
+        _ = self.has_errors.swap(true, .seq_cst);
+    };
+}
+
+fn createThunksWorker(self: *MachO, sect_id: u8) void {
+    const tracy = trace(@src());
+    defer tracy.end();
+    thunks.createThunks(sect_id, self) catch |err| {
+        const header = self.sections.items(.header)[sect_id];
+        self.reportUnexpectedError("failed to create thunks and calculate size of section '{s},{s}': {s}", .{
+            header.segName(),
+            header.sectName(),
+            @errorName(err),
+        }) catch {};
+        _ = self.has_errors.swap(true, .seq_cst);
+    };
+}
+
 fn generateUnwindInfo(self: *MachO) !void {
     const tracy = trace(@src());
     defer tracy.end();
@@ -2249,64 +2394,98 @@ fn writeSectionsAndUpdateLinkeditSizes(self: *MachO) !void {
     try self.strtab.resize(gpa, cmd.strsize);
     self.strtab.items[0] = 0;
 
-    for (self.objects.items) |index| {
-        try self.getFile(index).?.writeAtoms(self);
-    }
-    if (self.getZigObject()) |zo| {
-        try zo.writeAtoms(self);
-    }
-    if (self.getInternalObject()) |obj| {
-        try obj.asFile().writeAtoms(self);
-    }
-    for (self.thunks.items) |thunk| {
-        const out = self.sections.items(.out)[thunk.out_n_sect].items;
-        const off = math.cast(usize, thunk.value) orelse return error.Overflow;
-        const size = thunk.size();
-        var stream = std.io.fixedBufferStream(out[off..][0..size]);
-        try thunk.write(self, stream.writer());
-    }
+    const tp = self.base.comp.thread_pool;
+    var wg: WaitGroup = .{};
+    {
+        wg.reset();
+        defer wg.wait();
 
-    const slice = self.sections.slice();
-    for (&[_]?u8{
-        self.eh_frame_sect_index,
-        self.unwind_info_sect_index,
-        self.got_sect_index,
-        self.stubs_sect_index,
-        self.la_symbol_ptr_sect_index,
-        self.tlv_ptr_sect_index,
-        self.objc_stubs_sect_index,
-    }) |maybe_sect_id| {
-        if (maybe_sect_id) |sect_id| {
-            const out = slice.items(.out)[sect_id].items;
-            try self.writeSyntheticSection(sect_id, out);
+        for (self.objects.items) |index| {
+            tp.spawnWg(&wg, writeAtomsWorker, .{ self, self.getFile(index).? });
+        }
+        if (self.getZigObject()) |zo| {
+            tp.spawnWg(&wg, writeAtomsWorker, .{ self, zo.asFile() });
+        }
+        if (self.getInternalObject()) |obj| {
+            tp.spawnWg(&wg, writeAtomsWorker, .{ self, obj.asFile() });
+        }
+        for (self.thunks.items) |thunk| {
+            tp.spawnWg(&wg, writeThunkWorker, .{ self, thunk });
         }
-    }
 
-    if (self.la_symbol_ptr_sect_index) |_| {
-        try self.updateLazyBindSize();
-    }
+        const slice = self.sections.slice();
+        for (&[_]?u8{
+            self.eh_frame_sect_index,
+            self.unwind_info_sect_index,
+            self.got_sect_index,
+            self.stubs_sect_index,
+            self.la_symbol_ptr_sect_index,
+            self.tlv_ptr_sect_index,
+            self.objc_stubs_sect_index,
+        }) |maybe_sect_id| {
+            if (maybe_sect_id) |sect_id| {
+                const out = slice.items(.out)[sect_id].items;
+                tp.spawnWg(&wg, writeSyntheticSectionWorker, .{ self, sect_id, out });
+            }
+        }
 
-    try self.rebase.updateSize(self);
-    try self.bind.updateSize(self);
-    try self.weak_bind.updateSize(self);
-    try self.export_trie.updateSize(self);
-    try self.data_in_code.updateSize(self);
+        if (self.la_symbol_ptr_sect_index) |_| {
+            tp.spawnWg(&wg, updateLazyBindSizeWorker, .{self});
+        }
 
-    if (self.getZigObject()) |zo| {
-        zo.asFile().writeSymtab(self, self);
-    }
-    for (self.objects.items) |index| {
-        self.getFile(index).?.writeSymtab(self, self);
-    }
-    for (self.dylibs.items) |index| {
-        self.getFile(index).?.writeSymtab(self, self);
-    }
-    if (self.getInternalObject()) |obj| {
-        obj.asFile().writeSymtab(self, self);
+        tp.spawnWg(&wg, updateLinkeditSizeWorker, .{ self, .rebase });
+        tp.spawnWg(&wg, updateLinkeditSizeWorker, .{ self, .bind });
+        tp.spawnWg(&wg, updateLinkeditSizeWorker, .{ self, .weak_bind });
+        tp.spawnWg(&wg, updateLinkeditSizeWorker, .{ self, .export_trie });
+        tp.spawnWg(&wg, updateLinkeditSizeWorker, .{ self, .data_in_code });
+
+        if (self.getZigObject()) |zo| {
+            tp.spawnWg(&wg, File.writeSymtab, .{ zo.asFile(), self, self });
+        }
+        for (self.objects.items) |index| {
+            tp.spawnWg(&wg, File.writeSymtab, .{ self.getFile(index).?, self, self });
+        }
+        for (self.dylibs.items) |index| {
+            tp.spawnWg(&wg, File.writeSymtab, .{ self.getFile(index).?, self, self });
+        }
+        if (self.getInternalObject()) |obj| {
+            tp.spawnWg(&wg, File.writeSymtab, .{ obj.asFile(), self, self });
+        }
     }
+
+    if (self.has_errors.swap(false, .seq_cst)) return error.FlushFailure;
+}
+
+fn writeAtomsWorker(self: *MachO, file: File) void {
+    const tracy = trace(@src());
+    defer tracy.end();
+    file.writeAtoms(self) catch |err| {
+        self.reportParseError2(file.getIndex(), "failed to resolve relocations and write atoms: {s}", .{
+            @errorName(err),
+        }) catch {};
+        _ = self.has_errors.swap(true, .seq_cst);
+    };
 }
 
-fn writeSyntheticSection(self: *MachO, sect_id: u8, out: []u8) !void {
+fn writeThunkWorker(self: *MachO, thunk: Thunk) void {
+    const tracy = trace(@src());
+    defer tracy.end();
+    const doWork = struct {
+        fn doWork(th: Thunk, buffer: []u8, macho_file: *MachO) !void {
+            const off = th.value;
+            const size = th.size();
+            var stream = std.io.fixedBufferStream(buffer[off..][0..size]);
+            try th.write(macho_file, stream.writer());
+        }
+    }.doWork;
+    const out = self.sections.items(.out)[thunk.out_n_sect].items;
+    doWork(thunk, out, self) catch |err| {
+        self.reportUnexpectedError("failed to write contents of thunk: {s}", .{@errorName(err)}) catch {};
+        _ = self.has_errors.swap(true, .seq_cst);
+    };
+}
+
+fn writeSyntheticSectionWorker(self: *MachO, sect_id: u8, out: []u8) void {
     const tracy = trace(@src());
     defer tracy.end();
 
@@ -2320,6 +2499,22 @@ fn writeSyntheticSection(self: *MachO, sect_id: u8, out: []u8) !void {
         objc_stubs,
     };
 
+    const doWork = struct {
+        fn doWork(macho_file: *MachO, tag: Tag, buffer: []u8) !void {
+            var stream = std.io.fixedBufferStream(buffer);
+            switch (tag) {
+                .eh_frame => eh_frame.write(macho_file, buffer),
+                .unwind_info => try macho_file.unwind_info.write(macho_file, buffer),
+                .got => try macho_file.got.write(macho_file, stream.writer()),
+                .stubs => try macho_file.stubs.write(macho_file, stream.writer()),
+                .la_symbol_ptr => try macho_file.la_symbol_ptr.write(macho_file, stream.writer()),
+                .tlv_ptr => try macho_file.tlv_ptr.write(macho_file, stream.writer()),
+                .objc_stubs => try macho_file.objc_stubs.write(macho_file, stream.writer()),
+            }
+        }
+    }.doWork;
+
+    const header = self.sections.items(.header)[sect_id];
     const tag: Tag = tag: {
         if (self.eh_frame_sect_index != null and
             self.eh_frame_sect_index.? == sect_id) break :tag .eh_frame;
@@ -2337,26 +2532,57 @@ fn writeSyntheticSection(self: *MachO, sect_id: u8, out: []u8) !void {
             self.objc_stubs_sect_index.? == sect_id) break :tag .objc_stubs;
         unreachable;
     };
-    var stream = std.io.fixedBufferStream(out);
-    switch (tag) {
-        .eh_frame => eh_frame.write(self, out),
-        .unwind_info => try self.unwind_info.write(self, out),
-        .got => try self.got.write(self, stream.writer()),
-        .stubs => try self.stubs.write(self, stream.writer()),
-        .la_symbol_ptr => try self.la_symbol_ptr.write(self, stream.writer()),
-        .tlv_ptr => try self.tlv_ptr.write(self, stream.writer()),
-        .objc_stubs => try self.objc_stubs.write(self, stream.writer()),
-    }
+    doWork(self, tag, out) catch |err| {
+        self.reportUnexpectedError("could not write section '{s},{s}': {s}", .{
+            header.segName(),
+            header.sectName(),
+            @errorName(err),
+        }) catch {};
+        _ = self.has_errors.swap(true, .seq_cst);
+    };
 }
 
-fn updateLazyBindSize(self: *MachO) !void {
+fn updateLazyBindSizeWorker(self: *MachO) void {
     const tracy = trace(@src());
     defer tracy.end();
-    try self.lazy_bind.updateSize(self);
-    const sect_id = self.stubs_helper_sect_index.?;
-    const out = &self.sections.items(.out)[sect_id];
-    var stream = std.io.fixedBufferStream(out.items);
-    try self.stubs_helper.write(self, stream.writer());
+    const doWork = struct {
+        fn doWork(macho_file: *MachO) !void {
+            try macho_file.lazy_bind.updateSize(macho_file);
+            const sect_id = macho_file.stubs_helper_sect_index.?;
+            const out = &macho_file.sections.items(.out)[sect_id];
+            var stream = std.io.fixedBufferStream(out.items);
+            try macho_file.stubs_helper.write(macho_file, stream.writer());
+        }
+    }.doWork;
+    doWork(self) catch |err| {
+        self.reportUnexpectedError("could not calculate size of lazy binding section: {s}", .{
+            @errorName(err),
+        }) catch {};
+        _ = self.has_errors.swap(true, .seq_cst);
+    };
+}
+
+pub fn updateLinkeditSizeWorker(self: *MachO, tag: enum {
+    rebase,
+    bind,
+    weak_bind,
+    export_trie,
+    data_in_code,
+}) void {
+    const res = switch (tag) {
+        .rebase => self.rebase.updateSize(self),
+        .bind => self.bind.updateSize(self),
+        .weak_bind => self.weak_bind.updateSize(self),
+        .export_trie => self.export_trie.updateSize(self),
+        .data_in_code => self.data_in_code.updateSize(self),
+    };
+    res catch |err| {
+        self.reportUnexpectedError("could not calculate size of {s} section: {s}", .{
+            @tagName(tag),
+            @errorName(err),
+        }) catch {};
+        _ = self.has_errors.swap(true, .seq_cst);
+    };
 }
 
 fn writeSectionsToFile(self: *MachO) !void {
@@ -2684,13 +2910,13 @@ fn writeHeader(self: *MachO, ncmds: usize, sizeofcmds: usize) !void {
         header.flags |= macho.MH_NO_REEXPORTED_DYLIBS;
     }
 
-    if (self.has_tlv) {
+    if (self.has_tlv.load(.seq_cst)) {
         header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
     }
-    if (self.binds_to_weak) {
+    if (self.binds_to_weak.load(.seq_cst)) {
         header.flags |= macho.MH_BINDS_TO_WEAK;
     }
-    if (self.weak_defines) {
+    if (self.weak_defines.load(.seq_cst)) {
         header.flags |= macho.MH_WEAK_DEFINES;
     }
 
@@ -3586,19 +3812,29 @@ fn reportDuplicates(self: *MachO) error{ HasDuplicates, OutOfMemory }!void {
     const tracy = trace(@src());
     defer tracy.end();
 
+    if (self.dupes.keys().len == 0) return; // Nothing to do
+
+    const gpa = self.base.comp.gpa;
     const max_notes = 3;
 
-    var has_dupes = false;
-    var it = self.dupes.iterator();
-    while (it.next()) |entry| {
-        const sym = self.resolver.keys.items[entry.key_ptr.* - 1];
-        const notes = entry.value_ptr.*;
+    // We will sort by name, and then by file to ensure deterministic output.
+    var keys = try std.ArrayList(SymbolResolver.Index).initCapacity(gpa, self.dupes.keys().len);
+    defer keys.deinit();
+    keys.appendSliceAssumeCapacity(self.dupes.keys());
+    self.sortGlobalSymbolsByName(keys.items);
+
+    for (self.dupes.values()) |*refs| {
+        mem.sort(File.Index, refs.items, {}, std.sort.asc(File.Index));
+    }
+
+    for (keys.items) |key| {
+        const sym = self.resolver.keys.items[key - 1];
+        const notes = self.dupes.get(key).?;
         const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes);
 
         var err = try self.base.addErrorWithNotes(nnotes + 1);
         try err.addMsg("duplicate symbol definition: {s}", .{sym.getName(self)});
         try err.addNote("defined by {}", .{sym.getFile(self).?.fmtPath()});
-        has_dupes = true;
 
         var inote: usize = 0;
         while (inote < @min(notes.items.len, max_notes)) : (inote += 1) {
@@ -3611,7 +3847,7 @@ fn reportDuplicates(self: *MachO) error{ HasDuplicates, OutOfMemory }!void {
             try err.addNote("defined {d} more times", .{remaining});
         }
     }
-    if (has_dupes) return error.HasDuplicates;
+    return error.HasDuplicates;
 }
 
 pub fn getDebugSymbols(self: *MachO) ?*DebugSymbols {
@@ -4210,6 +4446,13 @@ pub const Ref = struct {
         return ref.index == other.index and ref.file == other.file;
     }
 
+    pub fn lessThan(ref: Ref, other: Ref) bool {
+        if (ref.file == other.file) {
+            return ref.index < other.index;
+        }
+        return ref.file < other.file;
+    }
+
     pub fn getFile(ref: Ref, macho_file: *MachO) ?File {
         return macho_file.getFile(ref.file);
     }
src/codegen.zig
@@ -924,7 +924,7 @@ fn genDeclRef(
             const name = decl.name.toSlice(ip);
             const lib_name = if (decl.getOwnedVariable(zcu)) |ov| ov.lib_name.toSlice(ip) else null;
             const sym_index = try macho_file.getGlobalSymbol(name, lib_name);
-            zo.symbols.items[sym_index].flags.needs_got = true;
+            zo.symbols.items[sym_index].setSectionFlags(.{ .needs_got = true });
             return GenResult.mcv(.{ .load_symbol = sym_index });
         }
         const sym_index = try zo.getOrCreateMetadataForDecl(macho_file, decl_index);