Commit 4b07da7090

Jakub Konka <kubkon@jakubkonka.com>
2021-09-03 00:21:45
macho: remove all Zld codepaths
1 parent 5af13f3
Changed files (1)
src
src/link/MachO.zig
@@ -761,43 +761,34 @@ pub fn flush(self: *MachO, comp: *Compilation) !void {
         try self.addDataInCodeLC();
         try self.addCodeSignatureLC();
 
-        if (use_stage1) {
-            try self.parseTextBlocks();
-            try self.allocateTextSegment();
-            try self.allocateDataConstSegment();
-            try self.allocateDataSegment();
-            self.allocateLinkeditSegment();
-            try self.allocateTextBlocks();
-            try self.flushZld();
-        } else {
-            try self.parseTextBlocks();
-            try self.allocateGlobalSymbols();
-            {
-                log.debug("locals:", .{});
-                for (self.locals.items) |sym| {
-                    log.debug("  {s}: {}", .{ self.getString(sym.n_strx), sym });
-                }
-                log.debug("globals:", .{});
-                for (self.globals.items) |sym| {
-                    log.debug("  {s}: {}", .{ self.getString(sym.n_strx), sym });
-                }
-                log.debug("undefs:", .{});
-                for (self.undefs.items) |sym| {
-                    log.debug("  {s}: {}", .{ self.getString(sym.n_strx), sym });
-                }
-                log.debug("unresolved:", .{});
-                for (self.unresolved.keys()) |key| {
-                    log.debug("  {d} => {s}", .{ key, self.unresolved.get(key).? });
-                }
-                log.debug("resolved:", .{});
-                var it = self.symbol_resolver.iterator();
-                while (it.next()) |entry| {
-                    log.debug("  {s} => {}", .{ self.getString(entry.key_ptr.*), entry.value_ptr.* });
-                }
+        try self.parseTextBlocks();
+        try self.allocateGlobalSymbols();
+        {
+            log.debug("locals:", .{});
+            for (self.locals.items) |sym| {
+                log.debug("  {s}: {}", .{ self.getString(sym.n_strx), sym });
+            }
+            log.debug("globals:", .{});
+            for (self.globals.items) |sym| {
+                log.debug("  {s}: {}", .{ self.getString(sym.n_strx), sym });
+            }
+            log.debug("undefs:", .{});
+            for (self.undefs.items) |sym| {
+                log.debug("  {s}: {}", .{ self.getString(sym.n_strx), sym });
+            }
+            log.debug("unresolved:", .{});
+            for (self.unresolved.keys()) |key| {
+                log.debug("  {d} => {s}", .{ key, self.unresolved.get(key).? });
+            }
+            log.debug("resolved:", .{});
+            var it = self.symbol_resolver.iterator();
+            while (it.next()) |entry| {
+                log.debug("  {s} => {}", .{ self.getString(entry.key_ptr.*), entry.value_ptr.* });
             }
-            try self.writeAtoms();
-            try self.flushModule(comp);
         }
+        try self.writeAtoms();
+        try self.writeDices();
+        try self.flushModule(comp);
     }
 
     if (!self.base.options.disable_lld_caching) {
@@ -1638,248 +1629,6 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
     return res;
 }
 
-fn allocateTextSegment(self: *MachO) !void {
-    const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
-    const base_vmaddr = self.load_commands.items[self.pagezero_segment_cmd_index.?].Segment.inner.vmsize;
-    seg.inner.fileoff = 0;
-    seg.inner.vmaddr = base_vmaddr;
-
-    var sizeofcmds: u64 = 0;
-    for (self.load_commands.items) |lc| {
-        sizeofcmds += lc.cmdsize();
-    }
-
-    try self.allocateSegment(self.text_segment_cmd_index.?, @sizeOf(macho.mach_header_64) + sizeofcmds);
-
-    // Shift all sections to the back to minimize jump size between __TEXT and __DATA segments.
-    var min_alignment: u32 = 0;
-    for (seg.sections.items) |sect| {
-        const alignment = try math.powi(u32, 2, sect.@"align");
-        min_alignment = math.max(min_alignment, alignment);
-    }
-
-    assert(min_alignment > 0);
-    const last_sect_idx = seg.sections.items.len - 1;
-    const last_sect = seg.sections.items[last_sect_idx];
-    const shift: u32 = blk: {
-        const diff = seg.inner.filesize - last_sect.offset - last_sect.size;
-        const factor = @divTrunc(diff, min_alignment);
-        break :blk @intCast(u32, factor * min_alignment);
-    };
-
-    if (shift > 0) {
-        for (seg.sections.items) |*sect| {
-            sect.offset += shift;
-            sect.addr += shift;
-        }
-    }
-}
-
-fn allocateDataConstSegment(self: *MachO) !void {
-    const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
-    const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
-    seg.inner.fileoff = text_seg.inner.fileoff + text_seg.inner.filesize;
-    seg.inner.vmaddr = text_seg.inner.vmaddr + text_seg.inner.vmsize;
-    try self.allocateSegment(self.data_const_segment_cmd_index.?, 0);
-}
-
-fn allocateDataSegment(self: *MachO) !void {
-    const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
-    const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
-    seg.inner.fileoff = data_const_seg.inner.fileoff + data_const_seg.inner.filesize;
-    seg.inner.vmaddr = data_const_seg.inner.vmaddr + data_const_seg.inner.vmsize;
-    try self.allocateSegment(self.data_segment_cmd_index.?, 0);
-}
-
-fn allocateLinkeditSegment(self: *MachO) void {
-    const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
-    const data_seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
-    seg.inner.fileoff = data_seg.inner.fileoff + data_seg.inner.filesize;
-    seg.inner.vmaddr = data_seg.inner.vmaddr + data_seg.inner.vmsize;
-}
-
-fn allocateSegment(self: *MachO, index: u16, offset: u64) !void {
-    const seg = &self.load_commands.items[index].Segment;
-
-    // Allocate the sections according to their alignment at the beginning of the segment.
-    var start: u64 = offset;
-    for (seg.sections.items) |*sect, sect_id| {
-        const alignment = try math.powi(u32, 2, sect.@"align");
-        const start_aligned = mem.alignForwardGeneric(u64, start, alignment);
-        const end_aligned = mem.alignForwardGeneric(u64, start_aligned + sect.size, alignment);
-        const file_offset = @intCast(u32, seg.inner.fileoff + start_aligned);
-
-        blk: {
-            if (index == self.data_segment_cmd_index.?) {
-                if (self.bss_section_index) |idx| {
-                    if (sect_id == idx) {
-                        self.bss_file_offset = file_offset;
-                        break :blk;
-                    }
-                }
-                if (self.tlv_bss_section_index) |idx| {
-                    if (sect_id == idx) {
-                        self.tlv_bss_file_offset = file_offset;
-                        break :blk;
-                    }
-                }
-            }
-            sect.offset = @intCast(u32, seg.inner.fileoff + start_aligned);
-        }
-
-        sect.addr = seg.inner.vmaddr + start_aligned;
-        start = end_aligned;
-    }
-
-    const seg_size_aligned = mem.alignForwardGeneric(u64, start, self.page_size);
-    seg.inner.filesize = seg_size_aligned;
-    seg.inner.vmsize = seg_size_aligned;
-}
-
-fn allocateTextBlocks(self: *MachO) !void {
-    var it = self.blocks.iterator();
-    while (it.next()) |entry| {
-        const match = entry.key_ptr.*;
-        var block: *TextBlock = entry.value_ptr.*;
-
-        // Find the first block
-        while (block.prev) |prev| {
-            block = prev;
-        }
-
-        const seg = self.load_commands.items[match.seg].Segment;
-        const sect = seg.sections.items[match.sect];
-
-        var base_addr: u64 = sect.addr;
-        const n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
-
-        log.debug("  within section {s},{s}", .{ commands.segmentName(sect), commands.sectionName(sect) });
-        log.debug("    {}", .{sect});
-
-        while (true) {
-            const block_alignment = try math.powi(u32, 2, block.alignment);
-            base_addr = mem.alignForwardGeneric(u64, base_addr, block_alignment);
-
-            const sym = &self.locals.items[block.local_sym_index];
-            sym.n_value = base_addr;
-            sym.n_sect = n_sect;
-
-            log.debug("    {s}: start=0x{x}, end=0x{x}, size={}, align={}", .{
-                self.getString(sym.n_strx),
-                base_addr,
-                base_addr + block.size,
-                block.size,
-                block.alignment,
-            });
-
-            // Update each alias (if any)
-            for (block.aliases.items) |index| {
-                const alias_sym = &self.locals.items[index];
-                alias_sym.n_value = base_addr;
-                alias_sym.n_sect = n_sect;
-            }
-
-            // Update each symbol contained within the TextBlock
-            for (block.contained.items) |sym_at_off| {
-                const contained_sym = &self.locals.items[sym_at_off.local_sym_index];
-                contained_sym.n_value = base_addr + sym_at_off.offset;
-                contained_sym.n_sect = n_sect;
-            }
-
-            base_addr += block.size;
-
-            if (block.next) |next| {
-                block = next;
-            } else break;
-        }
-    }
-
-    // Update globals
-    {
-        var sym_it = self.symbol_resolver.valueIterator();
-        while (sym_it.next()) |resolv| {
-            if (resolv.where != .global) continue;
-
-            assert(resolv.local_sym_index != 0);
-            const local_sym = self.locals.items[resolv.local_sym_index];
-            const sym = &self.globals.items[resolv.where_index];
-            sym.n_value = local_sym.n_value;
-            sym.n_sect = local_sym.n_sect;
-        }
-    }
-}
-
-fn writeTextBlocks(self: *MachO) !void {
-    var it = self.blocks.iterator();
-    while (it.next()) |entry| {
-        const match = entry.key_ptr.*;
-        var block: *TextBlock = entry.value_ptr.*;
-
-        while (block.prev) |prev| {
-            block = prev;
-        }
-
-        const seg = self.load_commands.items[match.seg].Segment;
-        const sect = seg.sections.items[match.sect];
-        const sect_type = commands.sectionType(sect);
-
-        log.debug("  for section {s},{s}", .{ commands.segmentName(sect), commands.sectionName(sect) });
-        log.debug("    {}", .{sect});
-
-        var code = try self.base.allocator.alloc(u8, sect.size);
-        defer self.base.allocator.free(code);
-
-        const file_offset: u64 = blk: {
-            if (self.data_segment_cmd_index.? == match.seg) {
-                if (self.bss_section_index) |idx| {
-                    if (idx == match.sect) break :blk self.bss_file_offset.?;
-                }
-                if (self.tlv_bss_section_index) |idx| {
-                    if (idx == match.sect) break :blk self.tlv_bss_file_offset.?;
-                }
-            }
-            break :blk sect.offset;
-        };
-
-        if (sect_type == macho.S_ZEROFILL or sect_type == macho.S_THREAD_LOCAL_ZEROFILL) {
-            mem.set(u8, code, 0);
-        } else {
-            var base_off: u64 = 0;
-
-            while (true) {
-                const block_alignment = try math.powi(u32, 2, block.alignment);
-                const aligned_base_off = mem.alignForwardGeneric(u64, base_off, block_alignment);
-
-                const sym = self.locals.items[block.local_sym_index];
-                log.debug("    {s}: start=0x{x}, end=0x{x}, size={}, align={}", .{
-                    self.getString(sym.n_strx),
-                    aligned_base_off,
-                    aligned_base_off + block.size,
-                    block.size,
-                    block.alignment,
-                });
-
-                try block.resolveRelocs(self);
-                mem.copy(u8, code[aligned_base_off..][0..block.size], block.code.items);
-
-                // TODO NOP for machine code instead of just zeroing out
-                const padding_len = aligned_base_off - base_off;
-                mem.set(u8, code[base_off..][0..padding_len], 0);
-
-                base_off = aligned_base_off + block.size;
-
-                if (block.next) |next| {
-                    block = next;
-                } else break;
-            }
-
-            mem.set(u8, code[base_off..], 0);
-        }
-
-        try self.base.file.?.pwriteAll(code, file_offset);
-    }
-}
-
 pub fn createEmptyAtom(self: *MachO, local_sym_index: u32, size: u64, alignment: u32) !*TextBlock {
     const code = try self.base.allocator.alloc(u8, size);
     defer self.base.allocator.free(code);
@@ -2920,17 +2669,23 @@ fn parseTextBlocks(self: *MachO) !void {
 }
 
 fn addDataInCodeLC(self: *MachO) !void {
-    if (self.data_in_code_cmd_index == null) {
-        self.data_in_code_cmd_index = @intCast(u16, self.load_commands.items.len);
-        try self.load_commands.append(self.base.allocator, .{
-            .LinkeditData = .{
-                .cmd = macho.LC_DATA_IN_CODE,
-                .cmdsize = @sizeOf(macho.linkedit_data_command),
-                .dataoff = 0,
-                .datasize = 0,
-            },
-        });
-    }
+    if (self.data_in_code_cmd_index != null) return;
+    self.data_in_code_cmd_index = @intCast(u16, self.load_commands.items.len);
+    try self.load_commands.append(self.base.allocator, .{
+        .LinkeditData = .{
+            .cmd = macho.LC_DATA_IN_CODE,
+            .cmdsize = @sizeOf(macho.linkedit_data_command),
+            .dataoff = 0,
+            .datasize = 0,
+        },
+    });
+    const dice_cmd = &self.load_commands.items[self.data_in_code_cmd_index.?].LinkeditData;
+    const needed_size = 10 * @sizeOf(macho.data_in_code_entry);
+    const dataoff = self.findFreeSpaceLinkedit(needed_size, @alignOf(macho.data_in_code_entry), null);
+    log.debug("found data-in-code free space 0x{x} to 0x{x}", .{ dataoff, dataoff + needed_size });
+    dice_cmd.dataoff = @intCast(u32, dataoff);
+    dice_cmd.datasize = needed_size;
+    self.load_commands_dirty = true;
 }
 
 fn addCodeSignatureLC(self: *MachO) !void {
@@ -2982,42 +2737,6 @@ fn addLoadDylibLCs(self: *MachO) !void {
     }
 }
 
-fn flushZld(self: *MachO) !void {
-    try self.writeTextBlocks();
-    try self.setEntryPoint();
-    try self.writeRebaseInfoTableZld();
-    try self.writeBindInfoTableZld();
-    try self.writeLazyBindInfoTableZld();
-    try self.writeExportInfoZld();
-    try self.writeDices();
-
-    {
-        const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
-        const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
-        symtab.symoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
-    }
-
-    try self.writeSymbolTable();
-    try self.writeStringTableZld();
-
-    {
-        // Seal __LINKEDIT size
-        const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
-        seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size);
-    }
-
-    if (self.requires_adhoc_codesig) {
-        try self.writeCodeSignaturePadding();
-    }
-
-    try self.writeLoadCommands();
-    try self.writeHeader();
-
-    if (self.requires_adhoc_codesig) {
-        try self.writeCodeSignature();
-    }
-}
-
 fn setEntryPoint(self: *MachO) !void {
     if (self.base.options.output_mode != .Exe) return;
 
@@ -3039,343 +2758,6 @@ fn setEntryPoint(self: *MachO) !void {
     self.load_commands_dirty = true;
 }
 
-fn writeRebaseInfoTableZld(self: *MachO) !void {
-    var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);
-    defer pointers.deinit();
-
-    {
-        var it = self.blocks.iterator();
-        while (it.next()) |entry| {
-            const match = entry.key_ptr.*;
-            var block: *TextBlock = entry.value_ptr.*;
-
-            if (match.seg == self.text_segment_cmd_index.?) continue; // __TEXT is non-writable
-
-            const seg = self.load_commands.items[match.seg].Segment;
-
-            while (true) {
-                const sym = self.locals.items[block.local_sym_index];
-                const base_offset = sym.n_value - seg.inner.vmaddr;
-
-                for (block.rebases.items) |offset| {
-                    try pointers.append(.{
-                        .offset = base_offset + offset,
-                        .segment_id = match.seg,
-                    });
-                }
-
-                if (block.prev) |prev| {
-                    block = prev;
-                } else break;
-            }
-        }
-    }
-
-    const size = try bind.rebaseInfoSize(pointers.items);
-    var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
-    defer self.base.allocator.free(buffer);
-
-    var stream = std.io.fixedBufferStream(buffer);
-    try bind.writeRebaseInfo(pointers.items, stream.writer());
-
-    const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
-    const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
-    dyld_info.rebase_off = @intCast(u32, seg.inner.fileoff);
-    dyld_info.rebase_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @sizeOf(u64)));
-    seg.inner.filesize += dyld_info.rebase_size;
-
-    log.debug("writing rebase info from 0x{x} to 0x{x}", .{ dyld_info.rebase_off, dyld_info.rebase_off + dyld_info.rebase_size });
-
-    try self.base.file.?.pwriteAll(buffer, dyld_info.rebase_off);
-}
-
-fn writeBindInfoTableZld(self: *MachO) !void {
-    var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);
-    defer pointers.deinit();
-
-    {
-        var it = self.blocks.iterator();
-        while (it.next()) |entry| {
-            const match = entry.key_ptr.*;
-            var block: *TextBlock = entry.value_ptr.*;
-
-            if (match.seg == self.text_segment_cmd_index.?) continue; // __TEXT is non-writable
-
-            const seg = self.load_commands.items[match.seg].Segment;
-
-            while (true) {
-                const sym = self.locals.items[block.local_sym_index];
-                const base_offset = sym.n_value - seg.inner.vmaddr;
-
-                for (block.bindings.items) |binding| {
-                    const bind_sym = self.undefs.items[binding.local_sym_index];
-                    try pointers.append(.{
-                        .offset = binding.offset + base_offset,
-                        .segment_id = match.seg,
-                        .dylib_ordinal = @divExact(bind_sym.n_desc, macho.N_SYMBOL_RESOLVER),
-                        .name = self.getString(bind_sym.n_strx),
-                    });
-                }
-
-                if (block.prev) |prev| {
-                    block = prev;
-                } else break;
-            }
-        }
-    }
-
-    const size = try bind.bindInfoSize(pointers.items);
-    var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
-    defer self.base.allocator.free(buffer);
-
-    var stream = std.io.fixedBufferStream(buffer);
-    try bind.writeBindInfo(pointers.items, stream.writer());
-
-    const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
-    const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
-    dyld_info.bind_off = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
-    dyld_info.bind_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64)));
-    seg.inner.filesize += dyld_info.bind_size;
-
-    log.debug("writing binding info from 0x{x} to 0x{x}", .{ dyld_info.bind_off, dyld_info.bind_off + dyld_info.bind_size });
-
-    try self.base.file.?.pwriteAll(buffer, dyld_info.bind_off);
-}
-
-fn writeLazyBindInfoTableZld(self: *MachO) !void {
-    var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);
-    defer pointers.deinit();
-
-    if (self.la_symbol_ptr_section_index) |sect| blk: {
-        var atom = self.blocks.get(.{
-            .seg = self.data_segment_cmd_index.?,
-            .sect = sect,
-        }) orelse break :blk;
-        const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
-
-        while (true) {
-            const sym = self.locals.items[atom.local_sym_index];
-            const base_offset = sym.n_value - seg.inner.vmaddr;
-
-            for (atom.lazy_bindings.items) |binding| {
-                const bind_sym = self.undefs.items[binding.local_sym_index];
-                try pointers.append(.{
-                    .offset = binding.offset + base_offset,
-                    .segment_id = self.data_segment_cmd_index.?,
-                    .dylib_ordinal = @divExact(bind_sym.n_desc, macho.N_SYMBOL_RESOLVER),
-                    .name = self.getString(bind_sym.n_strx),
-                });
-            }
-            if (atom.prev) |prev| {
-                atom = prev;
-            } else break;
-        }
-    }
-
-    const size = try bind.lazyBindInfoSize(pointers.items);
-    var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
-    defer self.base.allocator.free(buffer);
-
-    var stream = std.io.fixedBufferStream(buffer);
-    try bind.writeLazyBindInfo(pointers.items, stream.writer());
-
-    const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
-    const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
-    dyld_info.lazy_bind_off = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
-    dyld_info.lazy_bind_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64)));
-    seg.inner.filesize += dyld_info.lazy_bind_size;
-
-    log.debug("writing lazy binding info from 0x{x} to 0x{x}", .{ dyld_info.lazy_bind_off, dyld_info.lazy_bind_off + dyld_info.lazy_bind_size });
-
-    try self.base.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off);
-    try self.populateLazyBindOffsetsInStubHelper(buffer);
-}
-
-fn writeExportInfoZld(self: *MachO) !void {
-    var trie: Trie = .{};
-    defer trie.deinit(self.base.allocator);
-
-    const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
-    const base_address = text_segment.inner.vmaddr;
-
-    // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.
-    log.debug("writing export trie", .{});
-
-    for (self.globals.items) |sym| {
-        const sym_name = self.getString(sym.n_strx);
-        log.debug("  | putting '{s}' defined at 0x{x}", .{ sym_name, sym.n_value });
-
-        try trie.put(self.base.allocator, .{
-            .name = sym_name,
-            .vmaddr_offset = sym.n_value - base_address,
-            .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,
-        });
-    }
-
-    try trie.finalize(self.base.allocator);
-
-    var buffer = try self.base.allocator.alloc(u8, @intCast(usize, trie.size));
-    defer self.base.allocator.free(buffer);
-
-    var stream = std.io.fixedBufferStream(buffer);
-    const nwritten = try trie.write(stream.writer());
-    assert(nwritten == trie.size);
-
-    const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
-    const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
-    dyld_info.export_off = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
-    dyld_info.export_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64)));
-    seg.inner.filesize += dyld_info.export_size;
-
-    log.debug("writing export info from 0x{x} to 0x{x}", .{ dyld_info.export_off, dyld_info.export_off + dyld_info.export_size });
-
-    try self.base.file.?.pwriteAll(buffer, dyld_info.export_off);
-}
-
-fn writeSymbolTable(self: *MachO) !void {
-    const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
-    const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
-
-    var locals = std.ArrayList(macho.nlist_64).init(self.base.allocator);
-    defer locals.deinit();
-    try locals.appendSlice(self.locals.items);
-
-    if (self.has_stabs) {
-        for (self.objects.items) |object| {
-            if (object.debug_info == null) continue;
-
-            // Open scope
-            try locals.ensureUnusedCapacity(3);
-            locals.appendAssumeCapacity(.{
-                .n_strx = try self.makeString(object.tu_comp_dir.?),
-                .n_type = macho.N_SO,
-                .n_sect = 0,
-                .n_desc = 0,
-                .n_value = 0,
-            });
-            locals.appendAssumeCapacity(.{
-                .n_strx = try self.makeString(object.tu_name.?),
-                .n_type = macho.N_SO,
-                .n_sect = 0,
-                .n_desc = 0,
-                .n_value = 0,
-            });
-            locals.appendAssumeCapacity(.{
-                .n_strx = try self.makeString(object.name),
-                .n_type = macho.N_OSO,
-                .n_sect = 0,
-                .n_desc = 1,
-                .n_value = object.mtime orelse 0,
-            });
-
-            for (object.text_blocks.items) |block| {
-                if (block.stab) |stab| {
-                    const nlists = try stab.asNlists(block.local_sym_index, self);
-                    defer self.base.allocator.free(nlists);
-                    try locals.appendSlice(nlists);
-                } else {
-                    for (block.contained.items) |sym_at_off| {
-                        const stab = sym_at_off.stab orelse continue;
-                        const nlists = try stab.asNlists(sym_at_off.local_sym_index, self);
-                        defer self.base.allocator.free(nlists);
-                        try locals.appendSlice(nlists);
-                    }
-                }
-            }
-
-            // Close scope
-            try locals.append(.{
-                .n_strx = 0,
-                .n_type = macho.N_SO,
-                .n_sect = 0,
-                .n_desc = 0,
-                .n_value = 0,
-            });
-        }
-    }
-
-    const nlocals = locals.items.len;
-    const nexports = self.globals.items.len;
-    const nundefs = self.undefs.items.len;
-
-    const locals_off = symtab.symoff;
-    const locals_size = nlocals * @sizeOf(macho.nlist_64);
-    log.debug("writing local symbols from 0x{x} to 0x{x}", .{ locals_off, locals_size + locals_off });
-    try self.base.file.?.pwriteAll(mem.sliceAsBytes(locals.items), locals_off);
-
-    const exports_off = locals_off + locals_size;
-    const exports_size = nexports * @sizeOf(macho.nlist_64);
-    log.debug("writing exported symbols from 0x{x} to 0x{x}", .{ exports_off, exports_size + exports_off });
-    try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.globals.items), exports_off);
-
-    const undefs_off = exports_off + exports_size;
-    const undefs_size = nundefs * @sizeOf(macho.nlist_64);
-    log.debug("writing undefined symbols from 0x{x} to 0x{x}", .{ undefs_off, undefs_size + undefs_off });
-    try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.undefs.items), undefs_off);
-
-    symtab.nsyms = @intCast(u32, nlocals + nexports + nundefs);
-    seg.inner.filesize += locals_size + exports_size + undefs_size;
-
-    // Update dynamic symbol table.
-    const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
-    dysymtab.nlocalsym = @intCast(u32, nlocals);
-    dysymtab.iextdefsym = dysymtab.nlocalsym;
-    dysymtab.nextdefsym = @intCast(u32, nexports);
-    dysymtab.iundefsym = dysymtab.nlocalsym + dysymtab.nextdefsym;
-    dysymtab.nundefsym = @intCast(u32, nundefs);
-
-    const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
-    const stubs = &text_segment.sections.items[self.stubs_section_index.?];
-    const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
-    const got = &data_const_segment.sections.items[self.got_section_index.?];
-    const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
-    const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?];
-
-    const nstubs = @intCast(u32, self.stubs_map.keys().len);
-    const ngot_entries = @intCast(u32, self.got_entries_map.keys().len);
-
-    dysymtab.indirectsymoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
-    dysymtab.nindirectsyms = nstubs * 2 + ngot_entries;
-
-    const needed_size = dysymtab.nindirectsyms * @sizeOf(u32);
-    seg.inner.filesize += needed_size;
-
-    log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{
-        dysymtab.indirectsymoff,
-        dysymtab.indirectsymoff + needed_size,
-    });
-
-    var buf = try self.base.allocator.alloc(u8, needed_size);
-    defer self.base.allocator.free(buf);
-
-    var stream = std.io.fixedBufferStream(buf);
-    var writer = stream.writer();
-
-    stubs.reserved1 = 0;
-    for (self.stubs_map.keys()) |key| {
-        try writer.writeIntLittle(u32, dysymtab.iundefsym + key);
-    }
-
-    got.reserved1 = nstubs;
-    for (self.got_entries_map.keys()) |key| {
-        switch (key.where) {
-            .undef => {
-                try writer.writeIntLittle(u32, dysymtab.iundefsym + key.where_index);
-            },
-            .local => {
-                try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);
-            },
-        }
-    }
-
-    la_symbol_ptr.reserved1 = got.reserved1 + ngot_entries;
-    for (self.stubs_map.keys()) |key| {
-        try writer.writeIntLittle(u32, dysymtab.iundefsym + key);
-    }
-
-    try self.base.file.?.pwriteAll(buf, dysymtab.indirectsymoff);
-}
-
 pub fn deinit(self: *MachO) void {
     if (build_options.have_llvm) {
         if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator);
@@ -3812,12 +3194,6 @@ fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64
             try ds.writeLocalSymbol(decl.link.macho.local_sym_index);
     }
 
