Commit 2e87883be8

Jakub Konka <kubkon@jakubkonka.com>
2024-07-09 18:17:23
macho: migrate the MachO driver
1 parent 355992c
Changed files (1)
src
src/link/MachO.zig
@@ -540,13 +540,13 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
     try self.sortSections();
     try self.addAtomsToSections();
     try self.calcSectionSizes();
+
     try self.generateUnwindInfo();
-    try self.initSegments();
 
+    try self.initSegments();
     try self.allocateSections();
     self.allocateSegments();
     self.allocateSyntheticSymbols();
-    try self.allocateLinkeditSegment();
 
     if (build_options.enable_logging) {
         state_log.debug("{}", .{self.dumpState()});
@@ -554,6 +554,8 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
 
     // Beyond this point, everything has been allocated a virtual address and we can resolve
     // the relocations, and commit objects to file.
+    try self.resizeSections();
+
     if (self.getZigObject()) |zo| {
         var has_resolve_error = false;
 
@@ -597,33 +599,11 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
 
         if (has_resolve_error) return error.FlushFailure;
     }
+    try self.writeSectionsAndUpdateLinkeditSizes();
 
-    self.writeAtoms() catch |err| switch (err) {
-        error.ResolveFailed => return error.FlushFailure,
-        else => |e| {
-            try self.reportUnexpectedError("unexpected error while resolving relocations", .{});
-            return e;
-        },
-    };
-    try self.writeUnwindInfo();
-    try self.finalizeDyldInfoSections();
-    try self.writeSyntheticSections();
-
-    var off = math.cast(u32, self.getLinkeditSegment().fileoff) orelse return error.Overflow;
-    off = try self.writeDyldInfoSections(off);
-    off = mem.alignForward(u32, off, @alignOf(u64));
-    off = try self.writeFunctionStarts(off);
-    off = mem.alignForward(u32, off, @alignOf(u64));
-    off = try self.writeDataInCode(self.getTextSegment().vmaddr, off);
-    try self.calcSymtabSize();
-    off = mem.alignForward(u32, off, @alignOf(u64));
-    off = try self.writeSymtab(off);
-    off = mem.alignForward(u32, off, @alignOf(u32));
-    off = try self.writeIndsymtab(off);
-    off = mem.alignForward(u32, off, @alignOf(u64));
-    off = try self.writeStrtab(off);
-
-    self.getLinkeditSegment().filesize = off - self.getLinkeditSegment().fileoff;
+    try self.writeSectionsToFile();
+    try self.allocateLinkeditSegment();
+    try self.writeLinkeditSectionsToFile();
 
     var codesig: ?CodeSignature = if (self.requiresCodeSig()) blk: {
         // Preallocate space for the code signature.
@@ -1498,48 +1478,23 @@ fn checkDuplicates(self: *MachO) !void {
 }
 
 fn markImportsAndExports(self: *MachO) void {
+    const tracy = trace(@src());
+    defer tracy.end();
+
     if (self.getZigObject()) |zo| {
         zo.asFile().markImportsExports(self);
     }
     for (self.objects.items) |index| {
         self.getFile(index).?.markImportsExports(self);
     }
-
-    for (self.undefined_symbols.items) |index| {
-        const sym = self.getSymbol(index);
-        if (sym.getFile(self)) |file| {
-            if (sym.visibility != .global) continue;
-            if (file == .dylib and !sym.flags.abs) sym.flags.import = true;
-        }
-    }
-
-    for (&[_]?Symbol.Index{
-        self.entry_index,
-        self.dyld_stub_binder_index,
-        self.objc_msg_send_index,
-    }) |index| {
-        if (index) |idx| {
-            const sym = self.getSymbol(idx);
-            if (sym.getFile(self)) |file| {
-                if (file == .dylib) sym.flags.import = true;
-            }
-        }
+    if (self.getInternalObject()) |obj| {
+        obj.asFile().markImportsExports(self);
     }
 }
 
 fn deadStripDylibs(self: *MachO) void {
-    for (&[_]?Symbol.Index{
-        self.entry_index,
-        self.dyld_stub_binder_index,
-        self.objc_msg_send_index,
-    }) |index| {
-        if (index) |idx| {
-            const sym = self.getSymbol(idx);
-            if (sym.getFile(self)) |file| {
-                if (file == .dylib) file.dylib.referenced = true;
-            }
-        }
-    }
+    const tracy = trace(@src());
+    defer tracy.end();
 
     for (self.dylibs.items) |index| {
         self.getFile(index).?.dylib.markReferenced(self);
@@ -1560,50 +1515,26 @@ fn scanRelocs(self: *MachO) !void {
     const tracy = trace(@src());
     defer tracy.end();
 
-    if (self.getZigObject()) |zo| try zo.scanRelocs(self);
-
+    if (self.getZigObject()) |zo| {
+        try zo.scanRelocs(self);
+    }
     for (self.objects.items) |index| {
         try self.getFile(index).?.object.scanRelocs(self);
     }
+    if (self.getInternalObject()) |obj| {
+        try obj.scanRelocs(self);
+    }
 
     try self.reportUndefs();
 
-    if (self.entry_index) |index| {
-        const sym = self.getSymbol(index);
-        if (sym.getFile(self) != null) {
-            if (sym.flags.import) sym.flags.stubs = true;
-        }
-    }
-
-    if (self.dyld_stub_binder_index) |index| {
-        const sym = self.getSymbol(index);
-        if (sym.getFile(self) != null) sym.flags.needs_got = true;
+    if (self.getZigObject()) |zo| {
+        try zo.asFile().createSymbolIndirection(self);
     }
-
-    if (self.objc_msg_send_index) |index| {
-        const sym = self.getSymbol(index);
-        if (sym.getFile(self) != null)
-            sym.flags.needs_got = true; // TODO is it always needed, or only if we are synthesising fast stubs?
+    for (self.objects.items) |index| {
+        try self.getFile(index).?.createSymbolIndirection(self);
     }
-
-    for (self.symbols.items, 0..) |*symbol, i| {
-        const index = @as(Symbol.Index, @intCast(i));
-        if (symbol.flags.needs_got) {
-            log.debug("'{s}' needs GOT", .{symbol.getName(self)});
-            try self.got.addSymbol(index, self);
-        }
-        if (symbol.flags.stubs) {
-            log.debug("'{s}' needs STUBS", .{symbol.getName(self)});
-            try self.stubs.addSymbol(index, self);
-        }
-        if (symbol.flags.tlv_ptr) {
-            log.debug("'{s}' needs TLV pointer", .{symbol.getName(self)});
-            try self.tlv_ptr.addSymbol(index, self);
-        }
-        if (symbol.flags.objc_stubs) {
-            log.debug("'{s}' needs OBJC STUBS", .{symbol.getName(self)});
-            try self.objc_stubs.addSymbol(index, self);
-        }
+    if (self.getInternalObject()) |obj| {
+        try obj.asFile().createSymbolIndirection(self);
     }
 }
 
@@ -1611,17 +1542,15 @@ fn reportUndefs(self: *MachO) !void {
     const tracy = trace(@src());
     defer tracy.end();
 
-    switch (self.undefined_treatment) {
-        .dynamic_lookup, .suppress => return,
-        .@"error", .warn => {},
-    }
+    if (self.undefined_treatment == .suppress or
+        self.undefined_treatment == .dynamic_lookup) return;
 
     const max_notes = 4;
 
     var has_undefs = false;
     var it = self.undefs.iterator();
     while (it.next()) |entry| {
-        const undef_sym = self.getSymbol(entry.key_ptr.*);
+        const undef_sym = entry.key_ptr.getSymbol(self).?;
         const notes = entry.value_ptr.*;
         const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes);
 
@@ -1631,8 +1560,9 @@ fn reportUndefs(self: *MachO) !void {
 
         var inote: usize = 0;
         while (inote < @min(notes.items.len, max_notes)) : (inote += 1) {
-            const atom = self.getAtom(notes.items[inote]).?;
-            const file = atom.getFile(self);
+            const note = notes.items[inote];
+            const file = self.getFile(note.file).?;
+            const atom = note.getAtom(self).?;
             try err.addNote(self, "referenced by {}:{s}", .{ file.fmtPath(), atom.getName(self) });
         }
 
@@ -1641,64 +1571,18 @@ fn reportUndefs(self: *MachO) !void {
             try err.addNote(self, "referenced {d} more times", .{remaining});
         }
     }
-
-    for (self.undefined_symbols.items) |index| {
-        const sym = self.getSymbol(index);
-        if (sym.getFile(self) != null) continue; // If undefined in an object file, will be reported above
-        has_undefs = true;
-        var err = try self.addErrorWithNotes(1);
-        try err.addMsg(self, "undefined symbol: {s}", .{sym.getName(self)});
-        try err.addNote(self, "-u command line option", .{});
-    }
-
-    if (self.entry_index) |index| {
-        const sym = self.getSymbol(index);
-        if (sym.getFile(self) == null) {
-            has_undefs = true;
-            var err = try self.addErrorWithNotes(1);
-            try err.addMsg(self, "undefined symbol: {s}", .{sym.getName(self)});
-            try err.addNote(self, "implicit entry/start for main executable", .{});
-        }
-    }
-
-    if (self.dyld_stub_binder_index) |index| {
-        const sym = self.getSymbol(index);
-        if (sym.getFile(self) == null and self.stubs_sect_index != null) {
-            has_undefs = true;
-            var err = try self.addErrorWithNotes(1);
-            try err.addMsg(self, "undefined symbol: {s}", .{sym.getName(self)});
-            try err.addNote(self, "implicit -u command line option", .{});
-        }
-    }
-
-    if (self.objc_msg_send_index) |index| {
-        const sym = self.getSymbol(index);
-        if (sym.getFile(self) == null and self.objc_stubs_sect_index != null) {
-            has_undefs = true;
-            var err = try self.addErrorWithNotes(1);
-            try err.addMsg(self, "undefined symbol: {s}", .{sym.getName(self)});
-            try err.addNote(self, "implicit -u command line option", .{});
-        }
-    }
-
     if (has_undefs) return error.HasUndefinedSymbols;
 }
 
 fn initOutputSections(self: *MachO) !void {
+    const tracy = trace(@src());
+    defer tracy.end();
+
     for (self.objects.items) |index| {
-        const object = self.getFile(index).?.object;
-        for (object.atoms.items) |atom_index| {
-            const atom = self.getAtom(atom_index) orelse continue;
-            if (!atom.flags.alive) continue;
-            atom.out_n_sect = try Atom.initOutputSection(atom.getInputSection(self), self);
-        }
+        try self.getFile(index).?.initOutputSections(self);
     }
-    if (self.getInternalObject()) |object| {
-        for (object.atoms.items) |atom_index| {
-            const atom = self.getAtom(atom_index) orelse continue;
-            if (!atom.flags.alive) continue;
-            atom.out_n_sect = try Atom.initOutputSection(atom.getInputSection(self), self);
-        }
+    if (self.getInternalObject()) |obj| {
+        try obj.asFile().initOutputSections(self);
     }
     self.text_sect_index = self.getSectionByName("__TEXT", "__text") orelse
         try self.addSection("__TEXT", "__text", .{
@@ -1771,46 +1655,50 @@ fn initSyntheticSections(self: *MachO) !void {
         self.eh_frame_sect_index = try self.addSection("__TEXT", "__eh_frame", .{});
     }
 
-    for (self.boundary_symbols.items) |sym_index| {
+    if (self.getInternalObject()) |obj| {
         const gpa = self.base.comp.gpa;
-        const sym = self.getSymbol(sym_index);
-        const name = sym.getName(self);
-
-        if (eatPrefix(name, "segment$start$")) |segname| {
-            if (self.getSegmentByName(segname) == null) { // TODO check segname is valid
-                const prot = getSegmentProt(segname);
-                _ = try self.segments.append(gpa, .{
-                    .cmdsize = @sizeOf(macho.segment_command_64),
-                    .segname = makeStaticString(segname),
-                    .initprot = prot,
-                    .maxprot = prot,
-                });
-            }
-        } else if (eatPrefix(name, "segment$stop$")) |segname| {
-            if (self.getSegmentByName(segname) == null) { // TODO check segname is valid
-                const prot = getSegmentProt(segname);
-                _ = try self.segments.append(gpa, .{
-                    .cmdsize = @sizeOf(macho.segment_command_64),
-                    .segname = makeStaticString(segname),
-                    .initprot = prot,
-                    .maxprot = prot,
-                });
-            }
-        } else if (eatPrefix(name, "section$start$")) |actual_name| {
-            const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic
-            const segname = actual_name[0..sep]; // TODO check segname is valid
-            const sectname = actual_name[sep + 1 ..]; // TODO check sectname is valid
-            if (self.getSectionByName(segname, sectname) == null) {
-                _ = try self.addSection(segname, sectname, .{});
-            }
-        } else if (eatPrefix(name, "section$stop$")) |actual_name| {
-            const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic
-            const segname = actual_name[0..sep]; // TODO check segname is valid
-            const sectname = actual_name[sep + 1 ..]; // TODO check sectname is valid
-            if (self.getSectionByName(segname, sectname) == null) {
-                _ = try self.addSection(segname, sectname, .{});
-            }
-        } else unreachable;
+
+        for (obj.boundary_symbols.items) |sym_index| {
+            const ref = obj.getSymbolRef(sym_index, self);
+            const sym = ref.getSymbol(self).?;
+            const name = sym.getName(self);
+
+            if (eatPrefix(name, "segment$start$")) |segname| {
+                if (self.getSegmentByName(segname) == null) { // TODO check segname is valid
+                    const prot = getSegmentProt(segname);
+                    _ = try self.segments.append(gpa, .{
+                        .cmdsize = @sizeOf(macho.segment_command_64),
+                        .segname = makeStaticString(segname),
+                        .initprot = prot,
+                        .maxprot = prot,
+                    });
+                }
+            } else if (eatPrefix(name, "segment$stop$")) |segname| {
+                if (self.getSegmentByName(segname) == null) { // TODO check segname is valid
+                    const prot = getSegmentProt(segname);
+                    _ = try self.segments.append(gpa, .{
+                        .cmdsize = @sizeOf(macho.segment_command_64),
+                        .segname = makeStaticString(segname),
+                        .initprot = prot,
+                        .maxprot = prot,
+                    });
+                }
+            } else if (eatPrefix(name, "section$start$")) |actual_name| {
+                const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic
+                const segname = actual_name[0..sep]; // TODO check segname is valid
+                const sectname = actual_name[sep + 1 ..]; // TODO check sectname is valid
+                if (self.getSectionByName(segname, sectname) == null) {
+                    _ = try self.addSection(segname, sectname, .{});
+                }
+            } else if (eatPrefix(name, "section$stop$")) |actual_name| {
+                const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic
+                const segname = actual_name[0..sep]; // TODO check segname is valid
+                const sectname = actual_name[sep + 1 ..]; // TODO check sectname is valid
+                if (self.getSectionByName(segname, sectname) == null) {
+                    _ = try self.addSection(segname, sectname, .{});
+                }
+            } else unreachable;
+        }
     }
 }
 
@@ -1917,38 +1805,25 @@ pub fn sortSections(self: *MachO) !void {
     }
 
     if (self.getZigObject()) |zo| {
-        for (zo.atoms.items) |atom_index| {
-            const atom = self.getAtom(atom_index) orelse continue;
+        for (zo.getAtoms()) |atom_index| {
+            const atom = zo.getAtom(atom_index) orelse continue;
             if (!atom.flags.alive) continue;
             atom.out_n_sect = backlinks[atom.out_n_sect];
         }
-
-        for (zo.symtab.items(.nlist)) |*sym| {
-            if (sym.sect()) {
-                sym.n_sect = backlinks[sym.n_sect - 1] + 1;
-            }
-        }
-
-        for (zo.symbols.items) |sym_index| {
-            const sym = self.getSymbol(sym_index);
-            const atom = sym.getAtom(self) orelse continue;
-            if (!atom.flags.alive) continue;
-            if (sym.getFile(self).?.getIndex() != zo.index) continue;
-            sym.out_n_sect = backlinks[sym.out_n_sect];
-        }
     }
 
     for (self.objects.items) |index| {
-        for (self.getFile(index).?.object.atoms.items) |atom_index| {
-            const atom = self.getAtom(atom_index) orelse continue;
+        const file = self.getFile(index).?;
+        for (file.getAtoms()) |atom_index| {
+            const atom = file.getAtom(atom_index) orelse continue;
             if (!atom.flags.alive) continue;
             atom.out_n_sect = backlinks[atom.out_n_sect];
         }
     }
 
     if (self.getInternalObject()) |object| {
-        for (object.atoms.items) |atom_index| {
-            const atom = self.getAtom(atom_index) orelse continue;
+        for (object.getAtoms()) |atom_index| {
+            const atom = object.getAtom(atom_index) orelse continue;
             if (!atom.flags.alive) continue;
             atom.out_n_sect = backlinks[atom.out_n_sect];
         }
@@ -1985,35 +1860,23 @@ pub fn addAtomsToSections(self: *MachO) !void {
     const tracy = trace(@src());
     defer tracy.end();
 
+    const gpa = self.base.comp.gpa;
+
     for (self.objects.items) |index| {
-        const object = self.getFile(index).?.object;
-        for (object.atoms.items) |atom_index| {
-            const atom = self.getAtom(atom_index) orelse continue;
+        const file = self.getFile(index).?;
+        for (file.getAtoms()) |atom_index| {
+            const atom = file.getAtom(atom_index) orelse continue;
             if (!atom.flags.alive) continue;
             const atoms = &self.sections.items(.atoms)[atom.out_n_sect];
-            try atoms.append(self.base.comp.gpa, atom_index);
-        }
-        for (object.symbols.items) |sym_index| {
-            const sym = self.getSymbol(sym_index);
-            const atom = sym.getAtom(self) orelse continue;
-            if (!atom.flags.alive) continue;
-            if (sym.getFile(self).?.getIndex() != index) continue;
-            sym.out_n_sect = atom.out_n_sect;
+            try atoms.append(gpa, .{ .index = atom_index, .file = index });
         }
     }
     if (self.getInternalObject()) |object| {
-        for (object.atoms.items) |atom_index| {
-            const atom = self.getAtom(atom_index) orelse continue;
+        for (object.getAtoms()) |atom_index| {
+            const atom = object.getAtom(atom_index) orelse continue;
             if (!atom.flags.alive) continue;
             const atoms = &self.sections.items(.atoms)[atom.out_n_sect];
-            try atoms.append(self.base.comp.gpa, atom_index);
-        }
-        for (object.symbols.items) |sym_index| {
-            const sym = self.getSymbol(sym_index);
-            const atom = sym.getAtom(self) orelse continue;
-            if (!atom.flags.alive) continue;
-            if (sym.getFile(self).?.getIndex() != object.index) continue;
-            sym.out_n_sect = atom.out_n_sect;
+            try atoms.append(gpa, .{ .index = atom_index, .file = object.index });
         }
     }
 }
@@ -2035,8 +1898,8 @@ fn calcSectionSizes(self: *MachO) !void {
         if (atoms.items.len == 0) continue;
         if (self.requiresThunks() and header.isCode()) continue;
 
-        for (atoms.items) |atom_index| {
-            const atom = self.getAtom(atom_index).?;
+        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;
@@ -2056,6 +1919,22 @@ fn calcSectionSizes(self: *MachO) !void {
         }
     }
 
+    // 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);
+    }
+
+    try self.calcSymtabSize();
+
     if (self.got_sect_index) |idx| {
         const header = &self.sections.items(.header)[idx];
         header.size = self.got.size();
@@ -2336,77 +2215,61 @@ fn allocateSegments(self: *MachO) void {
 }
 
 fn allocateSyntheticSymbols(self: *MachO) void {
-    const text_seg = self.getTextSegment();
-
-    if (self.mh_execute_header_index) |index| {
-        const global = self.getSymbol(index);
-        global.value = text_seg.vmaddr;
-    }
+    if (self.getInternalObject()) |obj| {
+        obj.allocateSyntheticSymbols(self);
 
-    if (self.data_sect_index) |idx| {
-        const sect = self.sections.items(.header)[idx];
-        for (&[_]?Symbol.Index{
-            self.dso_handle_index,
-            self.mh_dylib_header_index,
-            self.dyld_private_index,
-        }) |maybe_index| {
-            if (maybe_index) |index| {
-                const global = self.getSymbol(index);
-                global.value = sect.addr;
-                global.out_n_sect = idx;
-            }
-        }
-    }
+        const text_seg = self.getTextSegment();
 
-    for (self.boundary_symbols.items) |sym_index| {
-        const sym = self.getSymbol(sym_index);
-        const name = sym.getName(self);
+        for (obj.boundary_symbols.items) |sym_index| {
+            const ref = obj.getSymbolRef(sym_index, self);
+            const sym = ref.getSymbol(self).?;
+            const name = sym.getName(self);
 
-        sym.flags.@"export" = false;
-        sym.value = text_seg.vmaddr;
+            sym.value = text_seg.vmaddr;
 
-        if (mem.startsWith(u8, name, "segment$start$")) {
-            const segname = name["segment$start$".len..];
-            if (self.getSegmentByName(segname)) |seg_id| {
-                const seg = self.segments.items[seg_id];
-                sym.value = seg.vmaddr;
-            }
-        } else if (mem.startsWith(u8, name, "segment$stop$")) {
-            const segname = name["segment$stop$".len..];
-            if (self.getSegmentByName(segname)) |seg_id| {
-                const seg = self.segments.items[seg_id];
-                sym.value = seg.vmaddr + seg.vmsize;
-            }
-        } else if (mem.startsWith(u8, name, "section$start$")) {
-            const actual_name = name["section$start$".len..];
-            const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic
-            const segname = actual_name[0..sep];
-            const sectname = actual_name[sep + 1 ..];
-            if (self.getSectionByName(segname, sectname)) |sect_id| {
-                const sect = self.sections.items(.header)[sect_id];
-                sym.value = sect.addr;
-                sym.out_n_sect = sect_id;
-            }
-        } else if (mem.startsWith(u8, name, "section$stop$")) {
-            const actual_name = name["section$stop$".len..];
-            const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic
-            const segname = actual_name[0..sep];
-            const sectname = actual_name[sep + 1 ..];
-            if (self.getSectionByName(segname, sectname)) |sect_id| {
-                const sect = self.sections.items(.header)[sect_id];
-                sym.value = sect.addr + sect.size;
-                sym.out_n_sect = sect_id;
-            }
-        } else unreachable;
-    }
+            if (mem.startsWith(u8, name, "segment$start$")) {
+                const segname = name["segment$start$".len..];
+                if (self.getSegmentByName(segname)) |seg_id| {
+                    const seg = self.segments.items[seg_id];
+                    sym.value = seg.vmaddr;
+                }
+            } else if (mem.startsWith(u8, name, "segment$stop$")) {
+                const segname = name["segment$stop$".len..];
+                if (self.getSegmentByName(segname)) |seg_id| {
+                    const seg = self.segments.items[seg_id];
+                    sym.value = seg.vmaddr + seg.vmsize;
+                }
+            } else if (mem.startsWith(u8, name, "section$start$")) {
+                const actual_name = name["section$start$".len..];
+                const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic
+                const segname = actual_name[0..sep];
+                const sectname = actual_name[sep + 1 ..];
+                if (self.getSectionByName(segname, sectname)) |sect_id| {
+                    const sect = self.sections.items(.header)[sect_id];
+                    sym.value = sect.addr;
+                    sym.out_n_sect = sect_id;
+                }
+            } else if (mem.startsWith(u8, name, "section$stop$")) {
+                const actual_name = name["section$stop$".len..];
+                const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic
+                const segname = actual_name[0..sep];
+                const sectname = actual_name[sep + 1 ..];
+                if (self.getSectionByName(segname, sectname)) |sect_id| {
+                    const sect = self.sections.items(.header)[sect_id];
+                    sym.value = sect.addr + sect.size;
+                    sym.out_n_sect = sect_id;
+                }
+            } else unreachable;
+        }
 
-    if (self.objc_stubs.symbols.items.len > 0) {
-        const addr = self.sections.items(.header)[self.objc_stubs_sect_index.?].addr;
+        if (self.objc_stubs.symbols.items.len > 0) {
+            const addr = self.sections.items(.header)[self.objc_stubs_sect_index.?].addr;
 
-        for (self.objc_stubs.symbols.items, 0..) |sym_index, idx| {
-            const sym = self.getSymbol(sym_index);
-            sym.value = addr + idx * ObjcStubsSection.entrySize(self.getTarget().cpu.arch);
-            sym.out_n_sect = self.objc_stubs_sect_index.?;
+            for (self.objc_stubs.symbols.items, 0..) |ref, idx| {
+                const sym = ref.getSymbol(self).?;
+                sym.value = addr + idx * ObjcStubsSection.entrySize(self.getTarget().cpu.arch);
+                sym.out_n_sect = self.objc_stubs_sect_index.?;
+            }
         }
     }
 }
@@ -2424,177 +2287,224 @@ fn allocateLinkeditSegment(self: *MachO) !void {
     const seg = self.getLinkeditSegment();
     seg.vmaddr = mem.alignForward(u64, vmaddr, page_size);
     seg.fileoff = mem.alignForward(u64, fileoff, page_size);
-}
 
-fn writeAtoms(self: *MachO) !void {
-    const tracy = trace(@src());
-    defer tracy.end();
+    var off = math.cast(u32, seg.fileoff) orelse return error.Overflow;
+    // DYLD_INFO_ONLY
+    {
+        const cmd = &self.dyld_info_cmd;
+        cmd.rebase_off = off;
+        off += cmd.rebase_size;
+        cmd.bind_off = off;
+        off += cmd.bind_size;
+        cmd.weak_bind_off = off;
+        off += cmd.weak_bind_size;
+        cmd.lazy_bind_off = off;
+        off += cmd.lazy_bind_size;
+        cmd.export_off = off;
+        off += cmd.export_size;
+        off = mem.alignForward(u32, off, @alignOf(u64));
+    }
+
+    // FUNCTION_STARTS
+    {
+        const cmd = &self.function_starts_cmd;
+        cmd.dataoff = off;
+        off += cmd.datasize;
+        off = mem.alignForward(u32, off, @alignOf(u64));
+    }
 
-    const gpa = self.base.comp.gpa;
-    var arena = std.heap.ArenaAllocator.init(gpa);
-    defer arena.deinit();
+    // DATA_IN_CODE
+    {
+        const cmd = &self.data_in_code_cmd;
+        cmd.dataoff = off;
+        off += cmd.datasize;
+        off = mem.alignForward(u32, off, @alignOf(u64));
+    }
 
-    const cpu_arch = self.getTarget().cpu.arch;
-    const slice = self.sections.slice();
+    // SYMTAB (symtab)
+    {
+        const cmd = &self.symtab_cmd;
+        cmd.symoff = off;
+        off += cmd.nsyms * @sizeOf(macho.nlist_64);
+        off = mem.alignForward(u32, off, @alignOf(u32));
+    }
 
-    var has_resolve_error = false;
-    for (slice.items(.header), slice.items(.atoms)) |header, atoms| {
+    // DYSYMTAB
+    {
+        const cmd = &self.dysymtab_cmd;
+        cmd.indirectsymoff = off;
+        off += cmd.nindirectsyms * @sizeOf(u32);
+        off = mem.alignForward(u32, off, @alignOf(u64));
+    }
+
+    // SYMTAB (strtab)
+    {
+        const cmd = &self.symtab_cmd;
+        cmd.stroff = off;
+        off += cmd.strsize;
+    }
+
+    seg.filesize = off - seg.fileoff;
+}
+
+fn resizeSections(self: *MachO) !void {
+    const slice = self.sections.slice();
+    for (slice.items(.header), slice.items(.atoms), slice.items(.out)) |header, atoms, *out| {
         if (atoms.items.len == 0) continue;
         if (header.isZerofill()) continue;
-
-        const size = math.cast(usize, header.size) orelse return error.Overflow;
-        const buffer = try gpa.alloc(u8, size);
-        defer gpa.free(buffer);
+        const cpu_arch = self.getTarget().cpu.arch;
+        try out.resize(self.base.comp.gpa, header.size);
         const padding_byte: u8 = if (header.isCode() and cpu_arch == .x86_64) 0xcc else 0;
-        @memset(buffer, padding_byte);
+        @memset(out.items, padding_byte);
+    }
+}
 
-        for (atoms.items) |atom_index| {
-            const atom = self.getAtom(atom_index).?;
-            assert(atom.flags.alive);
-            const off = math.cast(usize, atom.value) orelse return error.Overflow;
-            const atom_size = math.cast(usize, atom.size) orelse return error.Overflow;
-            try atom.getData(self, buffer[off..][0..atom_size]);
-            atom.resolveRelocs(self, buffer[off..][0..atom_size]) catch |err| switch (err) {
-                error.ResolveFailed => has_resolve_error = true,
-                else => |e| return e,
-            };
-        }
+fn writeSectionsAndUpdateLinkeditSizes(self: *MachO) !void {
+    const gpa = self.base.comp.gpa;
 
-        try self.base.file.?.pwriteAll(buffer, header.offset);
-    }
+    const cmd = self.symtab_cmd;
+    try self.symtab.resize(gpa, cmd.nsyms);
+    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.getInternalObject()) |obj| {
+        try obj.asFile().writeAtoms(self);
+    }
     for (self.thunks.items) |thunk| {
-        const header = slice.items(.header)[thunk.out_n_sect];
-        const offset = thunk.value + header.offset;
-        const buffer = try gpa.alloc(u8, thunk.size());
-        defer gpa.free(buffer);
-        var stream = std.io.fixedBufferStream(buffer);
+        const out = self.sections.items(.out)[thunk.out_n_sect].items;
+        const off = thunk.value;
+        const size = thunk.size();
+        var stream = std.io.fixedBufferStream(out[off..][0..size]);
         try thunk.write(self, stream.writer());
-        try self.base.file.?.pwriteAll(buffer, offset);
     }
 
-    if (has_resolve_error) return error.ResolveFailed;
-}
-
-fn writeUnwindInfo(self: *MachO) !void {
-    const tracy = trace(@src());
-    defer tracy.end();
-
-    const gpa = self.base.comp.gpa;
-
-    if (self.eh_frame_sect_index) |index| {
-        const header = self.sections.items(.header)[index];
-        const size = math.cast(usize, header.size) orelse return error.Overflow;
-        const buffer = try gpa.alloc(u8, size);
-        defer gpa.free(buffer);
-        eh_frame.write(self, buffer);
-        try self.base.file.?.pwriteAll(buffer, header.offset);
+    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];
+            try self.writeSyntheticSection(sect_id, out);
+        }
     }
 
-    if (self.unwind_info_sect_index) |index| {
-        const header = self.sections.items(.header)[index];
-        const size = math.cast(usize, header.size) orelse return error.Overflow;
-        const buffer = try gpa.alloc(u8, size);
-        defer gpa.free(buffer);
-        try self.unwind_info.write(self, buffer);
-        try self.base.file.?.pwriteAll(buffer, header.offset);
+    if (self.la_symbol_ptr_sect_index) |_| {
+        try self.updatelazyBindSize();
     }
-}
 
-fn finalizeDyldInfoSections(self: *MachO) !void {
-    const tracy = trace(@src());
-    defer tracy.end();
     try self.rebase.updateSize(self);
     try self.bind.updateSize(self);
     try self.weak_bind.updateSize(self);
-    if (self.la_symbol_ptr_sect_index) |_| {
-        try self.lazy_bind.updateSize(self);
-    }
     try self.export_trie.updateSize(self);
+    try self.data_in_code.updateSize(self);
+
+    if (self.getZigObject()) |zo| {
+        zo.asFile().writeSymtab(self);
+    }
+    for (self.objects.items) |index| {
+        self.getFile(index).?.writeSymtab(self);
+    }
+    for (self.dylibs.items) |index| {
+        self.getFile(index).?.writeSymtab(self);
+    }
+    if (self.getInternalObject()) |obj| {
+        obj.asFile().writeSymtab(self);
+    }
 }
 
-fn writeSyntheticSections(self: *MachO) !void {
+fn writeSyntheticSection(self: *MachO, sect_id: u8, out: []u8) !void {
     const tracy = trace(@src());
     defer tracy.end();
 
-    const gpa = self.base.comp.gpa;
-
-    if (self.got_sect_index) |sect_id| {
-        const header = self.sections.items(.header)[sect_id];
-        const size = math.cast(usize, header.size) orelse return error.Overflow;
-        var buffer = try std.ArrayList(u8).initCapacity(gpa, size);
-        defer buffer.deinit();
-        try self.got.write(self, buffer.writer());
-        assert(buffer.items.len == header.size);
-        try self.base.file.?.pwriteAll(buffer.items, header.offset);
-    }
+    const Tag = enum {
+        eh_frame,
+        unwind_info,
+        got,
+        stubs,
+        la_symbol_ptr,
+        tlv_ptr,
+        objc_stubs,
+    };
 
-    if (self.stubs_sect_index) |sect_id| {
-        const header = self.sections.items(.header)[sect_id];
-        const size = math.cast(usize, header.size) orelse return error.Overflow;
-        var buffer = try std.ArrayList(u8).initCapacity(gpa, size);
-        defer buffer.deinit();
-        try self.stubs.write(self, buffer.writer());
-        assert(buffer.items.len == header.size);
-        try self.base.file.?.pwriteAll(buffer.items, header.offset);
+    const tag: Tag = tag: {
+        if (self.eh_frame_sect_index != null and
+            self.eh_frame_sect_index.? == sect_id) break :tag .eh_frame;
+        if (self.unwind_info_sect_index != null and
+            self.unwind_info_sect_index.? == sect_id) break :tag .unwind_info;
+        if (self.got_sect_index != null and
+            self.got_sect_index.? == sect_id) break :tag .got;
+        if (self.stubs_sect_index != null and
+            self.stubs_sect_index.? == sect_id) break :tag .stubs;
+        if (self.la_symbol_ptr_sect_index != null and
+            self.la_symbol_ptr_sect_index.? == sect_id) break :tag .la_symbol_ptr;
+        if (self.tlv_ptr_sect_index != null and
+            self.tlv_ptr_sect_index.? == sect_id) break :tag .tlv_ptr;
+        if (self.objc_stubs_sect_index != null and
+            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()),
     }
+}
 
-    if (self.stubs_helper_sect_index) |sect_id| {
-        const header = self.sections.items(.header)[sect_id];
-        const size = math.cast(usize, header.size) orelse return error.Overflow;
-        var buffer = try std.ArrayList(u8).initCapacity(gpa, size);
-        defer buffer.deinit();
-        try self.stubs_helper.write(self, buffer.writer());
-        assert(buffer.items.len == header.size);
-        try self.base.file.?.pwriteAll(buffer.items, header.offset);
-    }
+fn updateLazyBindSize(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());
+}
 
-    if (self.la_symbol_ptr_sect_index) |sect_id| {
-        const header = self.sections.items(.header)[sect_id];
-        const size = math.cast(usize, header.size) orelse return error.Overflow;
-        var buffer = try std.ArrayList(u8).initCapacity(gpa, size);
-        defer buffer.deinit();
-        try self.la_symbol_ptr.write(self, buffer.writer());
-        assert(buffer.items.len == header.size);
-        try self.base.file.?.pwriteAll(buffer.items, header.offset);
-    }
+fn writeSectionsToFile(self: *MachO) !void {
+    const tracy = trace(@src());
+    defer tracy.end();
 
-    if (self.tlv_ptr_sect_index) |sect_id| {
-        const header = self.sections.items(.header)[sect_id];
-        const size = math.cast(usize, header.size) orelse return error.Overflow;
-        var buffer = try std.ArrayList(u8).initCapacity(gpa, size);
-        defer buffer.deinit();
-        try self.tlv_ptr.write(self, buffer.writer());
-        assert(buffer.items.len == header.size);
-        try self.base.file.?.pwriteAll(buffer.items, header.offset);
+    const slice = self.sections.slice();
+    for (slice.items(.header), slice.items(.out)) |header, out| {
+        try self.base.file.pwriteAll(out.items, header.offset);
     }
+}
 
-    if (self.objc_stubs_sect_index) |sect_id| {
-        const header = self.sections.items(.header)[sect_id];
-        const size = math.cast(usize, header.size) orelse return error.Overflow;
-        var buffer = try std.ArrayList(u8).initCapacity(gpa, size);
-        defer buffer.deinit();
-        try self.objc_stubs.write(self, buffer.writer());
-        assert(buffer.items.len == header.size);
-        try self.base.file.?.pwriteAll(buffer.items, header.offset);
-    }
+fn writeLinkeditSectionsToFile(self: *MachO) !void {
+    const tracy = trace(@src());
+    defer tracy.end();
+    try self.writeDyldInfo();
+    try self.writeDataInCode();
+    try self.writeSymtabToFile();
+    try self.writeIndsymtab();
 }
 
-fn writeDyldInfoSections(self: *MachO, off: u32) !u32 {
+fn writeDyldInfo(self: *MachO) !void {
     const tracy = trace(@src());
     defer tracy.end();
 
-    const gpa = self.base.comp.gpa;
-    const cmd = &self.dyld_info_cmd;
+    const gpa = self.base.allocator;
+    const base_off = self.getLinkeditSegment().fileoff;
+    const cmd = self.dyld_info_cmd;
     var needed_size: u32 = 0;
     needed_size += cmd.rebase_size;
-    cmd.bind_off = needed_size;
     needed_size += cmd.bind_size;
-    cmd.weak_bind_off = needed_size;
     needed_size += cmd.weak_bind_size;
-    cmd.lazy_bind_off = needed_size;
     needed_size += cmd.lazy_bind_size;
-    cmd.export_off = needed_size;
     needed_size += cmd.export_size;
 
     const buffer = try gpa.alloc(u8, needed_size);
@@ -2605,88 +2515,74 @@ fn writeDyldInfoSections(self: *MachO, off: u32) !u32 {
     const writer = stream.writer();
 
     try self.rebase.write(writer);
-    try stream.seekTo(cmd.bind_off);
+    try stream.seekTo(cmd.bind_off - base_off);
     try self.bind.write(writer);
-    try stream.seekTo(cmd.weak_bind_off);
+    try stream.seekTo(cmd.weak_bind_off - base_off);
     try self.weak_bind.write(writer);
-    try stream.seekTo(cmd.lazy_bind_off);
+    try stream.seekTo(cmd.lazy_bind_off - base_off);
     try self.lazy_bind.write(writer);
-    try stream.seekTo(cmd.export_off);
+    try stream.seekTo(cmd.export_off - base_off);
     try self.export_trie.write(writer);
+    try self.base.file.pwriteAll(buffer, cmd.rebase_off);
+}
 
-    cmd.rebase_off += off;
-    cmd.bind_off += off;
-    cmd.weak_bind_off += off;
-    cmd.lazy_bind_off += off;
-    cmd.export_off += off;
-    try self.base.file.?.pwriteAll(buffer, off);
+pub fn writeDataInCode(self: *MachO) !void {
+    const tracy = trace(@src());
+    defer tracy.end();
+    const cmd = self.data_in_code_cmd;
+    try self.base.file.pwriteAll(mem.sliceAsBytes(self.data_in_code.entries.items), cmd.dataoff);
+}
 
-    return off + needed_size;
+fn writeIndsymtab(self: *MachO) !void {
+    const tracy = trace(@src());
+    defer tracy.end();
+    const gpa = self.base.comp.gpa;
+    const cmd = self.dysymtab_cmd;
+    const needed_size = cmd.nindirectsyms * @sizeOf(u32);
+    var buffer = try std.ArrayList(u8).initCapacity(gpa, needed_size);
+    defer buffer.deinit();
+    try self.indsymtab.write(self, buffer.writer());
+    try self.base.file.pwriteAll(buffer.items, cmd.indirectsymoff);
 }
 
-fn writeFunctionStarts(self: *MachO, off: u32) !u32 {
-    // TODO actually write it out
-    const cmd = &self.function_starts_cmd;
-    cmd.dataoff = off;
-    return off;
+pub fn writeSymtabToFile(self: *MachO) !void {
+    const tracy = trace(@src());
+    defer tracy.end();
+    const cmd = self.symtab_cmd;
+    try self.base.file.pwriteAll(mem.sliceAsBytes(self.symtab.items), cmd.symoff);
+    try self.base.file.pwriteAll(self.strtab.items, cmd.stroff);
 }
 
-pub fn writeDataInCode(self: *MachO, base_address: u64, off: u32) !u32 {
-    const cmd = &self.data_in_code_cmd;
-    cmd.dataoff = off;
+fn writeUnwindInfo(self: *MachO) !void {
+    const tracy = trace(@src());
+    defer tracy.end();
 
     const gpa = self.base.comp.gpa;
-    var dices = std.ArrayList(macho.data_in_code_entry).init(gpa);
-    defer dices.deinit();
-
-    for (self.objects.items) |index| {
-        const object = self.getFile(index).?.object;
-        const in_dices = object.getDataInCode();
-
-        try dices.ensureUnusedCapacity(in_dices.len);
 
-        var next_dice: usize = 0;
-        for (object.atoms.items) |atom_index| {
-            if (next_dice >= in_dices.len) break;
-            const atom = self.getAtom(atom_index) orelse continue;
-            const start_off = atom.getInputAddress(self);
-            const end_off = start_off + atom.size;
-            const start_dice = next_dice;
-
-            if (end_off < in_dices[next_dice].offset) continue;
-
-            while (next_dice < in_dices.len and
-                in_dices[next_dice].offset < end_off) : (next_dice += 1)
-            {}
-
-            if (atom.flags.alive) for (in_dices[start_dice..next_dice]) |dice| {
-                dices.appendAssumeCapacity(.{
-                    .offset = @intCast(atom.getAddress(self) + dice.offset - start_off - base_address),
-                    .length = dice.length,
-                    .kind = dice.kind,
-                });
-            };
-        }
+    if (self.eh_frame_sect_index) |index| {
+        const header = self.sections.items(.header)[index];
+        const size = math.cast(usize, header.size) orelse return error.Overflow;
+        const buffer = try gpa.alloc(u8, size);
+        defer gpa.free(buffer);
+        eh_frame.write(self, buffer);
+        try self.base.file.?.pwriteAll(buffer, header.offset);
     }
 
-    const needed_size = math.cast(u32, dices.items.len * @sizeOf(macho.data_in_code_entry)) orelse return error.Overflow;
-    cmd.datasize = needed_size;
-
-    try self.base.file.?.pwriteAll(mem.sliceAsBytes(dices.items), cmd.dataoff);
-
-    return off + needed_size;
+    if (self.unwind_info_sect_index) |index| {
+        const header = self.sections.items(.header)[index];
+        const size = math.cast(usize, header.size) orelse return error.Overflow;
+        const buffer = try gpa.alloc(u8, size);
+        defer gpa.free(buffer);
+        try self.unwind_info.write(self, buffer);
+        try self.base.file.?.pwriteAll(buffer, header.offset);
+    }
 }
 
-pub fn calcSymtabSize(self: *MachO) !void {
+fn calcSymtabSize(self: *MachO) !void {
     const tracy = trace(@src());
     defer tracy.end();
-    const gpa = self.base.comp.gpa;
 
-    var nlocals: u32 = 0;
-    var nstabs: u32 = 0;
-    var nexports: u32 = 0;
-    var nimports: u32 = 0;
-    var strsize: u32 = 0;
+    const gpa = self.base.comp.gpa;
 
     var files = std.ArrayList(File.Index).init(gpa);
     defer files.deinit();
@@ -2696,6 +2592,12 @@ pub fn calcSymtabSize(self: *MachO) !void {
     for (self.dylibs.items) |index| files.appendAssumeCapacity(index);
     if (self.internal_object) |index| files.appendAssumeCapacity(index);
 
+    var nlocals: u32 = 0;
+    var nstabs: u32 = 0;
+    var nexports: u32 = 0;
+    var nimports: u32 = 0;
+    var strsize: u32 = 0;
+
     for (files.items) |index| {
         const file = self.getFile(index).?;
         const ctx = switch (file) {
@@ -2705,7 +2607,7 @@ pub fn calcSymtabSize(self: *MachO) !void {
         ctx.istab = nstabs;
         ctx.iexport = nexports;
         ctx.iimport = nimports;
-        try file.calcSymtabSize(self);
+        ctx.stroff = strsize;
         nlocals += ctx.nlocals;
         nstabs += ctx.nstabs;
         nexports += ctx.nexports;
@@ -2723,6 +2625,8 @@ pub fn calcSymtabSize(self: *MachO) !void {
         ctx.iimport += nlocals + nstabs + nexports;
     }
 
+    try self.indsymtab.updateSize(self);
+
     {
         const cmd = &self.symtab_cmd;
         cmd.nsyms = nlocals + nstabs + nexports + nimports;
@@ -2740,60 +2644,6 @@ pub fn calcSymtabSize(self: *MachO) !void {
     }
 }
 
-pub fn writeSymtab(self: *MachO, off: u32) !u32 {
-    const tracy = trace(@src());
-    defer tracy.end();
-    const gpa = self.base.comp.gpa;
-    const cmd = &self.symtab_cmd;
-    cmd.symoff = off;
-
-    try self.symtab.resize(gpa, cmd.nsyms);
-    try self.strtab.ensureUnusedCapacity(gpa, cmd.strsize - 1);
-
-    if (self.getZigObject()) |zo| {
-        zo.writeSymtab(self, self);
-    }
-    for (self.objects.items) |index| {
-        try self.getFile(index).?.writeSymtab(self, self);
-    }
-    for (self.dylibs.items) |index| {
-        try self.getFile(index).?.writeSymtab(self, self);
-    }
-    if (self.getInternalObject()) |internal| {
-        internal.writeSymtab(self, self);
-    }
-
-    assert(self.strtab.items.len == cmd.strsize);
-
-    try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.symtab.items), cmd.symoff);
-
-    return off + cmd.nsyms * @sizeOf(macho.nlist_64);
-}
-
-fn writeIndsymtab(self: *MachO, off: u32) !u32 {
-    const gpa = self.base.comp.gpa;
-    const cmd = &self.dysymtab_cmd;
-    cmd.indirectsymoff = off;
-    cmd.nindirectsyms = self.indsymtab.nsyms(self);
-
-    const needed_size = cmd.nindirectsyms * @sizeOf(u32);
-    var buffer = try std.ArrayList(u8).initCapacity(gpa, needed_size);
-    defer buffer.deinit();
-    try self.indsymtab.write(self, buffer.writer());
-
-    try self.base.file.?.pwriteAll(buffer.items, cmd.indirectsymoff);
-    assert(buffer.items.len == needed_size);
-
-    return off + needed_size;
-}
-
-pub fn writeStrtab(self: *MachO, off: u32) !u32 {
-    const cmd = &self.symtab_cmd;
-    cmd.stroff = off;
-    try self.base.file.?.pwriteAll(self.strtab.items, cmd.stroff);
-    return off + cmd.strsize;
-}
-
 fn writeLoadCommands(self: *MachO) !struct { usize, usize, u64 } {
     const comp = self.base.comp;
     const gpa = comp.gpa;