Commit ce207caa24

Jakub Konka <kubkon@jakubkonka.com>
2024-02-08 12:15:46
macho: in relocatable mode, macho emit __DWARF directly
1 parent 37033a9
Changed files (3)
src/link/MachO/ZigObject.zig
@@ -471,7 +471,13 @@ pub fn flushModule(self: *ZigObject, macho_file: *MachO) !void {
                 self.debug_strtab_dirty = false;
             }
         } else {
-            // TODO: relocatable
+            const sect_index = macho_file.debug_str_sect_index.?;
+            if (self.debug_strtab_dirty or dw.strtab.buffer.items.len != macho_file.sections.items(.header)[sect_index].size) {
+                const needed_size = @as(u32, @intCast(dw.strtab.buffer.items.len));
+                try macho_file.growSection(sect_index, needed_size);
+                try macho_file.base.file.?.pwriteAll(dw.strtab.buffer.items, macho_file.sections.items(.header)[sect_index].offset);
+                self.debug_strtab_dirty = false;
+            }
         }
     }
 
src/link/Dwarf.zig
@@ -1282,10 +1282,17 @@ pub fn commitDeclState(
                                 try pwriteDbgLineNops(elf_file.base.file.?, file_pos, 0, &[0]u8{}, src_fn.len);
                             },
                             .macho => {
-                                const d_sym = self.bin_file.cast(File.MachO).?.getDebugSymbols().?;
-                                const debug_line_sect = d_sym.getSectionPtr(d_sym.debug_line_section_index.?);
-                                const file_pos = debug_line_sect.offset + src_fn.off;
-                                try pwriteDbgLineNops(d_sym.file, file_pos, 0, &[0]u8{}, src_fn.len);
+                                const macho_file = self.bin_file.cast(File.MachO).?;
+                                if (macho_file.base.isRelocatable()) {
+                                    const debug_line_sect = &macho_file.sections.items(.header)[macho_file.debug_line_sect_index.?];
+                                    const file_pos = debug_line_sect.offset + src_fn.off;
+                                    try pwriteDbgLineNops(macho_file.base.file.?, file_pos, 0, &[0]u8{}, src_fn.len);
+                                } else {
+                                    const d_sym = macho_file.getDebugSymbols().?;
+                                    const debug_line_sect = d_sym.getSectionPtr(d_sym.debug_line_section_index.?);
+                                    const file_pos = debug_line_sect.offset + src_fn.off;
+                                    try pwriteDbgLineNops(d_sym.file, file_pos, 0, &[0]u8{}, src_fn.len);
+                                }
                             },
                             .wasm => {
                                 const wasm_file = self.bin_file.cast(File.Wasm).?;
@@ -1352,18 +1359,32 @@ pub fn commitDeclState(
 
                 .macho => {
                     const macho_file = self.bin_file.cast(File.MachO).?;
-                    const d_sym = macho_file.getDebugSymbols().?;
-                    const sect_index = d_sym.debug_line_section_index.?;
-                    try d_sym.growSection(sect_index, needed_size, true, macho_file);
-                    const sect = d_sym.getSection(sect_index);
-                    const file_pos = sect.offset + src_fn.off;
-                    try pwriteDbgLineNops(
-                        d_sym.file,
-                        file_pos,
-                        prev_padding_size,
-                        dbg_line_buffer.items,
-                        next_padding_size,
-                    );
+                    if (macho_file.base.isRelocatable()) {
+                        const sect_index = macho_file.debug_line_sect_index.?;
+                        try macho_file.growSection(sect_index, needed_size);
+                        const sect = macho_file.sections.items(.header)[sect_index];
+                        const file_pos = sect.offset + src_fn.off;
+                        try pwriteDbgLineNops(
+                            macho_file.base.file.?,
+                            file_pos,
+                            prev_padding_size,
+                            dbg_line_buffer.items,
+                            next_padding_size,
+                        );
+                    } else {
+                        const d_sym = macho_file.getDebugSymbols().?;
+                        const sect_index = d_sym.debug_line_section_index.?;
+                        try d_sym.growSection(sect_index, needed_size, true, macho_file);
+                        const sect = d_sym.getSection(sect_index);
+                        const file_pos = sect.offset + src_fn.off;
+                        try pwriteDbgLineNops(
+                            d_sym.file,
+                            file_pos,
+                            prev_padding_size,
+                            dbg_line_buffer.items,
+                            next_padding_size,
+                        );
+                    }
                 },
 
                 .wasm => {
@@ -1460,16 +1481,21 @@ pub fn commitDeclState(
     while (decl_state.exprloc_relocs.popOrNull()) |reloc| {
         switch (self.bin_file.tag) {
             .macho => {
-                const d_sym = self.bin_file.cast(File.MachO).?.getDebugSymbols().?;
-                try d_sym.relocs.append(d_sym.allocator, .{
-                    .type = switch (reloc.type) {
-                        .direct_load => .direct_load,
-                        .got_load => .got_load,
-                    },
-                    .target = reloc.target,
-                    .offset = reloc.offset + self.getAtom(.di_atom, di_atom_index).off,
-                    .addend = 0,
-                });
+                const macho_file = self.bin_file.cast(File.MachO).?;
+                if (macho_file.base.isRelocatable()) {
+                    // TODO
+                } else {
+                    const d_sym = macho_file.getDebugSymbols().?;
+                    try d_sym.relocs.append(d_sym.allocator, .{
+                        .type = switch (reloc.type) {
+                            .direct_load => .direct_load,
+                            .got_load => .got_load,
+                        },
+                        .target = reloc.target,
+                        .offset = reloc.offset + self.getAtom(.di_atom, di_atom_index).off,
+                        .addend = 0,
+                    });
+                }
             },
             .elf => {}, // TODO
             else => unreachable,
@@ -1512,10 +1538,17 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, atom_index: Atom.Index, len: u32)
                         try pwriteDbgInfoNops(elf_file.base.file.?, file_pos, 0, &[0]u8{}, atom.len, false);
                     },
                     .macho => {
-                        const d_sym = self.bin_file.cast(File.MachO).?.getDebugSymbols().?;
-                        const debug_info_sect = d_sym.getSectionPtr(d_sym.debug_info_section_index.?);
-                        const file_pos = debug_info_sect.offset + atom.off;
-                        try pwriteDbgInfoNops(d_sym.file, file_pos, 0, &[0]u8{}, atom.len, false);
+                        const macho_file = self.bin_file.cast(File.MachO).?;
+                        if (macho_file.base.isRelocatable()) {
+                            const debug_info_sect = macho_file.sections.items(.header)[macho_file.debug_info_sect_index.?];
+                            const file_pos = debug_info_sect.offset + atom.off;
+                            try pwriteDbgInfoNops(macho_file.base.file.?, file_pos, 0, &[0]u8{}, atom.len, false);
+                        } else {
+                            const d_sym = macho_file.getDebugSymbols().?;
+                            const debug_info_sect = d_sym.getSectionPtr(d_sym.debug_info_section_index.?);
+                            const file_pos = debug_info_sect.offset + atom.off;
+                            try pwriteDbgInfoNops(d_sym.file, file_pos, 0, &[0]u8{}, atom.len, false);
+                        }
                     },
                     .wasm => {
                         const wasm_file = self.bin_file.cast(File.Wasm).?;
@@ -1599,19 +1632,34 @@ fn writeDeclDebugInfo(self: *Dwarf, atom_index: Atom.Index, dbg_info_buf: []cons
 
         .macho => {
             const macho_file = self.bin_file.cast(File.MachO).?;
-            const d_sym = macho_file.getDebugSymbols().?;
-            const sect_index = d_sym.debug_info_section_index.?;
-            try d_sym.growSection(sect_index, needed_size, true, macho_file);
-            const sect = d_sym.getSection(sect_index);
-            const file_pos = sect.offset + atom.off;
-            try pwriteDbgInfoNops(
-                d_sym.file,
-                file_pos,
-                prev_padding_size,
-                dbg_info_buf,
-                next_padding_size,
-                trailing_zero,
-            );
+            if (macho_file.base.isRelocatable()) {
+                const sect_index = macho_file.debug_info_sect_index.?;
+                try macho_file.growSection(sect_index, needed_size);
+                const sect = macho_file.sections.items(.header)[sect_index];
+                const file_pos = sect.offset + atom.off;
+                try pwriteDbgInfoNops(
+                    macho_file.base.file.?,
+                    file_pos,
+                    prev_padding_size,
+                    dbg_info_buf,
+                    next_padding_size,
+                    trailing_zero,
+                );
+            } else {
+                const d_sym = macho_file.getDebugSymbols().?;
+                const sect_index = d_sym.debug_info_section_index.?;
+                try d_sym.growSection(sect_index, needed_size, true, macho_file);
+                const sect = d_sym.getSection(sect_index);
+                const file_pos = sect.offset + atom.off;
+                try pwriteDbgInfoNops(
+                    d_sym.file,
+                    file_pos,
+                    prev_padding_size,
+                    dbg_info_buf,
+                    next_padding_size,
+                    trailing_zero,
+                );
+            }
         },
 
         .wasm => {
@@ -1672,10 +1720,17 @@ pub fn updateDeclLineNumber(self: *Dwarf, mod: *Module, decl_index: InternPool.D
             try elf_file.base.file.?.pwriteAll(&data, file_pos);
         },
         .macho => {
-            const d_sym = self.bin_file.cast(File.MachO).?.getDebugSymbols().?;
-            const sect = d_sym.getSection(d_sym.debug_line_section_index.?);
-            const file_pos = sect.offset + atom.off + self.getRelocDbgLineOff();
-            try d_sym.file.pwriteAll(&data, file_pos);
+            const macho_file = self.bin_file.cast(File.MachO).?;
+            if (macho_file.base.isRelocatable()) {
+                const sect = macho_file.sections.items(.header)[macho_file.debug_line_sect_index.?];
+                const file_pos = sect.offset + atom.off + self.getRelocDbgLineOff();
+                try macho_file.base.file.?.pwriteAll(&data, file_pos);
+            } else {
+                const d_sym = macho_file.getDebugSymbols().?;
+                const sect = d_sym.getSection(d_sym.debug_line_section_index.?);
+                const file_pos = sect.offset + atom.off + self.getRelocDbgLineOff();
+                try d_sym.file.pwriteAll(&data, file_pos);
+            }
         },
         .wasm => {
             const wasm_file = self.bin_file.cast(File.Wasm).?;
@@ -1880,12 +1935,20 @@ pub fn writeDbgAbbrev(self: *Dwarf) !void {
         },
         .macho => {
             const macho_file = self.bin_file.cast(File.MachO).?;
-            const d_sym = macho_file.getDebugSymbols().?;
-            const sect_index = d_sym.debug_abbrev_section_index.?;
-            try d_sym.growSection(sect_index, needed_size, false, macho_file);
-            const sect = d_sym.getSection(sect_index);
-            const file_pos = sect.offset + abbrev_offset;
-            try d_sym.file.pwriteAll(&abbrev_buf, file_pos);
+            if (macho_file.base.isRelocatable()) {
+                const sect_index = macho_file.debug_abbrev_sect_index.?;
+                try macho_file.growSection(sect_index, needed_size);
+                const sect = macho_file.sections.items(.header)[sect_index];
+                const file_pos = sect.offset + abbrev_offset;
+                try macho_file.base.file.?.pwriteAll(&abbrev_buf, file_pos);
+            } else {
+                const d_sym = macho_file.getDebugSymbols().?;
+                const sect_index = d_sym.debug_abbrev_section_index.?;
+                try d_sym.growSection(sect_index, needed_size, false, macho_file);
+                const sect = d_sym.getSection(sect_index);
+                const file_pos = sect.offset + abbrev_offset;
+                try d_sym.file.pwriteAll(&abbrev_buf, file_pos);
+            }
         },
         .wasm => {
             const wasm_file = self.bin_file.cast(File.Wasm).?;
@@ -1970,10 +2033,17 @@ pub fn writeDbgInfoHeader(self: *Dwarf, zcu: *Module, low_pc: u64, high_pc: u64)
             try pwriteDbgInfoNops(elf_file.base.file.?, file_pos, 0, di_buf.items, jmp_amt, false);
         },
         .macho => {
-            const d_sym = self.bin_file.cast(File.MachO).?.getDebugSymbols().?;
-            const debug_info_sect = d_sym.getSection(d_sym.debug_info_section_index.?);
-            const file_pos = debug_info_sect.offset;
-            try pwriteDbgInfoNops(d_sym.file, file_pos, 0, di_buf.items, jmp_amt, false);
+            const macho_file = self.bin_file.cast(File.MachO).?;
+            if (macho_file.base.isRelocatable()) {
+                const debug_info_sect = macho_file.sections.items(.header)[macho_file.debug_info_sect_index.?];
+                const file_pos = debug_info_sect.offset;
+                try pwriteDbgInfoNops(macho_file.base.file.?, file_pos, 0, di_buf.items, jmp_amt, false);
+            } else {
+                const d_sym = macho_file.getDebugSymbols().?;
+                const debug_info_sect = d_sym.getSection(d_sym.debug_info_section_index.?);
+                const file_pos = debug_info_sect.offset;
+                try pwriteDbgInfoNops(d_sym.file, file_pos, 0, di_buf.items, jmp_amt, false);
+            }
         },
         .wasm => {
             const wasm_file = self.bin_file.cast(File.Wasm).?;
@@ -2296,12 +2366,20 @@ pub fn writeDbgAranges(self: *Dwarf, addr: u64, size: u64) !void {
         },
         .macho => {
             const macho_file = self.bin_file.cast(File.MachO).?;
-            const d_sym = macho_file.getDebugSymbols().?;
-            const sect_index = d_sym.debug_aranges_section_index.?;
-            try d_sym.growSection(sect_index, needed_size, false, macho_file);
-            const sect = d_sym.getSection(sect_index);
-            const file_pos = sect.offset;
-            try d_sym.file.pwriteAll(di_buf.items, file_pos);
+            if (macho_file.base.isRelocatable()) {
+                const sect_index = macho_file.debug_aranges_sect_index.?;
+                try macho_file.growSection(sect_index, needed_size);
+                const sect = macho_file.sections.items(.header)[sect_index];
+                const file_pos = sect.offset;
+                try macho_file.base.file.?.pwriteAll(di_buf.items, file_pos);
+            } else {
+                const d_sym = macho_file.getDebugSymbols().?;
+                const sect_index = d_sym.debug_aranges_section_index.?;
+                try d_sym.growSection(sect_index, needed_size, false, macho_file);
+                const sect = d_sym.getSection(sect_index);
+                const file_pos = sect.offset;
+                try d_sym.file.pwriteAll(di_buf.items, file_pos);
+            }
         },
         .wasm => {
             const wasm_file = self.bin_file.cast(File.Wasm).?;
@@ -2437,16 +2515,28 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void {
             },
             .macho => {
                 const macho_file = self.bin_file.cast(File.MachO).?;
-                const d_sym = macho_file.getDebugSymbols().?;
-                const sect_index = d_sym.debug_line_section_index.?;
-                const needed_size: u32 = @intCast(d_sym.getSection(sect_index).size + delta);
-                try d_sym.growSection(sect_index, needed_size, true, macho_file);
-                const file_pos = d_sym.getSection(sect_index).offset + first_fn.off;
+                if (macho_file.base.isRelocatable()) {
+                    const sect_index = macho_file.debug_line_sect_index.?;
+                    const needed_size: u32 = @intCast(macho_file.sections.items(.header)[sect_index].size + delta);
+                    try macho_file.growSection(sect_index, needed_size);
+                    const file_pos = macho_file.sections.items(.header)[sect_index].offset + first_fn.off;
 
-                const amt = try d_sym.file.preadAll(buffer, file_pos);
-                if (amt != buffer.len) return error.InputOutput;
+                    const amt = try macho_file.base.file.?.preadAll(buffer, file_pos);
+                    if (amt != buffer.len) return error.InputOutput;
+
+                    try macho_file.base.file.?.pwriteAll(buffer, file_pos + delta);
+                } else {
+                    const d_sym = macho_file.getDebugSymbols().?;
+                    const sect_index = d_sym.debug_line_section_index.?;
+                    const needed_size: u32 = @intCast(d_sym.getSection(sect_index).size + delta);
+                    try d_sym.growSection(sect_index, needed_size, true, macho_file);
+                    const file_pos = d_sym.getSection(sect_index).offset + first_fn.off;
+
+                    const amt = try d_sym.file.preadAll(buffer, file_pos);
+                    if (amt != buffer.len) return error.InputOutput;
 
-                try d_sym.file.pwriteAll(buffer, file_pos + delta);
+                    try d_sym.file.pwriteAll(buffer, file_pos + delta);
+                }
             },
             .wasm => {
                 const wasm_file = self.bin_file.cast(File.Wasm).?;
@@ -2492,10 +2582,17 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void {
             try pwriteDbgLineNops(elf_file.base.file.?, file_pos, 0, di_buf.items, jmp_amt);
         },
         .macho => {
-            const d_sym = self.bin_file.cast(File.MachO).?.getDebugSymbols().?;
-            const debug_line_sect = d_sym.getSection(d_sym.debug_line_section_index.?);
-            const file_pos = debug_line_sect.offset;
-            try pwriteDbgLineNops(d_sym.file, file_pos, 0, di_buf.items, jmp_amt);
+            const macho_file = self.bin_file.cast(File.MachO).?;
+            if (macho_file.base.isRelocatable()) {
+                const debug_line_sect = macho_file.sections.items(.header)[macho_file.debug_line_sect_index.?];
+                const file_pos = debug_line_sect.offset;
+                try pwriteDbgLineNops(macho_file.base.file.?, file_pos, 0, di_buf.items, jmp_amt);
+            } else {
+                const d_sym = macho_file.getDebugSymbols().?;
+                const debug_line_sect = d_sym.getSection(d_sym.debug_line_section_index.?);
+                const file_pos = debug_line_sect.offset;
+                try pwriteDbgLineNops(d_sym.file, file_pos, 0, di_buf.items, jmp_amt);
+            }
         },
         .wasm => {
             const wasm_file = self.bin_file.cast(File.Wasm).?;
@@ -2613,9 +2710,15 @@ pub fn flushModule(self: *Dwarf, module: *Module) !void {
                 break :pos debug_info_sect.sh_offset;
             },
             .macho => pos: {
-                const d_sym = self.bin_file.cast(File.MachO).?.getDebugSymbols().?;
-                const debug_info_sect = d_sym.getSectionPtr(d_sym.debug_info_section_index.?);
-                break :pos debug_info_sect.offset;
+                const macho_file = self.bin_file.cast(File.MachO).?;
+                if (macho_file.base.isRelocatable()) {
+                    const debug_info_sect = &macho_file.sections.items(.header)[macho_file.debug_info_sect_index.?];
+                    break :pos debug_info_sect.offset;
+                } else {
+                    const d_sym = macho_file.getDebugSymbols().?;
+                    const debug_info_sect = d_sym.getSectionPtr(d_sym.debug_info_section_index.?);
+                    break :pos debug_info_sect.offset;
+                }
             },
             // for wasm, the offset is always 0 as we write to memory first
             .wasm => 0,
@@ -2633,8 +2736,13 @@ pub fn flushModule(self: *Dwarf, module: *Module) !void {
                     try elf_file.base.file.?.pwriteAll(&buf, file_pos + atom.off + reloc.offset);
                 },
                 .macho => {
-                    const d_sym = self.bin_file.cast(File.MachO).?.getDebugSymbols().?;
-                    try d_sym.file.pwriteAll(&buf, file_pos + atom.off + reloc.offset);
+                    const macho_file = self.bin_file.cast(File.MachO).?;
+                    if (macho_file.base.isRelocatable()) {
+                        try macho_file.base.file.?.pwriteAll(&buf, file_pos + atom.off + reloc.offset);
+                    } else {
+                        const d_sym = macho_file.getDebugSymbols().?;
+                        try d_sym.file.pwriteAll(&buf, file_pos + atom.off + reloc.offset);
+                    }
                 },
                 .wasm => {
                     const wasm_file = self.bin_file.cast(File.Wasm).?;
@@ -2659,8 +2767,12 @@ fn addDIFile(self: *Dwarf, mod: *Module, decl_index: InternPool.DeclIndex) !u28
             },
             .macho => {
                 const macho_file = self.bin_file.cast(File.MachO).?;
-                const d_sym = macho_file.getDebugSymbols().?;
-                d_sym.markDirty(d_sym.debug_line_section_index.?, macho_file);
+                if (macho_file.base.isRelocatable()) {
+                    macho_file.markDirty(macho_file.debug_line_sect_index.?);
+                } else {
+                    const d_sym = macho_file.getDebugSymbols().?;
+                    d_sym.markDirty(d_sym.debug_line_section_index.?, macho_file);
+                }
             },
             .wasm => {},
             else => unreachable,
src/link/MachO.zig
@@ -103,6 +103,14 @@ zig_const_sect_index: ?u8 = null,
 zig_data_sect_index: ?u8 = null,
 zig_bss_sect_index: ?u8 = null,
 
+/// Tracked DWARF section headers that apply only when we emit relocatable.
+/// For executable and loadable images, DWARF is tracked directly by dSYM bundle object.
+debug_info_sect_index: ?u8 = null,
+debug_abbrev_sect_index: ?u8 = null,
+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,
@@ -259,32 +267,11 @@ pub fn createEmpty(
             try zo.init(self);
 
             try self.initMetadata(.{
+                .emit = emit,
+                .zo = zo,
                 .symbol_count_hint = options.symbol_count_hint,
                 .program_code_size_hint = options.program_code_size_hint,
             });
-
-            if (zo.dwarf != null and !self.base.isRelocatable()) {
-                // Create dSYM bundle.
-                log.debug("creating {s}.dSYM bundle", .{emit.sub_path});
-
-                const sep = fs.path.sep_str;
-                const d_sym_path = try std.fmt.allocPrint(
-                    arena,
-                    "{s}.dSYM" ++ sep ++ "Contents" ++ sep ++ "Resources" ++ sep ++ "DWARF",
-                    .{emit.sub_path},
-                );
-
-                var d_sym_bundle = try emit.directory.handle.makeOpenPath(d_sym_path, .{});
-                defer d_sym_bundle.close();
-
-                const d_sym_file = try d_sym_bundle.createFile(emit.sub_path, .{
-                    .truncate = false,
-                    .read = true,
-                });
-
-                self.d_sym = .{ .allocator = gpa, .file = d_sym_file };
-                try self.d_sym.?.initMetadata(self);
-            }
         }
     }
 
@@ -2007,6 +1994,11 @@ pub fn sortSections(self: *MachO) !void {
         &self.eh_frame_sect_index,
         &self.unwind_info_sect_index,
         &self.objc_stubs_sect_index,
+        &self.debug_info_sect_index,
+        &self.debug_str_sect_index,
+        &self.debug_line_sect_index,
+        &self.debug_abbrev_sect_index,
+        &self.debug_info_sect_index,
     }) |maybe_index| {
         if (maybe_index.*) |*index| {
             index.* = backlinks[index.*];
@@ -3317,6 +3309,8 @@ fn copyRangeAllZeroOut(self: *MachO, old_offset: u64, new_offset: u64, size: u64
 }
 
 const InitMetadataOptions = struct {
+    emit: Compilation.Emit,
+    zo: *ZigObject,
     symbol_count_hint: u64,
     program_code_size_hint: u64,
 };
@@ -3385,6 +3379,31 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void {
                 .prot = macho.PROT.READ | macho.PROT.WRITE,
             });
         }
+
+        if (options.zo.dwarf) |_| {
+            // Create dSYM bundle.
+            log.debug("creating {s}.dSYM bundle", .{options.emit.sub_path});
+
+            const gpa = self.base.comp.gpa;
+            const sep = fs.path.sep_str;
+            const d_sym_path = try std.fmt.allocPrint(
+                gpa,
+                "{s}.dSYM" ++ sep ++ "Contents" ++ sep ++ "Resources" ++ sep ++ "DWARF",
+                .{options.emit.sub_path},
+            );
+            defer gpa.free(d_sym_path);
+
+            var d_sym_bundle = try options.emit.directory.handle.makeOpenPath(d_sym_path, .{});
+            defer d_sym_bundle.close();
+
+            const d_sym_file = try d_sym_bundle.createFile(options.emit.sub_path, .{
+                .truncate = false,
+                .read = true,
+            });
+
+            self.d_sym = .{ .allocator = gpa, .file = d_sym_file };
+            try self.d_sym.?.initMetadata(self);
+        }
     }
 
     const appendSect = struct {
@@ -3462,6 +3481,44 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void {
             appendSect(self, self.zig_bss_sect_index.?, self.zig_bss_seg_index.?);
         }
     }
+
+    if (self.base.isRelocatable()) {
+        {
+            self.debug_str_sect_index = try self.addSection("__DWARF", "__debug_str", .{
+                .flags = macho.S_ATTR_DEBUG,
+            });
+            try allocSect(self, self.debug_str_sect_index.?, 200);
+        }
+
+        {
+            self.debug_info_sect_index = try self.addSection("__DWARF", "__debug_info", .{
+                .flags = macho.S_ATTR_DEBUG,
+            });
+            try allocSect(self, self.debug_info_sect_index.?, 200);
+        }
+
+        {
+            self.debug_abbrev_sect_index = try self.addSection("__DWARF", "__debug_abbrev", .{
+                .flags = macho.S_ATTR_DEBUG,
+            });
+            try allocSect(self, self.debug_abbrev_sect_index.?, 128);
+        }
+
+        {
+            self.debug_aranges_sect_index = try self.addSection("__DWARF", "__debug_aranges", .{
+                .alignment = 4,
+                .flags = macho.S_ATTR_DEBUG,
+            });
+            try allocSect(self, self.debug_aranges_sect_index.?, 160);
+        }
+
+        {
+            self.debug_line_sect_index = try self.addSection("__DWARF", "__debug_line", .{
+                .flags = macho.S_ATTR_DEBUG,
+            });
+            try allocSect(self, self.debug_line_sect_index.?, 250);
+        }
+    }
 }
 
 pub fn growSection(self: *MachO, sect_index: u8, needed_size: u64) !void {
@@ -3549,6 +3606,22 @@ fn growSectionRelocatable(self: *MachO, sect_index: u8, needed_size: u64) !void
     sect.size = needed_size;
 }
 
+pub fn markDirty(self: *MachO, sect_index: u8) void {
+    if (self.getZigObject()) |zo| {
+        if (self.debug_info_sect_index.? == sect_index) {
+            zo.debug_info_header_dirty = true;
+        } else if (self.debug_line_sect_index.? == sect_index) {
+            zo.debug_line_header_dirty = true;
+        } else if (self.debug_abbrev_sect_index.? == sect_index) {
+            zo.debug_abbrev_dirty = true;
+        } else if (self.debug_str_sect_index.? == sect_index) {
+            zo.debug_strtab_dirty = true;
+        } else if (self.debug_aranges_sect_index.? == sect_index) {
+            zo.debug_aranges_dirty = true;
+        }
+    }
+}
+
 pub fn getTarget(self: MachO) std.Target {
     return self.base.comp.root_mod.resolved_target.result;
 }
@@ -3624,6 +3697,21 @@ pub fn isZigSection(self: MachO, sect_id: u8) bool {
     return false;
 }
 
+pub fn isDebugSection(self: MachO, sect_id: u8) bool {
+    inline for (&[_]?u8{
+        self.debug_info_sect_index,
+        self.debug_abbrev_sect_index,
+        self.debug_str_sect_index,
+        self.debug_aranges_sect_index,
+        self.debug_line_sect_index,
+    }) |maybe_index| {
+        if (maybe_index) |index| {
+            if (index == sect_id) return true;
+        }
+    }
+    return false;
+}
+
 pub fn addSegment(self: *MachO, name: []const u8, opts: struct {
     vmaddr: u64 = 0,
     vmsize: u64 = 0,