-    // // Resolve relocations
-    // try decl.link.macho.resolveRelocs(self);
-    // // TODO this requires further investigation: should we dispose of resolved relocs, or keep them
-    // // so that we can reapply them when moving/growing sections?
-    // decl.link.macho.relocs.clearAndFree(self.base.allocator);
-
     return symbol;
 }
 
@@ -5003,9 +4379,8 @@ fn writeIndirectSymbolTable(self: *MachO) !void {
 fn writeDices(self: *MachO) !void {
     if (!self.has_dices) return;
 
-    const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
-    const dice_cmd = &self.load_commands.items[self.data_in_code_cmd_index.?].LinkeditData;
-    const fileoff = seg.inner.fileoff + seg.inner.filesize;
+    const tracy = trace(@src());
+    defer tracy.end();
 
     var buf = std.ArrayList(u8).init(self.base.allocator);
     defer buf.deinit();
@@ -5043,15 +4418,26 @@ fn writeDices(self: *MachO) !void {
         } else break;
     }
 
-    const datasize = @intCast(u32, buf.items.len);
-
-    dice_cmd.dataoff = @intCast(u32, fileoff);
-    dice_cmd.datasize = datasize;
-    seg.inner.filesize += datasize;
+    const dice_cmd = &self.load_commands.items[self.data_in_code_cmd_index.?].LinkeditData;
+    const allocated_size = self.allocatedSizeLinkedit(dice_cmd.dataoff);
+    const needed_size = @intCast(u32, buf.items.len);
 
-    log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ fileoff, fileoff + datasize });
+    if (needed_size > allocated_size) {
+        dice_cmd.datasize = 0;
+        dice_cmd.dataoff = @intCast(u32, self.findFreeSpaceLinkedit(
+            needed_size,
+            @alignOf(macho.data_in_code_entry),
+            dice_cmd.dataoff,
+        ));
+    }
+    dice_cmd.datasize = needed_size;
+    log.debug("writing data-in-code from 0x{x} to 0x{x}", .{
+        dice_cmd.dataoff,
+        dice_cmd.dataoff + dice_cmd.datasize,
+    });
 
