diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 92c0c5728a..6698a599a9 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -5402,8 +5402,8 @@ pub const SystemLib = struct { }; pub const Ref = struct { - index: u32, - file: u32, + index: u32 = 0, + file: u32 = 0, pub fn eql(ref: Ref, other: Ref) bool { return ref.index == other.index and ref.file == other.file; @@ -5522,7 +5522,7 @@ const Section = struct { atom_list: std.ArrayListUnmanaged(Ref) = .{}, /// Index of the last allocated atom in this section. - last_atom_index: Atom.Index = 0, + last_atom: Ref = .{ .index = 0, .file = 0 }, /// A list of atoms that have surplus capacity. This list can have false /// positives, as functions grow and shrink over time, only sometimes being added @@ -5539,7 +5539,7 @@ const Section = struct { /// overcapacity can be negative. A simple way to have negative overcapacity is to /// allocate a fresh text block, which will have ideal capacity, and then grow it /// by 1 byte. It will then have -1 overcapacity. - free_list: std.ArrayListUnmanaged(Atom.Index) = .{}, + free_list: std.ArrayListUnmanaged(Ref) = .{}, }; fn defaultEntrySymbolName(cpu_arch: std.Target.Cpu.Arch) []const u8 { diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index a761e977c3..5981b6ef2c 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -25,10 +25,9 @@ relocs_section_index: u32 = 0, /// Index of this atom in the linker's atoms table. atom_index: Index = 0, -/// Points to the previous and next neighbors, based on the `text_offset`. -/// This can be used to find, for example, the capacity of this `TextBlock`. -prev_index: Index = 0, -next_index: Index = 0, +/// Points to the previous and next neighbors. +prev_atom_ref: Elf.Ref = .{}, +next_atom_ref: Elf.Ref = .{}, /// Specifies whether this atom is alive or has been garbage collected. alive: bool = true, @@ -52,6 +51,18 @@ pub fn address(self: Atom, elf_file: *Elf) i64 { return @as(i64, @intCast(shdr.sh_addr)) + self.value; } +pub fn ref(self: Atom) Elf.Ref { + return .{ .index = self.atom_index, .file = self.file_index }; +} + +pub fn prevAtom(self: Atom, elf_file: *Elf) ?*Atom { + return elf_file.atom(self.prev_atom_ref); +} + +pub fn nextAtom(self: Atom, elf_file: *Elf) ?*Atom { + return elf_file.atom(self.next_atom_ref); +} + pub fn debugTombstoneValue(self: Atom, target: Symbol, elf_file: *Elf) ?u64 { if (target.mergeSubsection(elf_file)) |msub| { if (msub.alive) return null; @@ -95,18 +106,16 @@ pub fn priority(self: Atom, elf_file: *Elf) u64 { /// File offset relocation happens transparently, so it is not included in /// this calculation. pub fn capacity(self: Atom, elf_file: *Elf) u64 { - const zo = elf_file.zigObjectPtr().?; - const next_addr = if (zo.atom(self.next_index)) |next| - next.address(elf_file) + const next_addr = if (self.nextAtom(elf_file)) |next_atom| + next_atom.address(elf_file) else std.math.maxInt(u32); return @intCast(next_addr - self.address(elf_file)); } pub fn freeListEligible(self: Atom, elf_file: *Elf) bool { - const zo = elf_file.zigObjectPtr().?; // No need to keep a free list node for the last block. - const next = zo.atom(self.next_index) orelse return false; + const next = self.nextAtom(elf_file) orelse return false; const cap: u64 = @intCast(next.address(elf_file) - self.address(elf_file)); const ideal_cap = Elf.padToIdeal(self.size); if (cap <= ideal_cap) return false; @@ -115,11 +124,10 @@ pub fn freeListEligible(self: Atom, elf_file: *Elf) bool { } pub fn allocate(self: *Atom, elf_file: *Elf) !void { - const zo = elf_file.zigObjectPtr().?; const slice = elf_file.sections.slice(); const shdr = &slice.items(.shdr)[self.output_section_index]; const free_list = &slice.items(.free_list)[self.output_section_index]; - const last_atom_index = &slice.items(.last_atom_index)[self.output_section_index]; + const last_atom_ref = &slice.items(.last_atom)[self.output_section_index]; const new_atom_ideal_capacity = Elf.padToIdeal(self.size); // We use these to indicate our intention to update metadata, placing the new atom, @@ -127,7 +135,7 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void { // It would be simpler to do it inside the for loop below, but that would cause a // problem if an error was returned later in the function. So this action // is actually carried out at the end of the function, when errors are no longer possible. - var atom_placement: ?Atom.Index = null; + var atom_placement: ?Elf.Ref = null; var free_list_removal: ?usize = null; // First we look for an appropriately sized free list node. @@ -135,8 +143,8 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void { self.value = blk: { var i: usize = if (elf_file.base.child_pid == null) 0 else free_list.items.len; while (i < free_list.items.len) { - const big_atom_index = free_list.items[i]; - const big_atom = zo.atom(big_atom_index).?; + const big_atom_ref = free_list.items[i]; + const big_atom = elf_file.atom(big_atom_ref).?; // We now have a pointer to a live atom that has too much capacity. // Is it enough that we could fit this new atom? const cap = big_atom.capacity(elf_file); @@ -163,50 +171,52 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void { const keep_free_list_node = remaining_capacity >= Elf.min_text_capacity; // Set up the metadata to be updated, after errors are no longer possible. - atom_placement = big_atom_index; + atom_placement = big_atom_ref; if (!keep_free_list_node) { free_list_removal = i; } break :blk @intCast(new_start_vaddr); - } else if (zo.atom(last_atom_index.*)) |last| { - const ideal_capacity = Elf.padToIdeal(last.size); - const ideal_capacity_end_vaddr = @as(u64, @intCast(last.value)) + ideal_capacity; + } else if (elf_file.atom(last_atom_ref.*)) |last_atom| { + const ideal_capacity = Elf.padToIdeal(last_atom.size); + const ideal_capacity_end_vaddr = @as(u64, @intCast(last_atom.value)) + ideal_capacity; const new_start_vaddr = self.alignment.forward(ideal_capacity_end_vaddr); // Set up the metadata to be updated, after errors are no longer possible. - atom_placement = last.atom_index; + atom_placement = last_atom.ref(); break :blk @intCast(new_start_vaddr); } else { break :blk 0; } }; - log.debug("allocated atom({d}) : '{s}' at 0x{x} to 0x{x}", .{ - self.atom_index, + log.debug("allocated atom({}) : '{s}' at 0x{x} to 0x{x}", .{ + self.ref(), self.name(elf_file), self.address(elf_file), self.address(elf_file) + @as(i64, @intCast(self.size)), }); - const expand_section = if (atom_placement) |placement_index| - zo.atom(placement_index).?.next_index == 0 + const expand_section = if (atom_placement) |placement_ref| + elf_file.atom(placement_ref).?.nextAtom(elf_file) == null else true; if (expand_section) { const needed_size: u64 = @intCast(self.value + @as(i64, @intCast(self.size))); try elf_file.growAllocSection(self.output_section_index, needed_size); - last_atom_index.* = self.atom_index; + last_atom_ref.* = self.ref(); - const zig_object = elf_file.zigObjectPtr().?; - if (zig_object.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. - zig_object.debug_info_section_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. - zig_object.debug_aranges_section_dirty = true; - zig_object.debug_rnglists_section_dirty = true; + switch (self.file(elf_file).?) { + .zig_object => |zo| if (zo.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. + zo.debug_info_section_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. + zo.debug_aranges_section_dirty = true; + zo.debug_rnglists_section_dirty = true; + }, + else => {}, } } shdr.sh_addralign = @max(shdr.sh_addralign, self.alignment.toByteUnits().?); @@ -214,21 +224,21 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void { // 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. - if (zo.atom(self.prev_index)) |prev| { - prev.next_index = self.next_index; + if (self.prevAtom(elf_file)) |prev| { + prev.next_atom_ref = self.next_atom_ref; } - if (zo.atom(self.next_index)) |next| { - next.prev_index = self.prev_index; + if (self.nextAtom(elf_file)) |next| { + next.prev_atom_ref = self.prev_atom_ref; } - if (atom_placement) |big_atom_index| { - const big_atom = zo.atom(big_atom_index).?; - self.prev_index = big_atom_index; - self.next_index = big_atom.next_index; - big_atom.next_index = self.atom_index; + if (atom_placement) |big_atom_ref| { + const big_atom = elf_file.atom(big_atom_ref).?; + self.prev_atom_ref = big_atom_ref; + self.next_atom_ref = big_atom.next_atom_ref; + big_atom.next_atom_ref = self.ref(); } else { - self.prev_index = 0; - self.next_index = 0; + self.prev_atom_ref = .{ .index = 0, .file = 0 }; + self.next_atom_ref = .{ .index = 0, .file = 0 }; } if (free_list_removal) |i| { _ = free_list.swapRemove(i); @@ -248,64 +258,70 @@ pub fn grow(self: *Atom, elf_file: *Elf) !void { } pub fn free(self: *Atom, elf_file: *Elf) void { - log.debug("freeAtom {d} ({s})", .{ self.atom_index, self.name(elf_file) }); + log.debug("freeAtom atom({}) ({s})", .{ self.ref(), self.name(elf_file) }); - const zo = elf_file.zigObjectPtr().?; const comp = elf_file.base.comp; const gpa = comp.gpa; const shndx = self.output_section_index; const slice = elf_file.sections.slice(); const free_list = &slice.items(.free_list)[shndx]; - const last_atom_index = &slice.items(.last_atom_index)[shndx]; + const last_atom_ref = &slice.items(.last_atom)[shndx]; var already_have_free_list_node = false; { var i: usize = 0; // TODO turn free_list into a hash map while (i < free_list.items.len) { - if (free_list.items[i] == self.atom_index) { + if (free_list.items[i].eql(self.ref())) { _ = free_list.swapRemove(i); continue; } - if (free_list.items[i] == self.prev_index) { - already_have_free_list_node = true; + if (self.prevAtom(elf_file)) |prev_atom| { + if (free_list.items[i].eql(prev_atom.ref())) { + already_have_free_list_node = true; + } } i += 1; } } - if (zo.atom(last_atom_index.*)) |last_atom| { - if (last_atom.atom_index == self.atom_index) { - if (zo.atom(self.prev_index)) |_| { + if (elf_file.atom(last_atom_ref.*)) |last_atom| { + if (last_atom.ref().eql(self.ref())) { + if (self.prevAtom(elf_file)) |prev_atom| { // TODO shrink the section size here - last_atom_index.* = self.prev_index; + last_atom_ref.* = prev_atom.ref(); } else { - last_atom_index.* = 0; + last_atom_ref.* = .{}; } } } - if (zo.atom(self.prev_index)) |prev| { - prev.next_index = self.next_index; - if (!already_have_free_list_node and prev.*.freeListEligible(elf_file)) { + if (self.prevAtom(elf_file)) |prev_atom| { + prev_atom.next_atom_ref = self.next_atom_ref; + if (!already_have_free_list_node and prev_atom.*.freeListEligible(elf_file)) { // The free list is heuristics, it doesn't have to be perfect, so we can // ignore the OOM here. - free_list.append(gpa, prev.atom_index) catch {}; + free_list.append(gpa, prev_atom.ref()) catch {}; } } else { - self.prev_index = 0; + self.prev_atom_ref = .{}; } - if (zo.atom(self.next_index)) |next| { - next.prev_index = self.prev_index; + if (self.nextAtom(elf_file)) |next_atom| { + next_atom.prev_atom_ref = self.prev_atom_ref; } else { - self.next_index = 0; + self.next_atom_ref = .{}; } - // TODO create relocs free list - self.freeRelocs(zo); - // TODO figure out how to free input section mappind in ZigModule - // const zig_object = elf_file.zigObjectPtr().? - // assert(zig_object.atoms.swapRemove(self.atom_index)); + switch (self.file(elf_file).?) { + .zig_object => |zo| { + // TODO create relocs free list + self.freeRelocs(zo); + // TODO figure out how to free input section mappind in ZigModule + // const zig_object = elf_file.zigObjectPtr().? + // assert(zig_object.atoms.swapRemove(self.atom_index)); + }, + else => {}, + } self.* = .{}; } diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index d1097fa6fc..1b1c35b645 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -11,7 +11,7 @@ file_index: File.Index = 0, /// Reference to Atom or merge subsection containing this symbol if any. /// Use `atom` or `mergeSubsection` to get the pointer to the atom. -ref: Elf.Ref = .{ .index = 0, .file = 0 }, +ref: Elf.Ref = .{}, /// Assigned output section index for this symbol. output_section_index: u32 = 0,