Commit 5de2aae63c

Jakub Konka <kubkon@jakubkonka.com>
2023-02-01 15:03:55
link: decouple DI atoms from linker atoms, and manage them in Dwarf linker
1 parent d98fc53
src/arch/aarch64/CodeGen.zig
@@ -203,13 +203,7 @@ const DbgInfoReloc = struct {
                     else => unreachable, // not a possible argument
 
                 };
-                try dw.genArgDbgInfo(
-                    reloc.name,
-                    reloc.ty,
-                    function.bin_file.tag,
-                    function.mod_fn.owner_decl,
-                    loc,
-                );
+                try dw.genArgDbgInfo(reloc.name, reloc.ty, function.mod_fn.owner_decl, loc);
             },
             .plan9 => {},
             .none => {},
@@ -255,14 +249,7 @@ const DbgInfoReloc = struct {
                         break :blk .nop;
                     },
                 };
-                try dw.genVarDbgInfo(
-                    reloc.name,
-                    reloc.ty,
-                    function.bin_file.tag,
-                    function.mod_fn.owner_decl,
-                    is_ptr,
-                    loc,
-                );
+                try dw.genVarDbgInfo(reloc.name, reloc.ty, function.mod_fn.owner_decl, is_ptr, loc);
             },
             .plan9 => {},
             .none => {},
src/arch/arm/CodeGen.zig
@@ -282,13 +282,7 @@ const DbgInfoReloc = struct {
                     else => unreachable, // not a possible argument
                 };
 
-                try dw.genArgDbgInfo(
-                    reloc.name,
-                    reloc.ty,
-                    function.bin_file.tag,
-                    function.mod_fn.owner_decl,
-                    loc,
-                );
+                try dw.genArgDbgInfo(reloc.name, reloc.ty, function.mod_fn.owner_decl, loc);
             },
             .plan9 => {},
             .none => {},
@@ -331,14 +325,7 @@ const DbgInfoReloc = struct {
                         break :blk .nop;
                     },
                 };
-                try dw.genVarDbgInfo(
-                    reloc.name,
-                    reloc.ty,
-                    function.bin_file.tag,
-                    function.mod_fn.owner_decl,
-                    is_ptr,
-                    loc,
-                );
+                try dw.genVarDbgInfo(reloc.name, reloc.ty, function.mod_fn.owner_decl, is_ptr, loc);
             },
             .plan9 => {},
             .none => {},
src/arch/riscv64/CodeGen.zig
@@ -1615,13 +1615,9 @@ fn genArgDbgInfo(self: Self, inst: Air.Inst.Index, mcv: MCValue) !void {
 
     switch (self.debug_output) {
         .dwarf => |dw| switch (mcv) {
-            .register => |reg| try dw.genArgDbgInfo(
-                name,
-                ty,
-                self.bin_file.tag,
-                self.mod_fn.owner_decl,
-                .{ .register = reg.dwarfLocOp() },
-            ),
+            .register => |reg| try dw.genArgDbgInfo(name, ty, self.mod_fn.owner_decl, .{
+                .register = reg.dwarfLocOp(),
+            }),
             .stack_offset => {},
             else => {},
         },
src/arch/sparc64/CodeGen.zig
@@ -3412,13 +3412,9 @@ fn genArgDbgInfo(self: Self, inst: Air.Inst.Index, mcv: MCValue) !void {
 
     switch (self.debug_output) {
         .dwarf => |dw| switch (mcv) {
-            .register => |reg| try dw.genArgDbgInfo(
-                name,
-                ty,
-                self.bin_file.tag,
-                self.mod_fn.owner_decl,
-                .{ .register = reg.dwarfLocOp() },
-            ),
+            .register => |reg| try dw.genArgDbgInfo(name, ty, self.mod_fn.owner_decl, .{
+                .register = reg.dwarfLocOp(),
+            }),
             else => {},
         },
         else => {},
src/arch/wasm/CodeGen.zig
@@ -2475,7 +2475,7 @@ fn airArg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
         .dwarf => |dwarf| {
             const src_index = func.air.instructions.items(.data)[inst].arg.src_index;
             const name = func.mod_fn.getParamName(func.bin_file.base.options.module.?, src_index);
-            try dwarf.genArgDbgInfo(name, arg_ty, .wasm, func.mod_fn.owner_decl, .{
+            try dwarf.genArgDbgInfo(name, arg_ty, func.mod_fn.owner_decl, .{
                 .wasm_local = arg.local.value,
             });
         },
@@ -5539,7 +5539,7 @@ fn airDbgVar(func: *CodeGen, inst: Air.Inst.Index, is_ptr: bool) !void {
             break :blk .nop;
         },
     };
-    try func.debug_output.dwarf.genVarDbgInfo(name, ty, .wasm, func.mod_fn.owner_decl, is_ptr, loc);
+    try func.debug_output.dwarf.genVarDbgInfo(name, ty, func.mod_fn.owner_decl, is_ptr, loc);
 
     func.finishAir(inst, .none, &.{});
 }
src/arch/x86_64/CodeGen.zig
@@ -3836,7 +3836,7 @@ fn genArgDbgInfo(self: Self, ty: Type, name: [:0]const u8, mcv: MCValue) !void {
                 },
                 else => unreachable, // not a valid function parameter
             };
-            try dw.genArgDbgInfo(name, ty, self.bin_file.tag, self.mod_fn.owner_decl, loc);
+            try dw.genArgDbgInfo(name, ty, self.mod_fn.owner_decl, loc);
         },
         .plan9 => {},
         .none => {},
@@ -3876,7 +3876,7 @@ fn genVarDbgInfo(
                     break :blk .nop;
                 },
             };
-            try dw.genVarDbgInfo(name, ty, self.bin_file.tag, self.mod_fn.owner_decl, is_ptr, loc);
+            try dw.genVarDbgInfo(name, ty, self.mod_fn.owner_decl, is_ptr, loc);
         },
         .plan9 => {},
         .none => {},
src/link/Elf/Atom.zig
@@ -4,7 +4,6 @@ const std = @import("std");
 const assert = std.debug.assert;
 const elf = std.elf;
 
-const Dwarf = @import("../Dwarf.zig");
 const Elf = @import("../Elf.zig");
 
 /// Each decl always gets a local symbol with the fully qualified name.
@@ -23,8 +22,6 @@ offset_table_index: u32,
 prev_index: ?Index,
 next_index: ?Index,
 
