Commit e448fb9601

Jakub Konka <kubkon@jakubkonka.com>
2024-10-04 12:46:57
elf: change how we manage debug atoms in Dwarf linker
1 parent c92c72d
src/link/Elf/Atom.zig
@@ -118,10 +118,19 @@ pub fn capacity(self: Atom, elf_file: *Elf) u64 {
     return @intCast(next_addr - self.address(elf_file));
 }
 
+pub fn fileCapacity(self: Atom, elf_file: *Elf) u64 {
+    const self_off = self.offset(elf_file);
+    const next_off = if (self.nextAtom(elf_file)) |next_atom|
+        next_atom.offset(elf_file)
+    else
+        self_off + elf_file.allocatedSize(self_off);
+    return @intCast(next_off - self_off);
+}
+
 pub fn freeListEligible(self: Atom, elf_file: *Elf) bool {
     // No need to keep a free list node for the last block.
     const next = self.nextAtom(elf_file) orelse return false;
-    const cap: u64 = @intCast(next.address(elf_file) - self.address(elf_file));
+    const cap: u64 = @intCast(next.value - self.value);
     const ideal_cap = Elf.padToIdeal(self.size);
     if (cap <= ideal_cap) return false;
     const surplus = cap - ideal_cap;
src/link/Elf/relocatable.zig
@@ -394,24 +394,14 @@ fn allocateAllocSections(elf_file: *Elf) !void {
             shdr.sh_size = 0;
             const new_offset = try elf_file.findFreeSpace(needed_size, shdr.sh_addralign);
 
-            if (elf_file.zigObjectPtr()) |zo| blk: {
-                const existing_size = for ([_]?Symbol.Index{
-                    zo.text_index,
-                    zo.rodata_index,
-                    zo.data_relro_index,
-                    zo.data_index,
-                    zo.tdata_index,
-                    zo.eh_frame_index,
-                }) |maybe_sym_index| {
-                    const sect_sym_index = maybe_sym_index orelse continue;
-                    const sect_atom_ptr = zo.symbol(sect_sym_index).atom(elf_file).?;
-                    if (sect_atom_ptr.output_section_index == shndx) break sect_atom_ptr.size;
-                } else break :blk;
-                log.debug("moving {s} from 0x{x} to 0x{x}", .{
-                    elf_file.getShString(shdr.sh_name),
-                    shdr.sh_offset,
-                    new_offset,
-                });
+            log.debug("moving {s} from 0x{x} to 0x{x}", .{
+                elf_file.getShString(shdr.sh_name),
+                shdr.sh_offset,
+                new_offset,
+            });
+
+            if (shdr.sh_offset > 0) {
+                const existing_size = elf_file.sectionSize(@intCast(shndx));
                 const amt = try elf_file.base.file.?.copyRangeAll(
                     shdr.sh_offset,
                     elf_file.base.file.?,
src/link/Elf/ZigObject.zig
@@ -130,10 +130,10 @@ pub fn init(self: *ZigObject, elf_file: *Elf, options: InitOptions) !void {
                     .entsize = 1,
                     .type = elf.SHT_PROGBITS,
                     .addralign = 1,
+                    .offset = std.math.maxInt(u64),
                 });
                 self.debug_str_section_dirty = true;
                 self.debug_str_index = try addSectionSymbolWithAtom(self, gpa, ".debug_str", .@"1", osec);
-                elf_file.sections.items(.last_atom)[osec] = self.symbol(self.debug_str_index.?).ref;
             }
 
             if (self.debug_info_index == null) {
@@ -141,10 +141,10 @@ pub fn init(self: *ZigObject, elf_file: *Elf, options: InitOptions) !void {
                     .name = try elf_file.insertShString(".debug_info"),
                     .type = elf.SHT_PROGBITS,
                     .addralign = 1,
+                    .offset = std.math.maxInt(u64),
                 });
                 self.debug_info_section_dirty = true;
                 self.debug_info_index = try addSectionSymbolWithAtom(self, gpa, ".debug_info", .@"1", osec);
-                elf_file.sections.items(.last_atom)[osec] = self.symbol(self.debug_info_index.?).ref;
             }
 
             if (self.debug_abbrev_index == null) {
@@ -152,10 +152,10 @@ pub fn init(self: *ZigObject, elf_file: *Elf, options: InitOptions) !void {
                     .name = try elf_file.insertShString(".debug_abbrev"),
                     .type = elf.SHT_PROGBITS,
                     .addralign = 1,
+                    .offset = std.math.maxInt(u64),
                 });
                 self.debug_abbrev_section_dirty = true;
                 self.debug_abbrev_index = try addSectionSymbolWithAtom(self, gpa, ".debug_abbrev", .@"1", osec);
-                elf_file.sections.items(.last_atom)[osec] = self.symbol(self.debug_abbrev_index.?).ref;
             }
 
             if (self.debug_aranges_index == null) {
@@ -163,10 +163,10 @@ pub fn init(self: *ZigObject, elf_file: *Elf, options: InitOptions) !void {
                     .name = try elf_file.insertShString(".debug_aranges"),
                     .type = elf.SHT_PROGBITS,
                     .addralign = 16,
+                    .offset = std.math.maxInt(u64),
                 });
                 self.debug_aranges_section_dirty = true;
                 self.debug_aranges_index = try addSectionSymbolWithAtom(self, gpa, ".debug_aranges", .@"16", osec);
-                elf_file.sections.items(.last_atom)[osec] = self.symbol(self.debug_aranges_index.?).ref;
             }
 
             if (self.debug_line_index == null) {
@@ -174,10 +174,10 @@ pub fn init(self: *ZigObject, elf_file: *Elf, options: InitOptions) !void {
                     .name = try elf_file.insertShString(".debug_line"),
                     .type = elf.SHT_PROGBITS,
                     .addralign = 1,
+                    .offset = std.math.maxInt(u64),
                 });
                 self.debug_line_section_dirty = true;
                 self.debug_line_index = try addSectionSymbolWithAtom(self, gpa, ".debug_line", .@"1", osec);