-    try self.base.file.?.pwriteAll(buf.items, fileoff);
+    try self.base.file.?.pwriteAll(buf.items, dice_cmd.dataoff);
+    self.load_commands_dirty = true;
 }
 
 fn writeCodeSignaturePadding(self: *MachO) !void {
@@ -5130,7 +4516,7 @@ fn writeExportInfo(self: *MachO) !void {
 
     for (self.globals.items) |sym| {
         const sym_name = self.getString(sym.n_strx);
-        log.debug("  | putting '{s}' defined at 0x{x}", .{ sym_name, sym.n_value });
+        log.debug("  (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value });
 
         try trie.put(self.base.allocator, .{
             .name = sym_name,
@@ -5453,30 +4839,16 @@ fn writeStringTable(self: *MachO) !void {
         self.strtab_needs_relocation = false;
     }
     symtab.strsize = @intCast(u32, needed_size);
-    log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize });
+    log.debug("writing string table from 0x{x} to 0x{x}", .{
+        symtab.stroff,
+        symtab.stroff + symtab.strsize,
+    });
 
     try self.base.file.?.pwriteAll(self.strtab.items, symtab.stroff);
     self.load_commands_dirty = true;
     self.strtab_dirty = false;
 }
 
-fn writeStringTableZld(self: *MachO) !void {
-    const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
-    const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
-    symtab.stroff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
-    symtab.strsize = @intCast(u32, mem.alignForwardGeneric(u64, self.strtab.items.len, @alignOf(u64)));
-    seg.inner.filesize += symtab.strsize;
-
-    log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize });
-
-    try self.base.file.?.pwriteAll(self.strtab.items, symtab.stroff);
-
-    if (symtab.strsize > self.strtab.items.len) {
-        // This is potentially the last section, so we need to pad it out.
-        try self.base.file.?.pwriteAll(&[_]u8{0}, seg.inner.fileoff + seg.inner.filesize - 1);
-    }
-}
-
 fn updateLinkeditSegmentSizes(self: *MachO) !void {
     if (!self.load_commands_dirty) return;