From db769a8764a41764c05ce03da380a0c88852105a Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 4 Oct 2024 12:46:57 +0200 Subject: [PATCH] elf: change how we manage debug atoms in Dwarf linker --- src/link/Dwarf.zig | 44 +++++++++++++++----------- src/link/Elf.zig | 53 ++++++++++++++----------------- src/link/Elf/Atom.zig | 11 ++++++- src/link/Elf/ZigObject.zig | 60 ++++++++++++++++++------------------ src/link/Elf/relocatable.zig | 26 +++++----------- 5 files changed, 97 insertions(+), 97 deletions(-) diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index cde40294b4..59e0cae934 100644 --- a/src/link/Dwarf.zig +++ b/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 diff --git a/src/link/Elf.zig b/src/link/Elf.zig index e495e8fdaa..ed9e7a6477 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -561,10 +561,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( @@ -679,13 +679,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 @@ -695,6 +688,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; } @@ -1969,6 +1974,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; } @@ -4065,27 +4071,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.?, diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index ab87d4f38e..b229114d52 100644 --- a/src/link/Elf/Atom.zig +++ b/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; diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index 957418f65e..0901920ee5 100644 --- a/src/link/Elf/ZigObject.zig +++ b/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 { diff --git a/src/link/Elf/relocatable.zig b/src/link/Elf/relocatable.zig index 7d0631833e..2dec484c3d 100644 --- a/src/link/Elf/relocatable.zig +++ b/src/link/Elf/relocatable.zig @@ -414,24 +414,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.?,