-                elf_file.sections.items(.last_atom)[osec] = self.symbol(self.debug_line_index.?).ref;
             }
 
             if (self.debug_line_str_index == null) {
@@ -187,10 +187,10 @@ pub fn init(self: *ZigObject, elf_file: *Elf, options: InitOptions) !void {
                     .entsize = 1,
                     .type = elf.SHT_PROGBITS,
                     .addralign = 1,
+                    .offset = std.math.maxInt(u64),
                 });
                 self.debug_line_str_section_dirty = true;
                 self.debug_line_str_index = try addSectionSymbolWithAtom(self, gpa, ".debug_line_str", .@"1", osec);
-                elf_file.sections.items(.last_atom)[osec] = self.symbol(self.debug_line_str_index.?).ref;
             }
 
             if (self.debug_loclists_index == null) {
@@ -198,10 +198,10 @@ pub fn init(self: *ZigObject, elf_file: *Elf, options: InitOptions) !void {
                     .name = try elf_file.insertShString(".debug_loclists"),
                     .type = elf.SHT_PROGBITS,
                     .addralign = 1,
+                    .offset = std.math.maxInt(u64),
                 });
                 self.debug_loclists_section_dirty = true;
                 self.debug_loclists_index = try addSectionSymbolWithAtom(self, gpa, ".debug_loclists", .@"1", osec);
-                elf_file.sections.items(.last_atom)[osec] = self.symbol(self.debug_loclists_index.?).ref;
             }
 
             if (self.debug_rnglists_index == null) {
@@ -209,10 +209,10 @@ pub fn init(self: *ZigObject, elf_file: *Elf, options: InitOptions) !void {
                     .name = try elf_file.insertShString(".debug_rnglists"),
                     .type = elf.SHT_PROGBITS,
                     .addralign = 1,
+                    .offset = std.math.maxInt(u64),
                 });
                 self.debug_rnglists_section_dirty = true;
                 self.debug_rnglists_index = try addSectionSymbolWithAtom(self, gpa, ".debug_rnglists", .@"1", osec);
-                elf_file.sections.items(.last_atom)[osec] = self.symbol(self.debug_rnglists_index.?).ref;
             }
 
             if (self.eh_frame_index == null) {
@@ -224,10 +224,10 @@ pub fn init(self: *ZigObject, elf_file: *Elf, options: InitOptions) !void {
                         elf.SHT_PROGBITS,
                     .flags = elf.SHF_ALLOC,
                     .addralign = ptr_size,
+                    .offset = std.math.maxInt(u64),
                 });
                 self.eh_frame_section_dirty = true;
                 self.eh_frame_index = try addSectionSymbolWithAtom(self, gpa, ".eh_frame", Atom.Alignment.fromNonzeroByteUnits(ptr_size), osec);