-dbg_info_atom: Dwarf.Atom,
-
 pub const Index = u32;
 
 pub const Reloc = struct {
src/link/MachO/Atom.zig
@@ -13,7 +13,6 @@ const trace = @import("../../tracy.zig").trace;
 
 const Allocator = mem.Allocator;
 const Arch = std.Target.Cpu.Arch;
-const Dwarf = @import("../Dwarf.zig");
 const MachO = @import("../MachO.zig");
 const Relocation = @import("Relocation.zig");
 const SymbolWithLoc = MachO.SymbolWithLoc;
@@ -43,8 +42,6 @@ alignment: u32,
 next_index: ?Index,
 prev_index: ?Index,
 
-dbg_info_atom: Dwarf.Atom,
-
 pub const Index = u32;
 
 pub const Binding = struct {
src/link/Wasm/Atom.zig
@@ -4,7 +4,6 @@ const std = @import("std");
 const types = @import("types.zig");
 const Wasm = @import("../Wasm.zig");
 const Symbol = @import("Symbol.zig");
-const Dwarf = @import("../Dwarf.zig");
 
 const leb = std.leb;
 const log = std.log.scoped(.link);
@@ -39,9 +38,6 @@ prev: ?*Atom,
 /// When the parent atom is being freed, it will also do so for all local atoms.
 locals: std.ArrayListUnmanaged(Atom) = .{},
 
-/// Represents the debug Atom that holds all debug information of this Atom.
-dbg_info_atom: Dwarf.Atom,
-
 /// Represents a default empty wasm `Atom`
 pub const empty: Atom = .{
     .alignment = 0,
@@ -51,7 +47,6 @@ pub const empty: Atom = .{
     .prev = null,
     .size = 0,
     .sym_index = 0,
-    .dbg_info_atom = undefined,
 };
 
 /// Frees all resources owned by this `Atom`.
src/link/C.zig
@@ -219,12 +219,12 @@ pub fn updateDecl(self: *C, module: *Module, decl_index: Module.Decl.Index) !voi
     code.shrinkAndFree(module.gpa, code.items.len);
 }
 
-pub fn updateDeclLineNumber(self: *C, module: *Module, decl: *Module.Decl) !void {
+pub fn updateDeclLineNumber(self: *C, module: *Module, decl_index: Module.Decl.Index) !void {
     // The C backend does not have the ability to fix line numbers without re-generating
     // the entire Decl.
     _ = self;
     _ = module;
-    _ = decl;
+    _ = decl_index;
 }
 
 pub fn flush(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) !void {
src/link/Coff.zig
@@ -195,7 +195,6 @@ pub const PtrWidth = enum {
         };
     }
 };
-pub const SrcFn = void;
 
 pub const SymbolWithLoc = struct {
     // Index into the respective symbol table.
@@ -1545,10 +1544,10 @@ pub fn getGlobalSymbol(self: *Coff, name: []const u8) !u32 {
     return global_index;
 }
 
-pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !void {
+pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !void {
     _ = self;
     _ = module;
-    _ = decl;
+    _ = decl_index;
     log.debug("TODO implement updateDeclLineNumber", .{});
 }
 
src/link/Dwarf.zig
@@ -27,17 +27,21 @@ bin_file: *File,
 ptr_width: PtrWidth,
 target: std.Target,
 
-/// A list of `File.LinkFn` whose Line Number Programs have surplus capacity.
-/// This is the same concept as `text_block_free_list`; see those doc comments.
-dbg_line_fn_free_list: std.AutoHashMapUnmanaged(*SrcFn, void) = .{},
-dbg_line_fn_first: ?*SrcFn = null,
-dbg_line_fn_last: ?*SrcFn = null,
+/// A list of `Atom`s whose Line Number Programs have surplus capacity.
+/// This is the same concept as `Section.free_list` in Elf; see those doc comments.
+src_fn_free_list: std.AutoHashMapUnmanaged(Atom.Index, void) = .{},
+src_fn_first_index: ?Atom.Index = null,
+src_fn_last_index: ?Atom.Index = null,
+src_fns: std.ArrayListUnmanaged(Atom) = .{},
+src_fn_decls: AtomTable = .{},
 
 /// A list of `Atom`s whose corresponding .debug_info tags have surplus capacity.
 /// This is the same concept as `text_block_free_list`; see those doc comments.
-atom_free_list: std.AutoHashMapUnmanaged(*Atom, void) = .{},
-atom_first: ?*Atom = null,
-atom_last: ?*Atom = null,
+di_atom_free_list: std.AutoHashMapUnmanaged(Atom.Index, void) = .{},
+di_atom_first_index: ?Atom.Index = null,
+di_atom_last_index: ?Atom.Index = null,
+di_atoms: std.ArrayListUnmanaged(Atom) = .{},
+di_atom_decls: AtomTable = .{},
 
 abbrev_table_offset: ?u64 = null,
 
@@ -51,22 +55,23 @@ strtab: StringTable(.strtab) = .{},
 /// * []file_names
 di_files: std.AutoArrayHashMapUnmanaged(*const Module.File, void) = .{},
 
-/// List of atoms that are owned directly by the DWARF module.
-/// TODO convert links in DebugInfoAtom into indices and make
-/// sure every atom is owned by this module.
-managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},
-
 global_abbrev_relocs: std.ArrayListUnmanaged(AbbrevRelocation) = .{},
 
-pub const Atom = struct {
-    /// Previous/next linked list pointers.
-    /// This is the linked list node for this Decl's corresponding .debug_info tag.
-    prev: ?*Atom,
-    next: ?*Atom,
-    /// Offset into .debug_info pointing to the tag for this Decl.
+const AtomTable = std.AutoHashMapUnmanaged(Module.Decl.Index, Atom.Index);
+
+const Atom = struct {
+    /// Offset into .debug_info pointing to the tag for this Decl, or
+    /// offset from the beginning of the Debug Line Program header that contains this function.
     off: u32,
-    /// Size of the .debug_info tag for this Decl, not including padding.
+    /// Size of the .debug_info tag for this Decl, not including padding, or
+    /// size of the line number program component belonging to this function, not
+    /// including padding.
     len: u32,
+
+    prev_index: ?Index,
+    next_index: ?Index,
+
+    pub const Index = u32;
 };
 
 /// Represents state of the analysed Decl.
@@ -76,6 +81,7 @@ pub const Atom = struct {
 pub const DeclState = struct {
     gpa: Allocator,
     mod: *Module,
+    di_atom_decls: *const AtomTable,
     dbg_line: std.ArrayList(u8),
     dbg_info: std.ArrayList(u8),
     abbrev_type_arena: std.heap.ArenaAllocator,
@@ -89,10 +95,11 @@ pub const DeclState = struct {
     abbrev_relocs: std.ArrayListUnmanaged(AbbrevRelocation) = .{},
     exprloc_relocs: std.ArrayListUnmanaged(ExprlocRelocation) = .{},
 
-    fn init(gpa: Allocator, mod: *Module) DeclState {
+    fn init(gpa: Allocator, mod: *Module, di_atom_decls: *const AtomTable) DeclState {
         return .{
             .gpa = gpa,
             .mod = mod,
+            .di_atom_decls = di_atom_decls,
             .dbg_line = std.ArrayList(u8).init(gpa),
             .dbg_info = std.ArrayList(u8).init(gpa),
             .abbrev_type_arena = std.heap.ArenaAllocator.init(gpa),
@@ -120,11 +127,11 @@ pub const DeclState = struct {
 
     /// Adds local type relocation of the form: @offset => @this + addend
     /// @this signifies the offset within the .debug_abbrev section of the containing atom.
-    fn addTypeRelocLocal(self: *DeclState, atom: *const Atom, offset: u32, addend: u32) !void {
+    fn addTypeRelocLocal(self: *DeclState, atom_index: Atom.Index, offset: u32, addend: u32) !void {
         log.debug("{x}: @this + {x}", .{ offset, addend });
         try self.abbrev_relocs.append(self.gpa, .{
             .target = null,
-            .atom = atom,
+            .atom_index = atom_index,
             .offset = offset,
             .addend = addend,
         });
@@ -133,13 +140,13 @@ pub const DeclState = struct {
     /// Adds global type relocation of the form: @offset => @symbol + 0
     /// @symbol signifies a type abbreviation posititioned somewhere in the .debug_abbrev section
     /// which we use as our target of the relocation.
-    fn addTypeRelocGlobal(self: *DeclState, atom: *const Atom, ty: Type, offset: u32) !void {
+    fn addTypeRelocGlobal(self: *DeclState, atom_index: Atom.Index, ty: Type, offset: u32) !void {
         const resolv = self.abbrev_resolver.getContext(ty, .{
             .mod = self.mod,
         }) orelse blk: {
             const sym_index = @intCast(u32, self.abbrev_table.items.len);
             try self.abbrev_table.append(self.gpa, .{
-                .atom = atom,
+                .atom_index = atom_index,
                 .type = ty,
                 .offset = undefined,
             });
@@ -154,7 +161,7 @@ pub const DeclState = struct {
         log.debug("{x}: %{d} + 0", .{ offset, resolv });
         try self.abbrev_relocs.append(self.gpa, .{
             .target = resolv,
-            .atom = atom,
+            .atom_index = atom_index,
             .offset = offset,
             .addend = 0,
         });
@@ -163,7 +170,7 @@ pub const DeclState = struct {
     fn addDbgInfoType(
         self: *DeclState,
         module: *Module,
-        atom: *Atom,
+        atom_index: Atom.Index,
         ty: Type,
     ) error{OutOfMemory}!void {
         const arena = self.abbrev_type_arena.allocator();
@@ -228,7 +235,7 @@ pub const DeclState = struct {
                     // DW.AT.type, DW.FORM.ref4
                     var index = dbg_info_buffer.items.len;
                     try dbg_info_buffer.resize(index + 4);
-                    try self.addTypeRelocGlobal(atom, Type.bool, @intCast(u32, index));
+                    try self.addTypeRelocGlobal(atom_index, Type.bool, @intCast(u32, index));
                     // DW.AT.data_member_location, DW.FORM.sdata
                     try dbg_info_buffer.ensureUnusedCapacity(6);
                     dbg_info_buffer.appendAssumeCapacity(0);
@@ -240,7 +247,7 @@ pub const DeclState = struct {
                     // DW.AT.type, DW.FORM.ref4
                     index = dbg_info_buffer.items.len;
                     try dbg_info_buffer.resize(index + 4);
-                    try self.addTypeRelocGlobal(atom, payload_ty, @intCast(u32, index));
+                    try self.addTypeRelocGlobal(atom_index, payload_ty, @intCast(u32, index));
                     // DW.AT.data_member_location, DW.FORM.sdata
                     const offset = abi_size - payload_ty.abiSize(target);
                     try leb128.writeULEB128(dbg_info_buffer.writer(), offset);
@@ -271,7 +278,7 @@ pub const DeclState = struct {
                     try dbg_info_buffer.resize(index + 4);
                     var buf = try arena.create(Type.SlicePtrFieldTypeBuffer);
                     const ptr_ty = ty.slicePtrFieldType(buf);
-                    try self.addTypeRelocGlobal(atom, ptr_ty, @intCast(u32, index));
+                    try self.addTypeRelocGlobal(atom_index, ptr_ty, @intCast(u32, index));
                     // DW.AT.data_member_location, DW.FORM.sdata
                     try dbg_info_buffer.ensureUnusedCapacity(6);
                     dbg_info_buffer.appendAssumeCapacity(0);
@@ -283,7 +290,7 @@ pub const DeclState = struct {
                     // DW.AT.type, DW.FORM.ref4
                     index = dbg_info_buffer.items.len;
                     try dbg_info_buffer.resize(index + 4);
-                    try self.addTypeRelocGlobal(atom, Type.usize, @intCast(u32, index));
+                    try self.addTypeRelocGlobal(atom_index, Type.usize, @intCast(u32, index));
                     // DW.AT.data_member_location, DW.FORM.sdata
                     try dbg_info_buffer.ensureUnusedCapacity(2);
                     dbg_info_buffer.appendAssumeCapacity(ptr_bytes);
@@ -295,7 +302,7 @@ pub const DeclState = struct {
                     // DW.AT.type, DW.FORM.ref4
                     const index = dbg_info_buffer.items.len;
                     try dbg_info_buffer.resize(index + 4);
-                    try self.addTypeRelocGlobal(atom, ty.childType(), @intCast(u32, index));
+                    try self.addTypeRelocGlobal(atom_index, ty.childType(), @intCast(u32, index));
                 }
             },
             .Array => {
@@ -306,13 +313,13 @@ pub const DeclState = struct {
                 // DW.AT.type, DW.FORM.ref4
                 var index = dbg_info_buffer.items.len;
                 try dbg_info_buffer.resize(index + 4);
-                try self.addTypeRelocGlobal(atom, ty.childType(), @intCast(u32, index));
+                try self.addTypeRelocGlobal(atom_index, ty.childType(), @intCast(u32, index));
                 // DW.AT.subrange_type
                 try dbg_info_buffer.append(@enumToInt(AbbrevKind.array_dim));
                 // DW.AT.type, DW.FORM.ref4
                 index = dbg_info_buffer.items.len;
                 try dbg_info_buffer.resize(index + 4);
-                try self.addTypeRelocGlobal(atom, Type.usize, @intCast(u32, index));
+                try self.addTypeRelocGlobal(atom_index, Type.usize, @intCast(u32, index));
                 // DW.AT.count, DW.FORM.udata
                 const len = ty.arrayLenIncludingSentinel();
                 try leb128.writeULEB128(dbg_info_buffer.writer(), len);
@@ -340,7 +347,7 @@ pub const DeclState = struct {
                             // DW.AT.type, DW.FORM.ref4
                             var index = dbg_info_buffer.items.len;
                             try dbg_info_buffer.resize(index + 4);
-                            try self.addTypeRelocGlobal(atom, field, @intCast(u32, index));
+                            try self.addTypeRelocGlobal(atom_index, field, @intCast(u32, index));
                             // DW.AT.data_member_location, DW.FORM.sdata
                             const field_off = ty.structFieldOffset(field_index, target);
                             try leb128.writeULEB128(dbg_info_buffer.writer(), field_off);
@@ -372,7 +379,7 @@ pub const DeclState = struct {
                             // DW.AT.type, DW.FORM.ref4
                             var index = dbg_info_buffer.items.len;
                             try dbg_info_buffer.resize(index + 4);
-                            try self.addTypeRelocGlobal(atom, field.ty, @intCast(u32, index));
+                            try self.addTypeRelocGlobal(atom_index, field.ty, @intCast(u32, index));
                             // DW.AT.data_member_location, DW.FORM.sdata
                             const field_off = ty.structFieldOffset(field_index, target);
                             try leb128.writeULEB128(dbg_info_buffer.writer(), field_off);
@@ -455,7 +462,7 @@ pub const DeclState = struct {
                     // DW.AT.type, DW.FORM.ref4
                     const inner_union_index = dbg_info_buffer.items.len;
                     try dbg_info_buffer.resize(inner_union_index + 4);
-                    try self.addTypeRelocLocal(atom, @intCast(u32, inner_union_index), 5);
+                    try self.addTypeRelocLocal(atom_index, @intCast(u32, inner_union_index), 5);
                     // DW.AT.data_member_location, DW.FORM.sdata
                     try leb128.writeULEB128(dbg_info_buffer.writer(), payload_offset);
                 }
@@ -482,7 +489,7 @@ pub const DeclState = struct {
                     // DW.AT.type, DW.FORM.ref4
                     const index = dbg_info_buffer.items.len;
                     try dbg_info_buffer.resize(index + 4);
-                    try self.addTypeRelocGlobal(atom, field.ty, @intCast(u32, index));
+                    try self.addTypeRelocGlobal(atom_index, field.ty, @intCast(u32, index));
                     // DW.AT.data_member_location, DW.FORM.sdata
                     try dbg_info_buffer.append(0);
                 }
@@ -499,7 +506,7 @@ pub const DeclState = struct {
                     // DW.AT.type, DW.FORM.ref4
                     const index = dbg_info_buffer.items.len;
                     try dbg_info_buffer.resize(index + 4);
-                    try self.addTypeRelocGlobal(atom, union_obj.tag_ty, @intCast(u32, index));
+                    try self.addTypeRelocGlobal(atom_index, union_obj.tag_ty, @intCast(u32, index));
                     // DW.AT.data_member_location, DW.FORM.sdata
                     try leb128.writeULEB128(dbg_info_buffer.writer(), tag_offset);
 
@@ -542,7 +549,7 @@ pub const DeclState = struct {
                 // DW.AT.type, DW.FORM.ref4
                 var index = dbg_info_buffer.items.len;
                 try dbg_info_buffer.resize(index + 4);
-                try self.addTypeRelocGlobal(atom, payload_ty, @intCast(u32, index));
+                try self.addTypeRelocGlobal(atom_index, payload_ty, @intCast(u32, index));
                 // DW.AT.data_member_location, DW.FORM.sdata
                 try leb128.writeULEB128(dbg_info_buffer.writer(), payload_off);
 
@@ -555,7 +562,7 @@ pub const DeclState = struct {
                 // DW.AT.type, DW.FORM.ref4
                 index = dbg_info_buffer.items.len;
                 try dbg_info_buffer.resize(index + 4);
-                try self.addTypeRelocGlobal(atom, error_ty, @intCast(u32, index));
+                try self.addTypeRelocGlobal(atom_index, error_ty, @intCast(u32, index));
                 // DW.AT.data_member_location, DW.FORM.sdata
                 try leb128.writeULEB128(dbg_info_buffer.writer(), error_off);
 
@@ -588,12 +595,11 @@ pub const DeclState = struct {
         self: *DeclState,
         name: [:0]const u8,
         ty: Type,
-        tag: File.Tag,
         owner_decl: Module.Decl.Index,
         loc: DbgInfoLoc,
     ) error{OutOfMemory}!void {
         const dbg_info = &self.dbg_info;
-        const atom = getDbgInfoAtom(tag, self.mod, owner_decl);
+        const atom_index = self.di_atom_decls.get(owner_decl).?;
         const name_with_null = name.ptr[0 .. name.len + 1];
 
         switch (loc) {
@@ -638,7 +644,7 @@ pub const DeclState = struct {
         try dbg_info.ensureUnusedCapacity(5 + name_with_null.len);
         const index = dbg_info.items.len;
         try dbg_info.resize(index + 4); // dw.at.type,  dw.form.ref4
-        try self.addTypeRelocGlobal(atom, ty, @intCast(u32, index)); // DW.AT.type,  DW.FORM.ref4
+        try self.addTypeRelocGlobal(atom_index, ty, @intCast(u32, index)); // DW.AT.type,  DW.FORM.ref4
         dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string
     }
 
@@ -646,13 +652,12 @@ pub const DeclState = struct {
         self: *DeclState,
         name: [:0]const u8,
         ty: Type,
-        tag: File.Tag,
         owner_decl: Module.Decl.Index,
         is_ptr: bool,
         loc: DbgInfoLoc,
     ) error{OutOfMemory}!void {
         const dbg_info = &self.dbg_info;
-        const atom = getDbgInfoAtom(tag, self.mod, owner_decl);
+        const atom_index = self.di_atom_decls.get(owner_decl).?;
         const name_with_null = name.ptr[0 .. name.len + 1];
         try dbg_info.append(@enumToInt(AbbrevKind.variable));
         const target = self.mod.getTarget();
@@ -782,7 +787,7 @@ pub const DeclState = struct {
         try dbg_info.ensureUnusedCapacity(5 + name_with_null.len);
         const index = dbg_info.items.len;
         try dbg_info.resize(index + 4); // dw.at.type,  dw.form.ref4
-        try self.addTypeRelocGlobal(atom, child_ty, @intCast(u32, index));
+        try self.addTypeRelocGlobal(atom_index, child_ty, @intCast(u32, index));
         dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string
     }
 
@@ -815,7 +820,7 @@ pub const DeclState = struct {
 };
 
 pub const AbbrevEntry = struct {
-    atom: *const Atom,
+    atom_index: Atom.Index,
     type: Type,
     offset: u32,
 };
@@ -824,7 +829,7 @@ pub const AbbrevRelocation = struct {
     /// If target is null, we deal with a local relocation that is based on simple offset + addend
     /// only.
     target: ?u32,
-    atom: *const Atom,
+    atom_index: Atom.Index,
     offset: u32,
     addend: u32,
 };
@@ -841,26 +846,6 @@ pub const ExprlocRelocation = struct {
     offset: u32,
 };
 
-pub const SrcFn = struct {
-    /// Offset from the beginning of the Debug Line Program header that contains this function.
-    off: u32,
-    /// Size of the line number program component belonging to this function, not
-    /// including padding.
-    len: u32,
-
-    /// Points to the previous and next neighbors, based on the offset from .debug_line.
-    /// This can be used to find, for example, the capacity of this `SrcFn`.
-    prev: ?*SrcFn,
-    next: ?*SrcFn,
-
-    pub const empty: SrcFn = .{
-        .off = 0,
-        .len = 0,
-        .prev = null,
-        .next = null,
-    };
-};
-
 pub const PtrWidth = enum { p32, p64 };
 
 pub const AbbrevKind = enum(u8) {
@@ -910,16 +895,18 @@ pub fn init(allocator: Allocator, bin_file: *File, target: std.Target) Dwarf {
 
 pub fn deinit(self: *Dwarf) void {
     const gpa = self.allocator;
-    self.dbg_line_fn_free_list.deinit(gpa);
-    self.atom_free_list.deinit(gpa);
+
+    self.src_fn_free_list.deinit(gpa);
+    self.src_fns.deinit(gpa);
+    self.src_fn_decls.deinit(gpa);
+
+    self.di_atom_free_list.deinit(gpa);
+    self.di_atoms.deinit(gpa);
+    self.di_atom_decls.deinit(gpa);
+
     self.strtab.deinit(gpa);
     self.di_files.deinit(gpa);
     self.global_abbrev_relocs.deinit(gpa);
-
-    for (self.managed_atoms.items) |atom| {
-        gpa.destroy(atom);
-    }
-    self.managed_atoms.deinit(gpa);
 }
 
 /// Initializes Decl's state and its matching output buffers.
@@ -935,15 +922,19 @@ pub fn initDeclState(self: *Dwarf, mod: *Module, decl_index: Module.Decl.Index)
     log.debug("initDeclState {s}{*}", .{ decl_name, decl });
 
     const gpa = self.allocator;
-    var decl_state = DeclState.init(gpa, mod);
+    var decl_state = DeclState.init(gpa, mod, &self.di_atom_decls);
     errdefer decl_state.deinit();
     const dbg_line_buffer = &decl_state.dbg_line;
     const dbg_info_buffer = &decl_state.dbg_info;
 
+    const di_atom_index = try self.getOrCreateAtomForDecl(.di_atom, decl_index);
+
     assert(decl.has_tv);
 
     switch (decl.ty.zigTypeTag()) {
         .Fn => {
+            _ = try self.getOrCreateAtomForDecl(.src_fn, decl_index);
+
             // For functions we need to add a prologue to the debug line program.
             try dbg_line_buffer.ensureTotalCapacity(26);
 
@@ -1003,8 +994,7 @@ pub fn initDeclState(self: *Dwarf, mod: *Module, decl_index: Module.Decl.Index)
             dbg_info_buffer.items.len += 4; // DW.AT.high_pc,  DW.FORM.data4
             //
             if (fn_ret_has_bits) {
-                const atom = getDbgInfoAtom(self.bin_file.tag, mod, decl_index);
-                try decl_state.addTypeRelocGlobal(atom, fn_ret_type, @intCast(u32, dbg_info_buffer.items.len));
+                try decl_state.addTypeRelocGlobal(di_atom_index, fn_ret_type, @intCast(u32, dbg_info_buffer.items.len));
                 dbg_info_buffer.items.len += 4; // DW.AT.type,  DW.FORM.ref4
             }
 
@@ -1076,26 +1066,23 @@ pub fn commitDeclState(
             // This logic is nearly identical to the logic below in `updateDeclDebugInfo` for
             // `TextBlock` and the .debug_info. If you are editing this logic, you
             // probably need to edit that logic too.
-            const src_fn = switch (self.bin_file.tag) {
-                .elf => &decl.fn_link.elf,
-                .macho => &decl.fn_link.macho,
-                .wasm => &decl.fn_link.wasm.src_fn,
-                else => unreachable, // TODO
-            };
+            const src_fn_index = self.src_fn_decls.get(decl_index).?;
+            const src_fn = self.getAtomPtr(.src_fn, src_fn_index);
             src_fn.len = @intCast(u32, dbg_line_buffer.items.len);
 
-            if (self.dbg_line_fn_last) |last| blk: {
-                if (src_fn == last) break :blk;
-                if (src_fn.next) |next| {
+            if (self.src_fn_last_index) |last_index| blk: {
+                if (src_fn_index == last_index) break :blk;
+                if (src_fn.next_index) |next_index| {
+                    const next = self.getAtomPtr(.src_fn, next_index);
                     // Update existing function - non-last item.
                     if (src_fn.off + src_fn.len + min_nop_size > next.off) {
                         // It grew too big, so we move it to a new location.
-                        if (src_fn.prev) |prev| {
-                            self.dbg_line_fn_free_list.put(gpa, prev, {}) catch {};
-                            prev.next = src_fn.next;
+                        if (src_fn.prev_index) |prev_index| {
+                            self.src_fn_free_list.put(gpa, prev_index, {}) catch {};
+                            self.getAtomPtr(.src_fn, prev_index).next_index = src_fn.next_index;
                         }
-                        next.prev = src_fn.prev;
-                        src_fn.next = null;
+                        next.prev_index = src_fn.prev_index;
+                        src_fn.next_index = null;
                         // Populate where it used to be with NOPs.
                         switch (self.bin_file.tag) {
                             .elf => {
@@ -1118,33 +1105,42 @@ pub fn commitDeclState(
                             else => unreachable,
                         }
                         // TODO Look at the free list before appending at the end.
-                        src_fn.prev = last;
-                        last.next = src_fn;
-                        self.dbg_line_fn_last = src_fn;
+                        src_fn.prev_index = last_index;
+                        const last = self.getAtomPtr(.src_fn, last_index);
+                        last.next_index = src_fn_index;
+                        self.src_fn_last_index = src_fn_index;
 
                         src_fn.off = last.off + padToIdeal(last.len);
                     }
-                } else if (src_fn.prev == null) {
+                } else if (src_fn.prev_index == null) {
                     // Append new function.
                     // TODO Look at the free list before appending at the end.
-                    src_fn.prev = last;
-                    last.next = src_fn;
-                    self.dbg_line_fn_last = src_fn;
+                    src_fn.prev_index = last_index;
+                    const last = self.getAtomPtr(.src_fn, last_index);
+                    last.next_index = src_fn_index;
+                    self.src_fn_last_index = src_fn_index;
 
                     src_fn.off = last.off + padToIdeal(last.len);
                 }
             } else {
                 // This is the first function of the Line Number Program.
-                self.dbg_line_fn_first = src_fn;
-                self.dbg_line_fn_last = src_fn;
+                self.src_fn_first_index = src_fn_index;
+                self.src_fn_last_index = src_fn_index;
 
                 src_fn.off = padToIdeal(self.dbgLineNeededHeaderBytes(&[0][]u8{}, &[0][]u8{}));
             }
 
-            const last_src_fn = self.dbg_line_fn_last.?;
+            const last_src_fn_index = self.src_fn_last_index.?;
+            const last_src_fn = self.getAtom(.src_fn, last_src_fn_index);
             const needed_size = last_src_fn.off + last_src_fn.len;
-            const prev_padding_size: u32 = if (src_fn.prev) |prev| src_fn.off - (prev.off + prev.len) else 0;
-            const next_padding_size: u32 = if (src_fn.next) |next| next.off - (src_fn.off + src_fn.len) else 0;
+            const prev_padding_size: u32 = if (src_fn.prev_index) |prev_index| blk: {
+                const prev = self.getAtom(.src_fn, prev_index);
+                break :blk src_fn.off - (prev.off + prev.len);
+            } else 0;
+            const next_padding_size: u32 = if (src_fn.next_index) |next_index| blk: {
+                const next = self.getAtom(.src_fn, next_index);
+                break :blk next.off - (src_fn.off + src_fn.len);
+            } else 0;
 
             // We only have support for one compilation unit so far, so the offsets are directly
             // from the .debug_line section.
@@ -1213,7 +1209,7 @@ pub fn commitDeclState(
     if (dbg_info_buffer.items.len == 0)
         return;
 
-    const atom = getDbgInfoAtom(self.bin_file.tag, module, decl_index);
+    const di_atom_index = self.di_atom_decls.get(decl_index).?;
     if (decl_state.abbrev_table.items.len > 0) {
         // Now we emit the .debug_info types of the Decl. These will count towards the size of
         // the buffer, so we have to do it before computing the offset, and we can't perform the actual
@@ -1235,12 +1231,12 @@ pub fn commitDeclState(
             if (deferred) continue;
 
             symbol.offset = @intCast(u32, dbg_info_buffer.items.len);
-            try decl_state.addDbgInfoType(module, atom, ty);
+            try decl_state.addDbgInfoType(module, di_atom_index, ty);
         }
     }
 
     log.debug("updateDeclDebugInfoAllocation for '{s}'", .{decl.name});
-    try self.updateDeclDebugInfoAllocation(atom, @intCast(u32, dbg_info_buffer.items.len));
+    try self.updateDeclDebugInfoAllocation(di_atom_index, @intCast(u32, dbg_info_buffer.items.len));
 
     while (decl_state.abbrev_relocs.popOrNull()) |reloc| {
         if (reloc.target) |target| {
@@ -1261,11 +1257,12 @@ pub fn commitDeclState(
                 try self.global_abbrev_relocs.append(gpa, .{
                     .target = null,
                     .offset = reloc.offset,
-                    .atom = reloc.atom,
+                    .atom_index = reloc.atom_index,
                     .addend = reloc.addend,
                 });
             } else {
-                const value = symbol.atom.off + symbol.offset + reloc.addend;
+                const atom = self.getAtom(.di_atom, symbol.atom_index);
+                const value = atom.off + symbol.offset + reloc.addend;
                 log.debug("{x}: [() => {x}] (%{d}, '{}')", .{ reloc.offset, value, target, ty.fmtDebug() });
                 mem.writeInt(
                     u32,
@@ -1275,10 +1272,11 @@ pub fn commitDeclState(
                 );
             }
         } else {
+            const atom = self.getAtom(.di_atom, reloc.atom_index);
             mem.writeInt(
                 u32,
                 dbg_info_buffer.items[reloc.offset..][0..@sizeOf(u32)],
-                reloc.atom.off + reloc.offset + reloc.addend,
+                atom.off + reloc.offset + reloc.addend,
                 target_endian,
             );
         }
@@ -1294,7 +1292,7 @@ pub fn commitDeclState(
                         .got_load => .got_load,
                     },
                     .target = reloc.target,
-                    .offset = reloc.offset + atom.off,
+                    .offset = reloc.offset + self.getAtom(.di_atom, di_atom_index).off,
                     .addend = 0,
                     .prev_vaddr = 0,
                 });
@@ -1304,10 +1302,10 @@ pub fn commitDeclState(
     }
 
     log.debug("writeDeclDebugInfo for '{s}", .{decl.name});
-    try self.writeDeclDebugInfo(atom, dbg_info_buffer.items);
+    try self.writeDeclDebugInfo(di_atom_index, dbg_info_buffer.items);
 }
 
-fn updateDeclDebugInfoAllocation(self: *Dwarf, atom: *Atom, len: u32) !void {
+fn updateDeclDebugInfoAllocation(self: *Dwarf, atom_index: Atom.Index, len: u32) !void {
     const tracy = trace(@src());
     defer tracy.end();
 
@@ -1316,19 +1314,21 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, atom: *Atom, len: u32) !void {
     // probably need to edit that logic too.
     const gpa = self.allocator;
 
+    const atom = self.getAtomPtr(.di_atom, atom_index);
     atom.len = len;
-    if (self.atom_last) |last| blk: {
-        if (atom == last) break :blk;
-        if (atom.next) |next| {
+    if (self.di_atom_last_index) |last_index| blk: {
+        if (atom_index == last_index) break :blk;
+        if (atom.next_index) |next_index| {
+            const next = self.getAtomPtr(.di_atom, next_index);
             // Update existing Decl - non-last item.
             if (atom.off + atom.len + min_nop_size > next.off) {
                 // It grew too big, so we move it to a new location.
-                if (atom.prev) |prev| {
-                    self.atom_free_list.put(gpa, prev, {}) catch {};
-                    prev.next = atom.next;
+                if (atom.prev_index) |prev_index| {
+                    self.di_atom_free_list.put(gpa, prev_index, {}) catch {};
+                    self.getAtomPtr(.di_atom, prev_index).next_index = atom.next_index;
                 }
-                next.prev = atom.prev;
-                atom.next = null;
+                next.prev_index = atom.prev_index;
+                atom.next_index = null;
                 // Populate where it used to be with NOPs.
                 switch (self.bin_file.tag) {
                     .elf => {
@@ -1351,31 +1351,33 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, atom: *Atom, len: u32) !void {
                     else => unreachable,
                 }
                 // TODO Look at the free list before appending at the end.
-                atom.prev = last;
-                last.next = atom;
-                self.atom_last = atom;
+                atom.prev_index = last_index;
+                const last = self.getAtomPtr(.di_atom, last_index);
+                last.next_index = atom_index;
+                self.di_atom_last_index = atom_index;
 
                 atom.off = last.off + padToIdeal(last.len);
             }
-        } else if (atom.prev == null) {
+        } else if (atom.prev_index == null) {
             // Append new Decl.
             // TODO Look at the free list before appending at the end.
-            atom.prev = last;
-            last.next = atom;
-            self.atom_last = atom;
+            atom.prev_index = last_index;
+            const last = self.getAtomPtr(.di_atom, last_index);
+            last.next_index = atom_index;
+            self.di_atom_last_index = atom_index;
 
             atom.off = last.off + padToIdeal(last.len);
         }
     } else {
         // This is the first Decl of the .debug_info
-        self.atom_first = atom;
-        self.atom_last = atom;
+        self.di_atom_first_index = atom_index;
+        self.di_atom_last_index = atom_index;
 
         atom.off = @intCast(u32, padToIdeal(self.dbgInfoHeaderBytes()));
     }
 }
 
-fn writeDeclDebugInfo(self: *Dwarf, atom: *Atom, dbg_info_buf: []const u8) !void {
+fn writeDeclDebugInfo(self: *Dwarf, atom_index: Atom.Index, dbg_info_buf: []const u8) !void {
     const tracy = trace(@src());
     defer tracy.end();
 
@@ -1384,14 +1386,22 @@ fn writeDeclDebugInfo(self: *Dwarf, atom: *Atom, dbg_info_buf: []const u8) !void
     // probably need to edit that logic too.
     const gpa = self.allocator;
 
-    const last_decl = self.atom_last.?;
+    const atom = self.getAtom(.di_atom, atom_index);
+    const last_decl_index = self.di_atom_last_index.?;
+    const last_decl = self.getAtom(.di_atom, last_decl_index);
     // +1 for a trailing zero to end the children of the decl tag.
     const needed_size = last_decl.off + last_decl.len + 1;
-    const prev_padding_size: u32 = if (atom.prev) |prev| atom.off - (prev.off + prev.len) else 0;
-    const next_padding_size: u32 = if (atom.next) |next| next.off - (atom.off + atom.len) else 0;
+    const prev_padding_size: u32 = if (atom.prev_index) |prev_index| blk: {
+        const prev = self.getAtom(.di_atom, prev_index);
+        break :blk atom.off - (prev.off + prev.len);
+    } else 0;
+    const next_padding_size: u32 = if (atom.next_index) |next_index| blk: {
+        const next = self.getAtom(.di_atom, next_index);
+        break :blk next.off - (atom.off + atom.len);
+    } else 0;
 
     // To end the children of the decl tag.
-    const trailing_zero = atom.next == null;
+    const trailing_zero = atom.next_index == null;
 
     // We only have support for one compilation unit so far, so the offsets are directly
     // from the .debug_info section.
@@ -1459,10 +1469,15 @@ fn writeDeclDebugInfo(self: *Dwarf, atom: *Atom, dbg_info_buf: []const u8) !void
     }
 }
 
-pub fn updateDeclLineNumber(self: *Dwarf, decl: *const Module.Decl) !void {
+pub fn updateDeclLineNumber(self: *Dwarf, module: *Module, decl_index: Module.Decl.Index) !void {
     const tracy = trace(@src());
     defer tracy.end();
 
+    const atom_index = try self.getOrCreateAtomForDecl(.src_fn, decl_index);
+    const atom = self.getAtom(.src_fn, atom_index);
+    if (atom.len == 0) return;
+
+    const decl = module.declPtr(decl_index);
     const func = decl.val.castTag(.function).?.data;
     log.debug("decl.src_line={d}, func.lbrace_line={d}, func.rbrace_line={d}", .{
         decl.src_line,
@@ -1477,78 +1492,80 @@ pub fn updateDeclLineNumber(self: *Dwarf, decl: *const Module.Decl) !void {
         .elf => {
             const elf_file = self.bin_file.cast(File.Elf).?;
             const shdr = elf_file.sections.items(.shdr)[elf_file.debug_line_section_index.?];
-            const file_pos = shdr.sh_offset + decl.fn_link.elf.off + self.getRelocDbgLineOff();
+            const file_pos = shdr.sh_offset + atom.off + self.getRelocDbgLineOff();
             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 + decl.fn_link.macho.off + self.getRelocDbgLineOff();
+            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).?;
-            const offset = decl.fn_link.wasm.src_fn.off + self.getRelocDbgLineOff();
-            const atom = wasm_file.debug_line_atom.?;
-            mem.copy(u8, atom.code.items[offset..], &data);
+            const offset = atom.off + self.getRelocDbgLineOff();
+            const atom_ = wasm_file.debug_line_atom.?;
+            mem.copy(u8, atom_.code.items[offset..], &data);
         },
         else => unreachable,
     }
 }
 
-pub fn freeAtom(self: *Dwarf, atom: *Atom) void {
-    if (self.atom_first == atom) {
-        self.atom_first = atom.next;
-    }
-    if (self.atom_last == atom) {
-        // TODO shrink the .debug_info section size here
-        self.atom_last = atom.prev;
-    }
-
-    if (atom.prev) |prev| {
-        prev.next = atom.next;
+pub fn freeDecl(self: *Dwarf, decl_index: Module.Decl.Index) void {
+    const gpa = self.allocator;
 
-        // TODO the free list logic like we do for text blocks above
-    } else {
-        atom.prev = null;
+    // Free SrcFn atom
+    if (self.src_fn_decls.fetchRemove(decl_index)) |kv| {
+        const src_fn_index = kv.value;
+        const src_fn = self.getAtom(.src_fn, src_fn_index);
+        _ = self.src_fn_free_list.remove(src_fn_index);
+
+        if (src_fn.prev_index) |prev_index| {
+            self.src_fn_free_list.put(gpa, prev_index, {}) catch {};
+            const prev = self.getAtomPtr(.src_fn, prev_index);
+            prev.next_index = src_fn.next_index;
+            if (src_fn.next_index) |next_index| {
+                self.getAtomPtr(.src_fn, next_index).prev_index = prev_index;
+            } else {
+                self.src_fn_last_index = prev_index;
+            }
+        } else if (src_fn.next_index) |next_index| {
+            self.src_fn_first_index = next_index;
+            self.getAtomPtr(.src_fn, next_index).prev_index = null;
+        }
+        if (self.src_fn_first_index == src_fn_index) {
+            self.src_fn_first_index = src_fn.next_index;
+        }
+        if (self.src_fn_last_index == src_fn_index) {
+            self.src_fn_last_index = src_fn.prev_index;
+        }
     }
 
-    if (atom.next) |next| {
-        next.prev = atom.prev;
-    } else {
-        atom.next = null;
-    }
-}
+    // Free DI atom
+    if (self.di_atom_decls.fetchRemove(decl_index)) |kv| {
+        const di_atom_index = kv.value;
+        const di_atom = self.getAtomPtr(.di_atom, di_atom_index);
 
-pub fn freeDecl(self: *Dwarf, decl: *Module.Decl) void {
-    // TODO make this logic match freeTextBlock. Maybe abstract the logic out since the same thing
-    // is desired for both.
-    const gpa = self.allocator;
-    const fn_link = switch (self.bin_file.tag) {
-        .elf => &decl.fn_link.elf,
-        .macho => &decl.fn_link.macho,
-        .wasm => &decl.fn_link.wasm.src_fn,
-        else => unreachable,
-    };
-    _ = self.dbg_line_fn_free_list.remove(fn_link);
+        if (self.di_atom_first_index == di_atom_index) {
+            self.di_atom_first_index = di_atom.next_index;
+        }
+        if (self.di_atom_last_index == di_atom_index) {
+            // TODO shrink the .debug_info section size here
+            self.di_atom_last_index = di_atom.prev_index;
+        }
 
-    if (fn_link.prev) |prev| {
-        self.dbg_line_fn_free_list.put(gpa, prev, {}) catch {};
-        prev.next = fn_link.next;
-        if (fn_link.next) |next| {
-            next.prev = prev;
+        if (di_atom.prev_index) |prev_index| {
+            self.getAtomPtr(.di_atom, prev_index).next_index = di_atom.next_index;
+            // TODO the free list logic like we do for SrcFn above
         } else {
-            self.dbg_line_fn_last = prev;
+            di_atom.prev_index = null;
+        }
+
+        if (di_atom.next_index) |next_index| {
+            self.getAtomPtr(.di_atom, next_index).prev_index = di_atom.prev_index;
+        } else {
+            di_atom.next_index = null;
         }
-    } else if (fn_link.next) |next| {
-        self.dbg_line_fn_first = next;
-        next.prev = null;
-    }
-    if (self.dbg_line_fn_first == fn_link) {
-        self.dbg_line_fn_first = fn_link.next;
-    }
-    if (self.dbg_line_fn_last == fn_link) {
-        self.dbg_line_fn_last = fn_link.prev;
     }
 }
 
@@ -2276,10 +2293,14 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void {
         const needed_with_padding = padToIdeal(needed_bytes);
         const delta = needed_with_padding - dbg_line_prg_off;
 
-        var src_fn = self.dbg_line_fn_first.?;
-        const last_fn = self.dbg_line_fn_last.?;
+        const first_fn_index = self.src_fn_first_index.?;
+        const first_fn = self.getAtom(.src_fn, first_fn_index);
+        const last_fn_index = self.src_fn_last_index.?;
+        const last_fn = self.getAtom(.src_fn, last_fn_index);
+
+        var src_fn_index = first_fn_index;
 
-        var buffer = try gpa.alloc(u8, last_fn.off + last_fn.len - src_fn.off);
+        var buffer = try gpa.alloc(u8, last_fn.off + last_fn.len - first_fn.off);
         defer gpa.free(buffer);
 
         switch (self.bin_file.tag) {
@@ -2288,7 +2309,7 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void {
                 const shdr_index = elf_file.debug_line_section_index.?;
                 const needed_size = elf_file.sections.items(.shdr)[shdr_index].sh_size + delta;
                 try elf_file.growNonAllocSection(shdr_index, needed_size, 1, true);
-                const file_pos = elf_file.sections.items(.shdr)[shdr_index].sh_offset + src_fn.off;
+                const file_pos = elf_file.sections.items(.shdr)[shdr_index].sh_offset + first_fn.off;
 
                 const amt = try elf_file.base.file.?.preadAll(buffer, file_pos);
                 if (amt != buffer.len) return error.InputOutput;
@@ -2300,7 +2321,7 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void {
                 const sect_index = d_sym.debug_line_section_index.?;
                 const needed_size = @intCast(u32, d_sym.getSection(sect_index).size + delta);
                 try d_sym.growSection(sect_index, needed_size, true);
-                const file_pos = d_sym.getSection(sect_index).offset + src_fn.off;
+                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;
@@ -2310,18 +2331,19 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void {
             .wasm => {
                 const wasm_file = self.bin_file.cast(File.Wasm).?;
                 const debug_line = &wasm_file.debug_line_atom.?.code;
-                mem.copy(u8, buffer, debug_line.items[src_fn.off..]);
+                mem.copy(u8, buffer, debug_line.items[first_fn.off..]);
                 try debug_line.resize(self.allocator, debug_line.items.len + delta);
-                mem.copy(u8, debug_line.items[src_fn.off + delta ..], buffer);
+                mem.copy(u8, debug_line.items[first_fn.off + delta ..], buffer);
             },
             else => unreachable,
         }
 
         while (true) {
+            const src_fn = self.getAtomPtr(.src_fn, src_fn_index);
             src_fn.off += delta;
 
-            if (src_fn.next) |next| {
-                src_fn = next;
+            if (src_fn.next_index) |next_index| {
+                src_fn_index = next_index;
             } else break;
         }
     }
@@ -2367,22 +2389,26 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void {
 }
 
 fn getDebugInfoOff(self: Dwarf) ?u32 {
-    const first = self.atom_first orelse return null;
+    const first_index = self.di_atom_first_index orelse return null;
+    const first = self.getAtom(.di_atom, first_index);
     return first.off;
 }
 
 fn getDebugInfoEnd(self: Dwarf) ?u32 {
-    const last = self.atom_last orelse return null;
+    const last_index = self.di_atom_last_index orelse return null;
+    const last = self.getAtom(.di_atom, last_index);
     return last.off + last.len;
 }
 
 fn getDebugLineProgramOff(self: Dwarf) ?u32 {
-    const first = self.dbg_line_fn_first orelse return null;
+    const first_index = self.src_fn_first_index orelse return null;
+    const first = self.getAtom(.src_fn, first_index);
     return first.off;
 }
 
 fn getDebugLineProgramEnd(self: Dwarf) ?u32 {
-    const last = self.dbg_line_fn_last orelse return null;
+    const last_index = self.src_fn_last_index orelse return null;
+    const last = self.getAtom(.src_fn, last_index);
     return last.off + last.len;
 }
 
@@ -2457,23 +2483,14 @@ pub fn flushModule(self: *Dwarf, module: *Module) !void {
         }
         error_set.names = names;
 
-        const atom = try gpa.create(Atom);
-        errdefer gpa.destroy(atom);
-        atom.* = .{
-            .prev = null,
-            .next = null,
-            .off = 0,
-            .len = 0,
-        };
-
         var dbg_info_buffer = std.ArrayList(u8).init(arena);
         try addDbgInfoErrorSet(arena, module, error_ty, self.target, &dbg_info_buffer);
 
-        try self.managed_atoms.append(gpa, atom);
+        const di_atom_index = try self.createAtom(.di_atom);
         log.debug("updateDeclDebugInfoAllocation in flushModule", .{});
-        try self.updateDeclDebugInfoAllocation(atom, @intCast(u32, dbg_info_buffer.items.len));
+        try self.updateDeclDebugInfoAllocation(di_atom_index, @intCast(u32, dbg_info_buffer.items.len));
         log.debug("writeDeclDebugInfo in flushModule", .{});
-        try self.writeDeclDebugInfo(atom, dbg_info_buffer.items);
+        try self.writeDeclDebugInfo(di_atom_index, dbg_info_buffer.items);
 
         const file_pos = blk: {
             switch (self.bin_file.tag) {
@@ -2494,22 +2511,23 @@ pub fn flushModule(self: *Dwarf, module: *Module) !void {
         };
 
         var buf: [@sizeOf(u32)]u8 = undefined;
-        mem.writeInt(u32, &buf, atom.off, self.target.cpu.arch.endian());
+        mem.writeInt(u32, &buf, self.getAtom(.di_atom, di_atom_index).off, self.target.cpu.arch.endian());
 
         while (self.global_abbrev_relocs.popOrNull()) |reloc| {
+            const atom = self.getAtom(.di_atom, reloc.atom_index);
             switch (self.bin_file.tag) {
                 .elf => {
                     const elf_file = self.bin_file.cast(File.Elf).?;
-                    try elf_file.base.file.?.pwriteAll(&buf, file_pos + reloc.atom.off + reloc.offset);
+                    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 + reloc.atom.off + reloc.offset);
+                    try d_sym.file.pwriteAll(&buf, file_pos + atom.off + reloc.offset);
                 },
                 .wasm => {
                     const wasm_file = self.bin_file.cast(File.Wasm).?;
                     const debug_info = wasm_file.debug_info_atom.?.code;
-                    mem.copy(u8, debug_info.items[reloc.atom.off + reloc.offset ..], &buf);
+                    mem.copy(u8, debug_info.items[atom.off + reloc.offset ..], &buf);
                 },
                 else => unreachable,
             }
@@ -2627,12 +2645,62 @@ fn addDbgInfoErrorSet(
     try dbg_info_buffer.append(0);
 }
 
-fn getDbgInfoAtom(tag: File.Tag, mod: *Module, decl_index: Module.Decl.Index) *Atom {
-    const decl = mod.declPtr(decl_index);
-    return switch (tag) {
-        .elf => unreachable,
-        .macho => unreachable,
-        .wasm => &decl.link.wasm.dbg_info_atom,
-        else => unreachable,
+const Kind = enum { src_fn, di_atom };
+
+fn createAtom(self: *Dwarf, comptime kind: Kind) !Atom.Index {
+    const index = blk: {
+        switch (kind) {
+            .src_fn => {
+                const index = @intCast(Atom.Index, self.src_fns.items.len);
+                _ = try self.src_fns.addOne(self.allocator);
+                break :blk index;
+            },
+            .di_atom => {
+                const index = @intCast(Atom.Index, self.di_atoms.items.len);
+                _ = try self.di_atoms.addOne(self.allocator);
+                break :blk index;
+            },
+        }
+    };
+    const atom = self.getAtomPtr(kind, index);
+    atom.* = .{
+        .off = 0,
+        .len = 0,
+        .prev_index = null,
+        .next_index = null,
+    };
+    return index;
+}
+
+fn getOrCreateAtomForDecl(self: *Dwarf, comptime kind: Kind, decl_index: Module.Decl.Index) !Atom.Index {
+    switch (kind) {
+        .src_fn => {
+            const gop = try self.src_fn_decls.getOrPut(self.allocator, decl_index);
+            if (!gop.found_existing) {
+                gop.value_ptr.* = try self.createAtom(kind);
+            }
+            return gop.value_ptr.*;
+        },
+        .di_atom => {
+            const gop = try self.di_atom_decls.getOrPut(self.allocator, decl_index);
+            if (!gop.found_existing) {
+                gop.value_ptr.* = try self.createAtom(kind);
+            }
+            return gop.value_ptr.*;
+        },
+    }
+}
+
+fn getAtom(self: *const Dwarf, comptime kind: Kind, index: Atom.Index) Atom {
+    return switch (kind) {
+        .src_fn => self.src_fns.items[index],
+        .di_atom => self.di_atoms.items[index],
+    };
+}
+
+fn getAtomPtr(self: *Dwarf, comptime kind: Kind, index: Atom.Index) *Atom {
+    return switch (kind) {
+        .src_fn => &self.src_fns.items[index],
+        .di_atom => &self.di_atoms.items[index],
     };
 }
src/link/Elf.zig
@@ -344,9 +344,9 @@ pub fn deinit(self: *Elf) void {
         self.relocs.deinit(gpa);
     }
 
-    // if (self.dwarf) |*dw| {
-    //     dw.deinit();
-    // }
+    if (self.dwarf) |*dw| {
+        dw.deinit();
+    }
 }
 
 pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: File.RelocInfo) !u64 {
@@ -685,146 +685,146 @@ pub fn populateMissingMetadata(self: *Elf) !void {
         try self.writeSymbol(0);
     }
 
-    // if (self.dwarf) |*dw| {
-    //     if (self.debug_str_section_index == null) {
-    //         self.debug_str_section_index = @intCast(u16, self.sections.slice().len);
-    //         assert(dw.strtab.buffer.items.len == 0);
-    //         try dw.strtab.buffer.append(gpa, 0);
-    //         try self.sections.append(gpa, .{
-    //             .shdr = .{
-    //                 .sh_name = try self.shstrtab.insert(gpa, ".debug_str"),
-    //                 .sh_type = elf.SHT_PROGBITS,
-    //                 .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS,
-    //                 .sh_addr = 0,
-    //                 .sh_offset = 0,
-    //                 .sh_size = 0,
-    //                 .sh_link = 0,
-    //                 .sh_info = 0,
-    //                 .sh_addralign = 1,
-    //                 .sh_entsize = 1,
-    //             },
-    //             .phdr_index = undefined,
-    //         });
-    //         self.debug_strtab_dirty = true;
-    //         self.shdr_table_dirty = true;
-    //     }
-
-    //     if (self.debug_info_section_index == null) {
-    //         self.debug_info_section_index = @intCast(u16, self.sections.slice().len);
-
-    //         const file_size_hint = 200;
-    //         const p_align = 1;
-    //         const off = self.findFreeSpace(file_size_hint, p_align);
-    //         log.debug("found .debug_info free space 0x{x} to 0x{x}", .{
-    //             off,
-    //             off + file_size_hint,
-    //         });
-    //         try self.sections.append(gpa, .{
-    //             .shdr = .{
-    //                 .sh_name = try self.shstrtab.insert(gpa, ".debug_info"),
-    //                 .sh_type = elf.SHT_PROGBITS,
-    //                 .sh_flags = 0,
-    //                 .sh_addr = 0,
-    //                 .sh_offset = off,
-    //                 .sh_size = file_size_hint,
-    //                 .sh_link = 0,
-    //                 .sh_info = 0,
-    //                 .sh_addralign = p_align,
-    //                 .sh_entsize = 0,
-    //             },
-    //             .phdr_index = undefined,
-    //         });
-    //         self.shdr_table_dirty = true;
-    //         self.debug_info_header_dirty = true;
-    //     }
-
-    //     if (self.debug_abbrev_section_index == null) {
-    //         self.debug_abbrev_section_index = @intCast(u16, self.sections.slice().len);
-
-    //         const file_size_hint = 128;
-    //         const p_align = 1;
-    //         const off = self.findFreeSpace(file_size_hint, p_align);
-    //         log.debug("found .debug_abbrev free space 0x{x} to 0x{x}", .{
-    //             off,
-    //             off + file_size_hint,
-    //         });
-    //         try self.sections.append(gpa, .{
-    //             .shdr = .{
-    //                 .sh_name = try self.shstrtab.insert(gpa, ".debug_abbrev"),
-    //                 .sh_type = elf.SHT_PROGBITS,
-    //                 .sh_flags = 0,
-    //                 .sh_addr = 0,
-    //                 .sh_offset = off,
-    //                 .sh_size = file_size_hint,
-    //                 .sh_link = 0,
-    //                 .sh_info = 0,
-    //                 .sh_addralign = p_align,
-    //                 .sh_entsize = 0,
-    //             },
-    //             .phdr_index = undefined,
-    //         });
-    //         self.shdr_table_dirty = true;
-    //         self.debug_abbrev_section_dirty = true;
-    //     }
-
-    //     if (self.debug_aranges_section_index == null) {
-    //         self.debug_aranges_section_index = @intCast(u16, self.sections.slice().len);
-
-    //         const file_size_hint = 160;
-    //         const p_align = 16;
-    //         const off = self.findFreeSpace(file_size_hint, p_align);
-    //         log.debug("found .debug_aranges free space 0x{x} to 0x{x}", .{
-    //             off,
-    //             off + file_size_hint,
-    //         });
-    //         try self.sections.append(gpa, .{
-    //             .shdr = .{
-    //                 .sh_name = try self.shstrtab.insert(gpa, ".debug_aranges"),
-    //                 .sh_type = elf.SHT_PROGBITS,
-    //                 .sh_flags = 0,
-    //                 .sh_addr = 0,
-    //                 .sh_offset = off,
-    //                 .sh_size = file_size_hint,
-    //                 .sh_link = 0,
-    //                 .sh_info = 0,
-    //                 .sh_addralign = p_align,
-    //                 .sh_entsize = 0,
-    //             },
-    //             .phdr_index = undefined,
-    //         });
-    //         self.shdr_table_dirty = true;
-    //         self.debug_aranges_section_dirty = true;
-    //     }
-
-    //     if (self.debug_line_section_index == null) {
-    //         self.debug_line_section_index = @intCast(u16, self.sections.slice().len);
-
-    //         const file_size_hint = 250;
-    //         const p_align = 1;
-    //         const off = self.findFreeSpace(file_size_hint, p_align);
-    //         log.debug("found .debug_line free space 0x{x} to 0x{x}", .{
-    //             off,
-    //             off + file_size_hint,
-    //         });
-    //         try self.sections.append(gpa, .{
-    //             .shdr = .{
-    //                 .sh_name = try self.shstrtab.insert(gpa, ".debug_line"),
-    //                 .sh_type = elf.SHT_PROGBITS,
-    //                 .sh_flags = 0,
-    //                 .sh_addr = 0,
-    //                 .sh_offset = off,
-    //                 .sh_size = file_size_hint,
-    //                 .sh_link = 0,
-    //                 .sh_info = 0,
-    //                 .sh_addralign = p_align,
-    //                 .sh_entsize = 0,
-    //             },
-    //             .phdr_index = undefined,
-    //         });
-    //         self.shdr_table_dirty = true;
-    //         self.debug_line_header_dirty = true;
-    //     }
-    // }
+    if (self.dwarf) |*dw| {
+        if (self.debug_str_section_index == null) {
+            self.debug_str_section_index = @intCast(u16, self.sections.slice().len);
+            assert(dw.strtab.buffer.items.len == 0);
+            try dw.strtab.buffer.append(gpa, 0);
+            try self.sections.append(gpa, .{
+                .shdr = .{
+                    .sh_name = try self.shstrtab.insert(gpa, ".debug_str"),
+                    .sh_type = elf.SHT_PROGBITS,
+                    .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS,
+                    .sh_addr = 0,
+                    .sh_offset = 0,
+                    .sh_size = 0,
+                    .sh_link = 0,
+                    .sh_info = 0,
+                    .sh_addralign = 1,
+                    .sh_entsize = 1,
+                },
+                .phdr_index = undefined,
+            });
+            self.debug_strtab_dirty = true;
+            self.shdr_table_dirty = true;
+        }
+
+        if (self.debug_info_section_index == null) {
+            self.debug_info_section_index = @intCast(u16, self.sections.slice().len);
+
+            const file_size_hint = 200;
+            const p_align = 1;
+            const off = self.findFreeSpace(file_size_hint, p_align);
+            log.debug("found .debug_info free space 0x{x} to 0x{x}", .{
+                off,
+                off + file_size_hint,
+            });
+            try self.sections.append(gpa, .{
+                .shdr = .{
+                    .sh_name = try self.shstrtab.insert(gpa, ".debug_info"),
+                    .sh_type = elf.SHT_PROGBITS,
+                    .sh_flags = 0,
+                    .sh_addr = 0,
+                    .sh_offset = off,
+                    .sh_size = file_size_hint,
+                    .sh_link = 0,
+                    .sh_info = 0,
+                    .sh_addralign = p_align,
+                    .sh_entsize = 0,
+                },
+                .phdr_index = undefined,
+            });
+            self.shdr_table_dirty = true;
+            self.debug_info_header_dirty = true;
+        }
+
+        if (self.debug_abbrev_section_index == null) {
+            self.debug_abbrev_section_index = @intCast(u16, self.sections.slice().len);
+
+            const file_size_hint = 128;
+            const p_align = 1;
+            const off = self.findFreeSpace(file_size_hint, p_align);
+            log.debug("found .debug_abbrev free space 0x{x} to 0x{x}", .{
+                off,
+                off + file_size_hint,
+            });
+            try self.sections.append(gpa, .{
+                .shdr = .{
+                    .sh_name = try self.shstrtab.insert(gpa, ".debug_abbrev"),
+                    .sh_type = elf.SHT_PROGBITS,
+                    .sh_flags = 0,
+                    .sh_addr = 0,
+                    .sh_offset = off,
+                    .sh_size = file_size_hint,
+                    .sh_link = 0,
+                    .sh_info = 0,
+                    .sh_addralign = p_align,
+                    .sh_entsize = 0,
+                },
+                .phdr_index = undefined,
+            });
+            self.shdr_table_dirty = true;
+            self.debug_abbrev_section_dirty = true;
+        }
+
+        if (self.debug_aranges_section_index == null) {
+            self.debug_aranges_section_index = @intCast(u16, self.sections.slice().len);
+
+            const file_size_hint = 160;
+            const p_align = 16;
+            const off = self.findFreeSpace(file_size_hint, p_align);
+            log.debug("found .debug_aranges free space 0x{x} to 0x{x}", .{
+                off,
+                off + file_size_hint,
+            });
+            try self.sections.append(gpa, .{
+                .shdr = .{
+                    .sh_name = try self.shstrtab.insert(gpa, ".debug_aranges"),
+                    .sh_type = elf.SHT_PROGBITS,
+                    .sh_flags = 0,
+                    .sh_addr = 0,
+                    .sh_offset = off,
+                    .sh_size = file_size_hint,
+                    .sh_link = 0,
+                    .sh_info = 0,
+                    .sh_addralign = p_align,
+                    .sh_entsize = 0,
+                },
+                .phdr_index = undefined,
+            });
+            self.shdr_table_dirty = true;
+            self.debug_aranges_section_dirty = true;
+        }
+
+        if (self.debug_line_section_index == null) {
+            self.debug_line_section_index = @intCast(u16, self.sections.slice().len);
+
+            const file_size_hint = 250;
+            const p_align = 1;
+            const off = self.findFreeSpace(file_size_hint, p_align);
+            log.debug("found .debug_line free space 0x{x} to 0x{x}", .{
+                off,
+                off + file_size_hint,
+            });
+            try self.sections.append(gpa, .{
+                .shdr = .{
+                    .sh_name = try self.shstrtab.insert(gpa, ".debug_line"),
+                    .sh_type = elf.SHT_PROGBITS,
+                    .sh_flags = 0,
+                    .sh_addr = 0,
+                    .sh_offset = off,
+                    .sh_size = file_size_hint,
+                    .sh_link = 0,
+                    .sh_info = 0,
+                    .sh_addralign = p_align,
+                    .sh_entsize = 0,
+                },
+                .phdr_index = undefined,
+            });
+            self.shdr_table_dirty = true;
+            self.debug_line_header_dirty = true;
+        }
+    }
 
     const shsize: u64 = switch (self.ptr_width) {
         .p32 => @sizeOf(elf.Elf32_Shdr),
@@ -956,26 +956,25 @@ pub fn growNonAllocSection(
 }
 
 pub fn markDirty(self: *Elf, shdr_index: u16, phdr_index: ?u16) void {
-    _ = shdr_index;
     self.shdr_table_dirty = true; // TODO look into only writing one section
 
     if (phdr_index) |_| {
         self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
     }
 
-    // if (self.dwarf) |_| {
-    //     if (self.debug_info_section_index.? == shdr_index) {
-    //         self.debug_info_header_dirty = true;
-    //     } else if (self.debug_line_section_index.? == shdr_index) {
-    //         self.debug_line_header_dirty = true;
-    //     } else if (self.debug_abbrev_section_index.? == shdr_index) {
-    //         self.debug_abbrev_section_dirty = true;
-    //     } else if (self.debug_str_section_index.? == shdr_index) {
-    //         self.debug_strtab_dirty = true;
-    //     } else if (self.debug_aranges_section_index.? == shdr_index) {
-    //         self.debug_aranges_section_dirty = true;
-    //     }
-    // }
+    if (self.dwarf) |_| {
+        if (self.debug_info_section_index.? == shdr_index) {
+            self.debug_info_header_dirty = true;
+        } else if (self.debug_line_section_index.? == shdr_index) {
+            self.debug_line_header_dirty = true;
+        } else if (self.debug_abbrev_section_index.? == shdr_index) {
+            self.debug_abbrev_section_dirty = true;
+        } else if (self.debug_str_section_index.? == shdr_index) {
+            self.debug_strtab_dirty = true;
+        } else if (self.debug_aranges_section_index.? == shdr_index) {
+            self.debug_aranges_section_dirty = true;
+        }
+    }
 }
 
 pub fn flush(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void {
@@ -1015,14 +1014,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
     // TODO This linker code currently assumes there is only 1 compilation unit and it
     // corresponds to the Zig source code.
     const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented;
-    _ = module;
 
     const target_endian = self.base.options.target.cpu.arch.endian();
     const foreign_endian = target_endian != builtin.cpu.arch.endian();
 
-    // if (self.dwarf) |*dw| {
-    //     try dw.flushModule(module);
-    // }
+    if (self.dwarf) |*dw| {
+        try dw.flushModule(module);
+    }
 
     {
         var it = self.relocs.iterator();
@@ -1068,43 +1066,43 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
         self.logSymtab();
     }
 
-    // if (self.dwarf) |*dw| {
-    //     if (self.debug_abbrev_section_dirty) {
-    //         try dw.writeDbgAbbrev();
-    //         if (!self.shdr_table_dirty) {
-    //             // Then it won't get written with the others and we need to do it.
-    //             try self.writeSectHeader(self.debug_abbrev_section_index.?);
-    //         }
-    //         self.debug_abbrev_section_dirty = false;
-    //     }
-
-    //     if (self.debug_info_header_dirty) {
-    //         // Currently only one compilation unit is supported, so the address range is simply
-    //         // identical to the main program header virtual address and memory size.
-    //         const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?];
-    //         const low_pc = text_phdr.p_vaddr;
-    //         const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz;
-    //         try dw.writeDbgInfoHeader(module, low_pc, high_pc);
-    //         self.debug_info_header_dirty = false;
-    //     }
-
-    //     if (self.debug_aranges_section_dirty) {
-    //         // Currently only one compilation unit is supported, so the address range is simply
-    //         // identical to the main program header virtual address and memory size.
-    //         const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?];
-    //         try dw.writeDbgAranges(text_phdr.p_vaddr, text_phdr.p_memsz);
-    //         if (!self.shdr_table_dirty) {
-    //             // Then it won't get written with the others and we need to do it.
-    //             try self.writeSectHeader(self.debug_aranges_section_index.?);
-    //         }
-    //         self.debug_aranges_section_dirty = false;
-    //     }
-
-    //     if (self.debug_line_header_dirty) {
-    //         try dw.writeDbgLineHeader();
-    //         self.debug_line_header_dirty = false;
-    //     }
-    // }
+    if (self.dwarf) |*dw| {
+        if (self.debug_abbrev_section_dirty) {
+            try dw.writeDbgAbbrev();
+            if (!self.shdr_table_dirty) {
+                // Then it won't get written with the others and we need to do it.
+                try self.writeSectHeader(self.debug_abbrev_section_index.?);
+            }
+            self.debug_abbrev_section_dirty = false;
+        }
+
+        if (self.debug_info_header_dirty) {
+            // Currently only one compilation unit is supported, so the address range is simply
+            // identical to the main program header virtual address and memory size.
+            const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?];
+            const low_pc = text_phdr.p_vaddr;
+            const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz;
+            try dw.writeDbgInfoHeader(module, low_pc, high_pc);
+            self.debug_info_header_dirty = false;
+        }
+
+        if (self.debug_aranges_section_dirty) {
+            // Currently only one compilation unit is supported, so the address range is simply
+            // identical to the main program header virtual address and memory size.
+            const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?];
+            try dw.writeDbgAranges(text_phdr.p_vaddr, text_phdr.p_memsz);
+            if (!self.shdr_table_dirty) {
+                // Then it won't get written with the others and we need to do it.
+                try self.writeSectHeader(self.debug_aranges_section_index.?);
+            }
+            self.debug_aranges_section_dirty = false;
+        }
+
+        if (self.debug_line_header_dirty) {
+            try dw.writeDbgLineHeader();
+            self.debug_line_header_dirty = false;
+        }
+    }
 
     if (self.phdr_table_dirty) {
         const phsize: u64 = switch (self.ptr_width) {
@@ -1162,15 +1160,15 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
         }
     }
 
-    // if (self.dwarf) |dwarf| {
-    //     const shdr_index = self.debug_str_section_index.?;
-    //     if (self.debug_strtab_dirty or dwarf.strtab.buffer.items.len != self.sections.items(.shdr)[shdr_index].sh_size) {
-    //         try self.growNonAllocSection(shdr_index, dwarf.strtab.buffer.items.len, 1, false);
-    //         const debug_strtab_sect = self.sections.items(.shdr)[shdr_index];
-    //         try self.base.file.?.pwriteAll(dwarf.strtab.buffer.items, debug_strtab_sect.sh_offset);
-    //         self.debug_strtab_dirty = false;
-    //     }
-    // }
+    if (self.dwarf) |dwarf| {
+        const shdr_index = self.debug_str_section_index.?;
+        if (self.debug_strtab_dirty or dwarf.strtab.buffer.items.len != self.sections.items(.shdr)[shdr_index].sh_size) {
+            try self.growNonAllocSection(shdr_index, dwarf.strtab.buffer.items.len, 1, false);
+            const debug_strtab_sect = self.sections.items(.shdr)[shdr_index];
+            try self.base.file.?.pwriteAll(dwarf.strtab.buffer.items, debug_strtab_sect.sh_offset);
+            self.debug_strtab_dirty = false;
+        }
+    }
 
     if (self.shdr_table_dirty) {
         const shsize: u64 = switch (self.ptr_width) {
@@ -2100,10 +2098,6 @@ fn freeAtom(self: *Elf, atom_index: Atom.Index) void {
     self.getAtomPtr(atom_index).local_sym_index = 0;
 
     self.offset_table_free_list.append(self.base.allocator, atom.offset_table_index) catch {};
-
-    // if (self.dwarf) |*dw| {
-    //     dw.freeAtom(&atom.dbg_info_atom);
-    // }
 }
 
 fn shrinkAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64) void {
@@ -2133,7 +2127,6 @@ pub fn createAtom(self: *Elf) !Atom.Index {
         .offset_table_index = offset_table_index,
         .prev_index = null,
         .next_index = null,
-        .dbg_info_atom = undefined,
     };
     log.debug("creating ATOM(%{d}) at index {d}", .{ local_sym_index, atom_index });
     return atom_index;
@@ -2219,16 +2212,16 @@ fn allocateAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64, alignme
         try self.growAllocSection(sym.st_shndx, needed_size);
         maybe_last_atom_index.* = atom_index;
 
-        // if (self.dwarf) |_| {
-        //     // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address
-        //     // range of the compilation unit. When we expand the text section, this range changes,
-        //     // so the DW_TAG.compile_unit tag of the .debug_info section becomes dirty.
-        //     self.debug_info_header_dirty = true;
-        //     // This becomes dirty for the same reason. We could potentially make this more
-        //     // fine-grained with the addition of support for more compilation units. It is planned to
-        //     // model each package as a different compilation unit.
-        //     self.debug_aranges_section_dirty = true;
-        // }
+        if (self.dwarf) |_| {
+            // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address
+            // range of the compilation unit. When we expand the text section, this range changes,
+            // so the DW_TAG.compile_unit tag of the .debug_info section becomes dirty.
+            self.debug_info_header_dirty = true;
+            // This becomes dirty for the same reason. We could potentially make this more
+            // fine-grained with the addition of support for more compilation units. It is planned to
+            // model each package as a different compilation unit.
+            self.debug_aranges_section_dirty = true;
+        }
     }
     shdr.sh_addralign = math.max(shdr.sh_addralign, alignment);
 
@@ -2333,9 +2326,9 @@ pub fn freeDecl(self: *Elf, decl_index: Module.Decl.Index) void {
         kv.value.exports.deinit(self.base.allocator);
     }
 
-    // if (self.dwarf) |*dw| {
-    //     dw.freeDecl(decl);
-    // }
+    if (self.dwarf) |*dw| {
+        dw.freeDecl(decl_index);
+    }
 }
 
 pub fn getOrCreateAtomForDecl(self: *Elf, decl_index: Module.Decl.Index) !Atom.Index {
@@ -2471,15 +2464,15 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven
     var code_buffer = std.ArrayList(u8).init(self.base.allocator);
     defer code_buffer.deinit();
 
-    // var decl_state: ?Dwarf.DeclState = if (self.dwarf) |*dw| try dw.initDeclState(module, decl_index) else null;
-    // defer if (decl_state) |*ds| ds.deinit();
+    var decl_state: ?Dwarf.DeclState = if (self.dwarf) |*dw| try dw.initDeclState(module, decl_index) else null;
+    defer if (decl_state) |*ds| ds.deinit();
 
-    // const res = if (decl_state) |*ds|
-    //     try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .{
-    //         .dwarf = ds,
-    //     })
-    // else
-    const res = try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none);
+    const res = if (decl_state) |*ds|
+        try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .{
+            .dwarf = ds,
+        })
+    else
+        try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none);
 
     const code = switch (res) {
         .ok => code_buffer.items,
@@ -2490,16 +2483,15 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven
         },
     };
     const local_sym = try self.updateDeclCode(decl_index, code, elf.STT_FUNC);
-    _ = local_sym;
-    // if (decl_state) |*ds| {
-    //     try self.dwarf.?.commitDeclState(
-    //         module,
-    //         decl_index,
-    //         local_sym.st_value,
-    //         local_sym.st_size,
-    //         ds,
-    //     );
-    // }
+    if (decl_state) |*ds| {
+        try self.dwarf.?.commitDeclState(
+            module,
+            decl_index,
+            local_sym.st_value,
+            local_sym.st_size,
+            ds,
+        );
+    }
 
     // Since we updated the vaddr and the size, each corresponding export
     // symbol also needs to be updated.
@@ -2536,27 +2528,27 @@ pub fn updateDecl(self: *Elf, module: *Module, decl_index: Module.Decl.Index) !v
     var code_buffer = std.ArrayList(u8).init(self.base.allocator);
     defer code_buffer.deinit();
 
-    // var decl_state: ?Dwarf.DeclState = if (self.dwarf) |*dw| try dw.initDeclState(module, decl_index) else null;
-    // defer if (decl_state) |*ds| ds.deinit();
+    var decl_state: ?Dwarf.DeclState = if (self.dwarf) |*dw| try dw.initDeclState(module, decl_index) else null;
+    defer if (decl_state) |*ds| ds.deinit();
 
     // TODO implement .debug_info for global variables
     const decl_val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val;
-    // const res = if (decl_state) |*ds|
-    //     try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
-    //         .ty = decl.ty,
-    //         .val = decl_val,
-    //     }, &code_buffer, .{
-    //         .dwarf = ds,
-    //     }, .{
-    //         .parent_atom_index = atom.getSymbolIndex().?,
-    //     })
-    // else
-    const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
-        .ty = decl.ty,
-        .val = decl_val,
-    }, &code_buffer, .none, .{
-        .parent_atom_index = atom.getSymbolIndex().?,
-    });
+    const res = if (decl_state) |*ds|
+        try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
+            .ty = decl.ty,
+            .val = decl_val,
+        }, &code_buffer, .{
+            .dwarf = ds,
+        }, .{
+            .parent_atom_index = atom.getSymbolIndex().?,
+        })
+    else
+        try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
+            .ty = decl.ty,
+            .val = decl_val,
+        }, &code_buffer, .none, .{
+            .parent_atom_index = atom.getSymbolIndex().?,
+        });
 
     const code = switch (res) {
         .ok => code_buffer.items,
@@ -2568,16 +2560,15 @@ pub fn updateDecl(self: *Elf, module: *Module, decl_index: Module.Decl.Index) !v
     };
 
     const local_sym = try self.updateDeclCode(decl_index, code, elf.STT_OBJECT);
-    _ = local_sym;
-    // if (decl_state) |*ds| {
-    //     try self.dwarf.?.commitDeclState(
-    //         module,
-    //         decl_index,
-    //         local_sym.st_value,
-    //         local_sym.st_size,
-    //         ds,
-    //     );
-    // }
+    if (decl_state) |*ds| {
+        try self.dwarf.?.commitDeclState(
+            module,
+            decl_index,
+            local_sym.st_value,
+            local_sym.st_size,
+            ds,
+        );
+    }
 
     // Since we updated the vaddr and the size, each corresponding export
     // symbol also needs to be updated.
@@ -2737,19 +2728,20 @@ pub fn updateDeclExports(
 }
 
 /// Must be called only after a successful call to `updateDecl`.
-pub fn updateDeclLineNumber(self: *Elf, mod: *Module, decl: *const Module.Decl) !void {
+pub fn updateDeclLineNumber(self: *Elf, mod: *Module, decl_index: Module.Decl.Index) !void {
     const tracy = trace(@src());
     defer tracy.end();
 
+    const decl = mod.declPtr(decl_index);
     const decl_name = try decl.getFullyQualifiedName(mod);
     defer self.base.allocator.free(decl_name);
 
     log.debug("updateDeclLineNumber {s}{*}", .{ decl_name, decl });
 
     if (self.llvm_object) |_| return;
-    // if (self.dwarf) |*dw| {
-    //     try dw.updateDeclLineNumber(decl);
-    // }
+    if (self.dwarf) |*dw| {
+        try dw.updateDeclLineNumber(mod, decl_index);
+    }
 }
 
 pub fn deleteDeclExport(self: *Elf, decl_index: Module.Decl.Index, name: []const u8) void {
src/link/MachO.zig
@@ -472,9 +472,9 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
 
     const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented;
 
-    // if (self.d_sym) |*d_sym| {
-    //     try d_sym.dwarf.flushModule(module);
-    // }
+    if (self.d_sym) |*d_sym| {
+        try d_sym.dwarf.flushModule(module);
+    }
 
     var libs = std.StringArrayHashMap(link.SystemLib).init(arena);
     try resolveLibSystem(
@@ -664,10 +664,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
         try self.writeCodeSignature(comp, csig); // code signing always comes last
     }
 
-    // if (self.d_sym) |*d_sym| {
-    //     // Flush debug symbols bundle.
-    //     try d_sym.flushModule(self);
-    // }
+    if (self.d_sym) |*d_sym| {
+        // Flush debug symbols bundle.
+        try d_sym.flushModule(self);
+    }
 
     // if (build_options.enable_link_snapshots) {
     //     if (self.base.options.enable_link_snapshots)
@@ -1089,7 +1089,6 @@ pub fn createAtom(self: *MachO) !Atom.Index {
         .alignment = 0,
         .prev_index = null,
         .next_index = null,
-        .dbg_info_atom = undefined,
     };
     log.debug("creating ATOM(%{d}) at index {d}", .{ sym_index, atom_index });
     return atom_index;
@@ -1724,9 +1723,9 @@ pub fn deinit(self: *MachO) void {
         if (self.llvm_object) |llvm_object| llvm_object.destroy(gpa);
     }
 
-    // if (self.d_sym) |*d_sym| {
-    //     d_sym.deinit();
-    // }
+    if (self.d_sym) |*d_sym| {
+        d_sym.deinit();
+    }
 
     self.got_entries.deinit(gpa);
     self.got_entries_free_list.deinit(gpa);
@@ -1804,9 +1803,8 @@ pub fn deinit(self: *MachO) void {
 }
 
 fn freeAtom(self: *MachO, atom_index: Atom.Index) void {
-    log.debug("freeAtom {d}", .{atom_index});
-
     const gpa = self.base.allocator;
+    log.debug("freeAtom {d}", .{atom_index});
 
     // Remove any relocs and base relocs associated with this Atom
     Atom.freeRelocations(self, atom_index);
@@ -1876,9 +1874,9 @@ fn freeAtom(self: *MachO, atom_index: Atom.Index) void {
         };
         _ = self.got_entries_table.remove(got_target);
 
-        // if (self.d_sym) |*d_sym| {
-        //     d_sym.swapRemoveRelocs(sym_index);
-        // }
+        if (self.d_sym) |*d_sym| {
+            d_sym.swapRemoveRelocs(sym_index);
+        }
 
         log.debug("  adding GOT index {d} to free list (target local@{d})", .{ got_index, sym_index });
     }
@@ -1887,10 +1885,6 @@ fn freeAtom(self: *MachO, atom_index: Atom.Index) void {
     _ = self.atom_by_index_table.remove(sym_index);
     log.debug("  adding local symbol index {d} to free list", .{sym_index});
     self.getAtomPtr(atom_index).sym_index = 0;
-
-    // if (self.d_sym) |*d_sym| {
-    //     d_sym.dwarf.freeAtom(&atom.dbg_info_atom);
-    // }
 }
 
 fn shrinkAtom(self: *MachO, atom_index: Atom.Index, new_block_size: u64) void {
@@ -2020,23 +2014,22 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv
     Atom.freeRelocations(self, atom_index);
 
     const atom = self.getAtom(atom_index);
-    _ = atom;
 
     var code_buffer = std.ArrayList(u8).init(self.base.allocator);
     defer code_buffer.deinit();
 
-    // var decl_state = if (self.d_sym) |*d_sym|
-    //     try d_sym.dwarf.initDeclState(module, decl_index)
-    // else
-    //     null;
-    // defer if (decl_state) |*ds| ds.deinit();
+    var decl_state = if (self.d_sym) |*d_sym|
+        try d_sym.dwarf.initDeclState(module, decl_index)
+    else
+        null;
+    defer if (decl_state) |*ds| ds.deinit();
 
-    // const res = if (decl_state) |*ds|
-    // try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .{
-    //     .dwarf = ds,
-    // })
-    // else
-    const res = try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none);
+    const res = if (decl_state) |*ds|
+        try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .{
+            .dwarf = ds,
+        })
+    else
+        try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none);
 
     const code = switch (res) {
         .ok => code_buffer.items,
@@ -2048,11 +2041,10 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv
     };
 
     const addr = try self.updateDeclCode(decl_index, code);
-    _ = addr;
 
-    // if (decl_state) |*ds| {
-    //     try self.d_sym.?.dwarf.commitDeclState(module, decl_index, addr, atom.size, ds);
-    // }
+    if (decl_state) |*ds| {
+        try self.d_sym.?.dwarf.commitDeclState(module, decl_index, addr, atom.size, ds);
+    }
 
     // Since we updated the vaddr and the size, each corresponding export symbol also
     // needs to be updated.
@@ -2154,29 +2146,29 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)
     var code_buffer = std.ArrayList(u8).init(self.base.allocator);
     defer code_buffer.deinit();
 
-    // var decl_state: ?Dwarf.DeclState = if (self.d_sym) |*d_sym|
-    //     try d_sym.dwarf.initDeclState(module, decl_index)
-    // else
-    //     null;
-    // defer if (decl_state) |*ds| ds.deinit();
+    var decl_state: ?Dwarf.DeclState = if (self.d_sym) |*d_sym|
+        try d_sym.dwarf.initDeclState(module, decl_index)
+    else
+        null;
+    defer if (decl_state) |*ds| ds.deinit();
 
     const decl_val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val;
-    // const res = if (decl_state) |*ds|
-    //     try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
-    //         .ty = decl.ty,
-    //         .val = decl_val,
-    //     }, &code_buffer, .{
-    //         .dwarf = ds,
-    //     }, .{
-    //         .parent_atom_index = atom.getSymbolIndex().?,
-    //     })
-    // else
-    const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
-        .ty = decl.ty,
-        .val = decl_val,
-    }, &code_buffer, .none, .{
-        .parent_atom_index = atom.getSymbolIndex().?,
-    });
+    const res = if (decl_state) |*ds|
+        try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
+            .ty = decl.ty,
+            .val = decl_val,
+        }, &code_buffer, .{
+            .dwarf = ds,
+        }, .{
+            .parent_atom_index = atom.getSymbolIndex().?,
+        })
+    else
+        try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
+            .ty = decl.ty,
+            .val = decl_val,
+        }, &code_buffer, .none, .{
+            .parent_atom_index = atom.getSymbolIndex().?,
+        });
 
     const code = switch (res) {
         .ok => code_buffer.items,
@@ -2187,11 +2179,10 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)
         },
     };
     const addr = try self.updateDeclCode(decl_index, code);
-    _ = addr;
 
-    // if (decl_state) |*ds| {
-    //     try self.d_sym.?.dwarf.commitDeclState(module, decl_index, addr, atom.size, ds);
-    // }
+    if (decl_state) |*ds| {
+        try self.d_sym.?.dwarf.commitDeclState(module, decl_index, addr, atom.size, ds);
+    }
 
     // Since we updated the vaddr and the size, each corresponding export symbol also
     // needs to be updated.
@@ -2432,13 +2423,10 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8)
     return atom.getSymbol(self).n_value;
 }
 
-pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {
-    _ = decl;
-    _ = self;
-    _ = module;
-    // if (self.d_sym) |*d_sym| {
-    //     try d_sym.dwarf.updateDeclLineNumber(decl);
-    // }
+pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl_index: Module.Decl.Index) !void {
+    if (self.d_sym) |*d_sym| {
+        try d_sym.dwarf.updateDeclLineNumber(module, decl_index);
+    }
 }
 
 pub fn updateDeclExports(
@@ -2611,9 +2599,9 @@ pub fn freeDecl(self: *MachO, decl_index: Module.Decl.Index) void {
         kv.value.exports.deinit(self.base.allocator);
     }
 
-    // if (self.d_sym) |*d_sym| {
-    //     d_sym.dwarf.freeDecl(decl);
-    // }
+    if (self.d_sym) |*d_sym| {
+        d_sym.dwarf.freeDecl(decl_index);
+    }
 }
 
 pub fn getDeclVAddr(self: *MachO, decl_index: Module.Decl.Index, reloc_info: File.RelocInfo) !u64 {
src/link/Plan9.zig
@@ -1018,10 +1018,10 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {
 }
 
 /// Must be called only after a successful call to `updateDecl`.
-pub fn updateDeclLineNumber(self: *Plan9, mod: *Module, decl: *const Module.Decl) !void {
+pub fn updateDeclLineNumber(self: *Plan9, mod: *Module, decl_index: Module.Decl.Index) !void {
     _ = self;
     _ = mod;
-    _ = decl;
+    _ = decl_index;
 }
 
 pub fn getDeclVAddr(
src/link/Wasm.zig
@@ -183,13 +183,9 @@ pub const Segment = struct {
 pub const FnData = struct {
     /// Reference to the wasm type that represents this function.
     type_index: u32,
-    /// Contains debug information related to this function.
-    /// For Wasm, the offset is relative to the code-section.
-    src_fn: Dwarf.SrcFn,
 
     pub const empty: FnData = .{
         .type_index = undefined,
-        .src_fn = Dwarf.SrcFn.empty,
     };
 };
 
@@ -1122,17 +1118,18 @@ pub fn updateDecl(wasm: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !voi
     return wasm.finishUpdateDecl(decl, code);
 }
 
-pub fn updateDeclLineNumber(wasm: *Wasm, mod: *Module, decl: *const Module.Decl) !void {
+pub fn updateDeclLineNumber(wasm: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !void {
     if (wasm.llvm_object) |_| return;
     if (wasm.dwarf) |*dw| {
         const tracy = trace(@src());
         defer tracy.end();
 
+        const decl = mod.declPtr(decl_index);
         const decl_name = try decl.getFullyQualifiedName(mod);
         defer wasm.base.allocator.free(decl_name);
 
         log.debug("updateDeclLineNumber {s}{*}", .{ decl_name, decl });
-        try dw.updateDeclLineNumber(decl);
+        try dw.updateDeclLineNumber(mod, decl_index);
     }
 }
 
@@ -1460,10 +1457,9 @@ pub fn freeDecl(wasm: *Wasm, decl_index: Module.Decl.Index) void {
     _ = wasm.resolved_symbols.swapRemove(atom.symbolLoc());
     _ = wasm.symbol_atom.remove(atom.symbolLoc());
 
-    if (wasm.dwarf) |*dwarf| {
-        dwarf.freeDecl(decl);
-        dwarf.freeAtom(&atom.dbg_info_atom);
-    }
+    // if (wasm.dwarf) |*dwarf| {
+    //     dwarf.freeDecl(decl_index);
+    // }
 
     atom.deinit(wasm.base.allocator);
 }
@@ -1882,7 +1878,6 @@ fn initializeCallCtorsFunction(wasm: *Wasm) !void {
         .next = null,
         .prev = null,
         .code = function_body.moveToUnmanaged(),
-        .dbg_info_atom = undefined,
     };
     try wasm.managed_atoms.append(wasm.base.allocator, atom);
     try wasm.appendAtomAtIndex(wasm.code_section_index.?, atom);
src/Compilation.zig
@@ -3299,7 +3299,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void {
             const gpa = comp.gpa;
             const module = comp.bin_file.options.module.?;
             const decl = module.declPtr(decl_index);
-            comp.bin_file.updateDeclLineNumber(module, decl) catch |err| {
+            comp.bin_file.updateDeclLineNumber(module, decl_index) catch |err| {
                 try module.failed_decls.ensureUnusedCapacity(gpa, 1);
                 module.failed_decls.putAssumeCapacityNoClobber(decl_index, try Module.ErrorMsg.create(
                     gpa,
src/link.zig
@@ -273,9 +273,9 @@ pub const File = struct {
     };
 
     pub const LinkFn = union {
-        elf: Dwarf.SrcFn,
-        coff: Coff.SrcFn,
-        macho: Dwarf.SrcFn,
+        elf: void,
+        coff: void,
+        macho: void,
         plan9: void,
         c: void,
         wasm: Wasm.FnData,
@@ -580,22 +580,23 @@ pub const File = struct {
         }
     }
 
-    pub fn updateDeclLineNumber(base: *File, module: *Module, decl: *Module.Decl) UpdateDeclError!void {
+    pub fn updateDeclLineNumber(base: *File, module: *Module, decl_index: Module.Decl.Index) UpdateDeclError!void {
+        const decl = module.declPtr(decl_index);
         log.debug("updateDeclLineNumber {*} ({s}), line={}", .{
             decl, decl.name, decl.src_line + 1,
         });
         assert(decl.has_tv);
         if (build_options.only_c) {
             assert(base.tag == .c);
-            return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl);
+            return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl_index);
         }
         switch (base.tag) {
-            .coff => return @fieldParentPtr(Coff, "base", base).updateDeclLineNumber(module, decl),
-            .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl),
-            .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl),
-            .c => return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl),
-            .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclLineNumber(module, decl),
-            .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDeclLineNumber(module, decl),
+            .coff => return @fieldParentPtr(Coff, "base", base).updateDeclLineNumber(module, decl_index),
+            .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl_index),
+            .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl_index),
+            .c => return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl_index),
+            .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclLineNumber(module, decl_index),
+            .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDeclLineNumber(module, decl_index),
             .spirv, .nvptx => {},
         }
     }
src/Module.zig
@@ -5186,12 +5186,12 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err
             .coff => {
                 // TODO Implement for COFF
             },
-            .elf => if (decl.fn_link.elf.len != 0) {
+            .elf => {
                 // TODO Look into detecting when this would be unnecessary by storing enough state
                 // in `Decl` to notice that the line number did not change.
                 comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index });
             },
-            .macho => if (decl.fn_link.macho.len != 0) {
+            .macho => {
                 // TODO Look into detecting when this would be unnecessary by storing enough state
                 // in `Decl` to notice that the line number did not change.
                 comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index });
@@ -5285,8 +5285,8 @@ pub fn clearDecl(
             };
             decl.fn_link = switch (mod.comp.bin_file.tag) {
                 .coff => .{ .coff = {} },
-                .elf => .{ .elf = link.File.Dwarf.SrcFn.empty },
-                .macho => .{ .macho = link.File.Dwarf.SrcFn.empty },
+                .elf => .{ .elf = {} },
+                .macho => .{ .macho = {} },
                 .plan9 => .{ .plan9 = {} },
                 .c => .{ .c = {} },
                 .wasm => .{ .wasm = link.File.Wasm.FnData.empty },
@@ -5705,8 +5705,8 @@ pub fn allocateNewDecl(
         },
         .fn_link = switch (mod.comp.bin_file.tag) {
             .coff => .{ .coff = {} },
-            .elf => .{ .elf = link.File.Dwarf.SrcFn.empty },
-            .macho => .{ .macho = link.File.Dwarf.SrcFn.empty },
+            .elf => .{ .elf = {} },
+            .macho => .{ .macho = {} },
             .plan9 => .{ .plan9 = {} },
             .c => .{ .c = {} },
             .wasm => .{ .wasm = link.File.Wasm.FnData.empty },