Commit 1254509d78
Changed files (3)
src
link
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;
-
- 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;
+ last_atom_ref.* = self.ref();
+
+ 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.* = .{};
}
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,
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 {