-                elf_file.sections.items(.last_atom)[osec] = self.symbol(self.eh_frame_index.?).ref;
             }
 
             try dwarf.initMetadata();
@@ -1318,7 +1318,7 @@ fn updateNavCode(
         const capacity = atom_ptr.capacity(elf_file);
         const need_realloc = code.len > capacity or !required_alignment.check(@intCast(atom_ptr.value));
         if (need_realloc) {
-            try self.growAtom(atom_ptr, elf_file);
+            try self.allocateAtom(atom_ptr, true, elf_file);
             log.debug("growing {} from 0x{x} to 0x{x}", .{ nav.fqn.fmt(ip), old_vaddr, atom_ptr.value });
             if (old_vaddr != atom_ptr.value) {
                 sym.value = 0;
@@ -1328,7 +1328,7 @@ fn updateNavCode(
             // TODO shrink section size
         }
     } else {
-        try self.allocateAtom(atom_ptr, elf_file);
+        try self.allocateAtom(atom_ptr, true, elf_file);
         errdefer self.freeNavMetadata(elf_file, sym_index);
         sym.value = 0;
         esym.st_value = 0;
@@ -1403,7 +1403,7 @@ fn updateTlv(
     const gop = try self.tls_variables.getOrPut(gpa, atom_ptr.atom_index);
     assert(!gop.found_existing); // TODO incremental updates
 
-    try self.allocateAtom(atom_ptr, elf_file);
+    try self.allocateAtom(atom_ptr, true, elf_file);
     sym.value = 0;
     esym.st_value = 0;
 
@@ -1729,7 +1729,7 @@ fn updateLazySymbol(
     atom_ptr.size = code.len;
     atom_ptr.output_section_index = output_section_index;
 
-    try self.allocateAtom(atom_ptr, elf_file);
+    try self.allocateAtom(atom_ptr, true, elf_file);
     errdefer self.freeNavMetadata(elf_file, symbol_index);
 
     local_sym.value = 0;
@@ -1784,7 +1784,7 @@ fn lowerConst(
     atom_ptr.size = code.len;
     atom_ptr.output_section_index = output_section_index;
 
-    try self.allocateAtom(atom_ptr, elf_file);
+    try self.allocateAtom(atom_ptr, true, elf_file);
     errdefer self.freeNavMetadata(elf_file, sym_index);
 
     try elf_file.base.file.?.pwriteAll(code, atom_ptr.offset(elf_file));
@@ -1981,17 +1981,27 @@ fn writeTrampoline(tr_sym: Symbol, target: Symbol, elf_file: *Elf) !void {
     }
 }
 
-fn allocateAtom(self: *ZigObject, atom_ptr: *Atom, elf_file: *Elf) !void {
+pub fn allocateAtom(self: *ZigObject, atom_ptr: *Atom, requires_padding: bool, elf_file: *Elf) !void {
+    const slice = elf_file.sections.slice();
+    const shdr = &slice.items(.shdr)[atom_ptr.output_section_index];
+    const last_atom_ref = &slice.items(.last_atom)[atom_ptr.output_section_index];
+
+    // FIXME:JK this only works if this atom is the only atom in the output section
+    // In every other case, we need to redo the prev/next links
+    if (last_atom_ref.eql(atom_ptr.ref())) last_atom_ref.* = .{};
+
     const alloc_res = try elf_file.allocateChunk(.{
         .shndx = atom_ptr.output_section_index,
         .size = atom_ptr.size,
         .alignment = atom_ptr.alignment,
+        .requires_padding = requires_padding,
     });
     atom_ptr.value = @intCast(alloc_res.value);
-
-    const slice = elf_file.sections.slice();
-    const shdr = &slice.items(.shdr)[atom_ptr.output_section_index];
-    const last_atom_ref = &slice.items(.last_atom)[atom_ptr.output_section_index];
+    log.debug("allocated {s} at {x}\n  placement {?}", .{
+        atom_ptr.name(elf_file),
+        atom_ptr.offset(elf_file),
+        alloc_res.placement,
+    });
 
     const expand_section = if (elf_file.atom(alloc_res.placement)) |placement_atom|
         placement_atom.nextAtom(elf_file) == null
@@ -2013,12 +2023,6 @@ fn allocateAtom(self: *ZigObject, atom_ptr: *Atom, elf_file: *Elf) !void {
     }
     shdr.sh_addralign = @max(shdr.sh_addralign, atom_ptr.alignment.toByteUnits().?);
 
-    if (self.sectionSymbol(atom_ptr.output_section_index, elf_file)) |sym| {
-        assert(sym.atom(elf_file) == null and sym.mergeSubsection(elf_file) == null);
-        const esym = &self.symtab.items(.elf_sym)[sym.esym_index];
-        esym.st_size += atom_ptr.size + Elf.padToIdeal(atom_ptr.size);
-    }
-
     // This function can also reallocate an atom.
     // In this case we need to "unplug" it from its previous location before
     // plugging it in to its new location.
@@ -2037,12 +2041,8 @@ fn allocateAtom(self: *ZigObject, atom_ptr: *Atom, elf_file: *Elf) !void {
         atom_ptr.prev_atom_ref = .{ .index = 0, .file = 0 };
         atom_ptr.next_atom_ref = .{ .index = 0, .file = 0 };
     }
-}
 
-fn growAtom(self: *ZigObject, atom_ptr: *Atom, elf_file: *Elf) !void {
-    if (!atom_ptr.alignment.check(@intCast(atom_ptr.value)) or atom_ptr.size > atom_ptr.capacity(elf_file)) {
-        try self.allocateAtom(atom_ptr, elf_file);
-    }
+    log.debug("  prev {?}, next {?}", .{ atom_ptr.prev_atom_ref, atom_ptr.next_atom_ref });
 }
 
 pub fn resetShdrIndexes(self: *ZigObject, backlinks: anytype) void {
src/link/Dwarf.zig
@@ -389,14 +389,20 @@ pub const Section = struct {
         if (dwarf.bin_file.cast(.elf)) |elf_file| {
             const zo = elf_file.zigObjectPtr().?;
             const atom = zo.symbol(sec.index).atom(elf_file).?;
-            const shndx = atom.output_section_index;
-            const needed_size = len;
-            const min_alignment = sec.alignment.toByteUnits().?;
-            try elf_file.growSection(shndx, needed_size, min_alignment);
-            const shdr = elf_file.sections.items(.shdr)[shndx];
-            atom.size = needed_size;
-            atom.alignment = InternPool.Alignment.fromNonzeroByteUnits(shdr.sh_addralign);
-            sec.len = needed_size;
+            const old_size = atom.size;
+            atom.size = len;
+            atom.alignment = sec.alignment;
+            sec.len = len;
+            if (old_size > 0) {
+                if (!atom.alignment.check(@intCast(atom.value)) or atom.size > atom.fileCapacity(elf_file)) {
+                    try zo.allocateAtom(atom, false, elf_file);
+                } else {
+                    const shdr = &elf_file.sections.items(.shdr)[atom.output_section_index];
+                    shdr.sh_size = (shdr.sh_size - old_size) + atom.size;
+                }
+            } else {
+                try zo.allocateAtom(atom, false, elf_file);
+            }
         } else if (dwarf.bin_file.cast(.macho)) |macho_file| {
             const header = if (macho_file.d_sym) |*d_sym| header: {
                 try d_sym.growSection(@intCast(sec.index), len, true, macho_file);
@@ -417,11 +423,15 @@ pub const Section = struct {
         if (dwarf.bin_file.cast(.elf)) |elf_file| {
             const zo = elf_file.zigObjectPtr().?;
             const atom = zo.symbol(sec.index).atom(elf_file).?;
-            const shndx = atom.output_section_index;
-            const shdr = &elf_file.sections.items(.shdr)[shndx];
-            atom.size = sec.len;
-            shdr.sh_offset += len;
-            shdr.sh_size = sec.len;
+            if (atom.prevAtom(elf_file)) |_| {
+                // FIXME:JK trimming/shrinking has to be reworked on ZigObject/Elf level
+                atom.value += len;
+            } else {
+                const shdr = &elf_file.sections.items(.shdr)[atom.output_section_index];
+                shdr.sh_offset += len;
+                atom.value = 0;
+            }
+            atom.size -= len;
         } else if (dwarf.bin_file.cast(.macho)) |macho_file| {
             const header = if (macho_file.d_sym) |*d_sym|
                 &d_sym.sections.items[sec.index]
@@ -910,11 +920,9 @@ const Entry = struct {
         if (std.debug.runtime_safety) {
             log.err("missing {} from {s}", .{
                 @as(Entry.Index, @enumFromInt(entry - unit.entries.items.ptr)),
-                std.mem.sliceTo(if (dwarf.bin_file.cast(.elf)) |elf_file| sh_name: {
-                    const zo = elf_file.zigObjectPtr().?;
-                    const shndx = zo.symbol(sec.index).atom(elf_file).?.output_section_index;
-                    break :sh_name elf_file.shstrtab.items[elf_file.sections.items(.shdr)[shndx].sh_name..];
-                } else if (dwarf.bin_file.cast(.macho)) |macho_file|
+                std.mem.sliceTo(if (dwarf.bin_file.cast(.elf)) |elf_file|
+                    elf_file.zigObjectPtr().?.symbol(sec.index).name(elf_file)
+                else if (dwarf.bin_file.cast(.macho)) |macho_file|
                     if (macho_file.d_sym) |*d_sym|
                         &d_sym.sections.items[sec.index].segname
                     else
src/link/Elf.zig
@@ -573,10 +573,10 @@ pub fn growSection(self: *Elf, shdr_index: u32, needed_size: u64, min_alignment:
             // Must move the entire section.
             const new_offset = try self.findFreeSpace(needed_size, min_alignment);
 
-            log.debug("new '{s}' file offset 0x{x} to 0x{x}", .{
+            log.debug("moving '{s}' from 0x{x} to 0x{x}", .{
                 self.getShString(shdr.sh_name),
+                shdr.sh_offset,
                 new_offset,
-                new_offset + existing_size,
             });
 
             const amt = try self.base.file.?.copyRangeAll(
@@ -691,13 +691,6 @@ pub fn allocateChunk(self: *Elf, args: struct {
         }
     };
 
-    log.debug("allocated chunk (size({x}),align({x})) at 0x{x} (file(0x{x}))", .{
-        args.size,
-        args.alignment.toByteUnits().?,
-        shdr.sh_addr + res.value,
-        shdr.sh_offset + res.value,
-    });
-
     const expand_section = if (self.atom(res.placement)) |placement_atom|
         placement_atom.nextAtom(self) == null
     else
@@ -707,6 +700,18 @@ pub fn allocateChunk(self: *Elf, args: struct {
         try self.growSection(args.shndx, needed_size, args.alignment.toByteUnits().?);
     }
 
+    log.debug("allocated chunk (size({x}),align({x})) in {s} at 0x{x} (file(0x{x}))", .{
+        args.size,
+        args.alignment.toByteUnits().?,
+        self.getShString(shdr.sh_name),
+        shdr.sh_addr + res.value,
+        shdr.sh_offset + res.value,
+    });
+    log.debug("  placement {}, {s}", .{
+        res.placement,
+        if (self.atom(res.placement)) |atom_ptr| atom_ptr.name(self) else "",
+    });
+
     return res;
 }
 
@@ -1796,6 +1801,7 @@ pub fn initOutputSection(self: *Elf, args: struct {
         .type = @"type",
         .flags = flags,
         .name = try self.insertShString(name),
+        .offset = std.math.maxInt(u64),
     });
     return out_shndx;
 }
@@ -3898,27 +3904,14 @@ pub fn allocateNonAllocSections(self: *Elf) !void {
             shdr.sh_size = 0;
             const new_offset = try self.findFreeSpace(needed_size, shdr.sh_addralign);
 
-            if (self.zigObjectPtr()) |zo| blk: {
-                const existing_size = for ([_]?Symbol.Index{
-                    zo.debug_info_index,
-                    zo.debug_abbrev_index,
-                    zo.debug_aranges_index,
-                    zo.debug_str_index,
-                    zo.debug_line_index,
-                    zo.debug_line_str_index,
-                    zo.debug_loclists_index,
-                    zo.debug_rnglists_index,
-                }) |maybe_sym_index| {
-                    const sym_index = maybe_sym_index orelse continue;
-                    const sym = zo.symbol(sym_index);
-                    const atom_ptr = sym.atom(self).?;
-                    if (atom_ptr.output_section_index == shndx) break atom_ptr.size;
-                } else break :blk;
-                log.debug("moving {s} from 0x{x} to 0x{x}", .{
-                    self.getShString(shdr.sh_name),
-                    shdr.sh_offset,
-                    new_offset,
-                });
+            log.debug("moving {s} from 0x{x} to 0x{x}", .{
+                self.getShString(shdr.sh_name),
+                shdr.sh_offset,
+                new_offset,
+            });
+
+            if (shdr.sh_offset > 0) {
+                const existing_size = self.sectionSize(@intCast(shndx));
                 const amt = try self.base.file.?.copyRangeAll(
                     shdr.sh_offset,
                     self.base.file.?,