authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-08-26 22:19:51+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-08-26 22:19:51+02:00
log1254509d78d8dd3d6eb7922bbb924084eae826fd
treee36656e3855b8946527f7184748eab01c34c665a
parent20240e9cd5a796b63bc2571aa70bb77144e8860c

elf: make Atom.allocate and related ZigObject-independent


3 files changed, 92 insertions(+), 76 deletions(-)

src/link/Elf.zig+4-4
...@@ -5402,8 +5402,8 @@ pub const SystemLib = struct {...@@ -5402,8 +5402,8 @@ pub const SystemLib = struct {
5402};5402};
54035403
5404pub const Ref = struct {5404pub const Ref = struct {
5405 index: u32,5405 index: u32 = 0,
5406 file: u32,5406 file: u32 = 0,
54075407
5408 pub fn eql(ref: Ref, other: Ref) bool {5408 pub fn eql(ref: Ref, other: Ref) bool {
5409 return ref.index == other.index and ref.file == other.file;5409 return ref.index == other.index and ref.file == other.file;
...@@ -5522,7 +5522,7 @@ const Section = struct {...@@ -5522,7 +5522,7 @@ const Section = struct {
5522 atom_list: std.ArrayListUnmanaged(Ref) = .{},5522 atom_list: std.ArrayListUnmanaged(Ref) = .{},
55235523
5524 /// Index of the last allocated atom in this section.5524 /// Index of the last allocated atom in this section.
5525 last_atom_index: Atom.Index = 0,5525 last_atom: Ref = .{ .index = 0, .file = 0 },
55265526
5527 /// A list of atoms that have surplus capacity. This list can have false5527 /// A list of atoms that have surplus capacity. This list can have false
5528 /// positives, as functions grow and shrink over time, only sometimes being added5528 /// positives, as functions grow and shrink over time, only sometimes being added
...@@ -5539,7 +5539,7 @@ const Section = struct {...@@ -5539,7 +5539,7 @@ const Section = struct {
5539 /// overcapacity can be negative. A simple way to have negative overcapacity is to5539 /// overcapacity can be negative. A simple way to have negative overcapacity is to
5540 /// allocate a fresh text block, which will have ideal capacity, and then grow it5540 /// allocate a fresh text block, which will have ideal capacity, and then grow it
5541 /// by 1 byte. It will then have -1 overcapacity.5541 /// by 1 byte. It will then have -1 overcapacity.
5542 free_list: std.ArrayListUnmanaged(Atom.Index) = .{},5542 free_list: std.ArrayListUnmanaged(Ref) = .{},
5543};5543};
55445544
5545fn defaultEntrySymbolName(cpu_arch: std.Target.Cpu.Arch) []const u8 {5545fn defaultEntrySymbolName(cpu_arch: std.Target.Cpu.Arch) []const u8 {
src/link/Elf/Atom.zig+87-71
...@@ -25,10 +25,9 @@ relocs_section_index: u32 = 0,...@@ -25,10 +25,9 @@ relocs_section_index: u32 = 0,
25/// Index of this atom in the linker's atoms table.25/// Index of this atom in the linker's atoms table.
26atom_index: Index = 0,26atom_index: Index = 0,
2727
28/// Points to the previous and next neighbors, based on the `text_offset`.28/// Points to the previous and next neighbors.
29/// This can be used to find, for example, the capacity of this `TextBlock`.29prev_atom_ref: Elf.Ref = .{},
30prev_index: Index = 0,30next_atom_ref: Elf.Ref = .{},
31next_index: Index = 0,
3231
33/// Specifies whether this atom is alive or has been garbage collected.32/// Specifies whether this atom is alive or has been garbage collected.
34alive: bool = true,33alive: bool = true,
...@@ -52,6 +51,18 @@ pub fn address(self: Atom, elf_file: *Elf) i64 {...@@ -52,6 +51,18 @@ pub fn address(self: Atom, elf_file: *Elf) i64 {
52 return @as(i64, @intCast(shdr.sh_addr)) + self.value;51 return @as(i64, @intCast(shdr.sh_addr)) + self.value;
53}52}
5453
54pub fn ref(self: Atom) Elf.Ref {
55 return .{ .index = self.atom_index, .file = self.file_index };
56}
57
58pub fn prevAtom(self: Atom, elf_file: *Elf) ?*Atom {
59 return elf_file.atom(self.prev_atom_ref);
60}
61
62pub fn nextAtom(self: Atom, elf_file: *Elf) ?*Atom {
63 return elf_file.atom(self.next_atom_ref);
64}
65
55pub fn debugTombstoneValue(self: Atom, target: Symbol, elf_file: *Elf) ?u64 {66pub fn debugTombstoneValue(self: Atom, target: Symbol, elf_file: *Elf) ?u64 {
56 if (target.mergeSubsection(elf_file)) |msub| {67 if (target.mergeSubsection(elf_file)) |msub| {
57 if (msub.alive) return null;68 if (msub.alive) return null;
...@@ -95,18 +106,16 @@ pub fn priority(self: Atom, elf_file: *Elf) u64 {...@@ -95,18 +106,16 @@ pub fn priority(self: Atom, elf_file: *Elf) u64 {
95/// File offset relocation happens transparently, so it is not included in106/// File offset relocation happens transparently, so it is not included in
96/// this calculation.107/// this calculation.
97pub fn capacity(self: Atom, elf_file: *Elf) u64 {108pub fn capacity(self: Atom, elf_file: *Elf) u64 {
98 const zo = elf_file.zigObjectPtr().?;109 const next_addr = if (self.nextAtom(elf_file)) |next_atom|
99 const next_addr = if (zo.atom(self.next_index)) |next|110 next_atom.address(elf_file)
100 next.address(elf_file)
101 else111 else
102 std.math.maxInt(u32);112 std.math.maxInt(u32);
103 return @intCast(next_addr - self.address(elf_file));113 return @intCast(next_addr - self.address(elf_file));
104}114}
105115
106pub fn freeListEligible(self: Atom, elf_file: *Elf) bool {116pub fn freeListEligible(self: Atom, elf_file: *Elf) bool {
107 const zo = elf_file.zigObjectPtr().?;
108 // No need to keep a free list node for the last block.117 // No need to keep a free list node for the last block.
109 const next = zo.atom(self.next_index) orelse return false;118 const next = self.nextAtom(elf_file) orelse return false;
110 const cap: u64 = @intCast(next.address(elf_file) - self.address(elf_file));119 const cap: u64 = @intCast(next.address(elf_file) - self.address(elf_file));
111 const ideal_cap = Elf.padToIdeal(self.size);120 const ideal_cap = Elf.padToIdeal(self.size);
112 if (cap <= ideal_cap) return false;121 if (cap <= ideal_cap) return false;
...@@ -115,11 +124,10 @@ pub fn freeListEligible(self: Atom, elf_file: *Elf) bool {...@@ -115,11 +124,10 @@ pub fn freeListEligible(self: Atom, elf_file: *Elf) bool {
115}124}
116125
117pub fn allocate(self: *Atom, elf_file: *Elf) !void {126pub fn allocate(self: *Atom, elf_file: *Elf) !void {
118 const zo = elf_file.zigObjectPtr().?;
119 const slice = elf_file.sections.slice();127 const slice = elf_file.sections.slice();
120 const shdr = &slice.items(.shdr)[self.output_section_index];128 const shdr = &slice.items(.shdr)[self.output_section_index];
121 const free_list = &slice.items(.free_list)[self.output_section_index];129 const free_list = &slice.items(.free_list)[self.output_section_index];
122 const last_atom_index = &slice.items(.last_atom_index)[self.output_section_index];130 const last_atom_ref = &slice.items(.last_atom)[self.output_section_index];
123 const new_atom_ideal_capacity = Elf.padToIdeal(self.size);131 const new_atom_ideal_capacity = Elf.padToIdeal(self.size);
124132
125 // We use these to indicate our intention to update metadata, placing the new atom,133 // 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 {...@@ -127,7 +135,7 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void {
127 // It would be simpler to do it inside the for loop below, but that would cause a135 // It would be simpler to do it inside the for loop below, but that would cause a
128 // problem if an error was returned later in the function. So this action136 // problem if an error was returned later in the function. So this action
129 // is actually carried out at the end of the function, when errors are no longer possible.137 // is actually carried out at the end of the function, when errors are no longer possible.
130 var atom_placement: ?Atom.Index = null;138 var atom_placement: ?Elf.Ref = null;
131 var free_list_removal: ?usize = null;139 var free_list_removal: ?usize = null;
132140
133 // First we look for an appropriately sized free list node.141 // First we look for an appropriately sized free list node.
...@@ -135,8 +143,8 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void {...@@ -135,8 +143,8 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void {
135 self.value = blk: {143 self.value = blk: {
136 var i: usize = if (elf_file.base.child_pid == null) 0 else free_list.items.len;144 var i: usize = if (elf_file.base.child_pid == null) 0 else free_list.items.len;
137 while (i < free_list.items.len) {145 while (i < free_list.items.len) {
138 const big_atom_index = free_list.items[i];146 const big_atom_ref = free_list.items[i];
139 const big_atom = zo.atom(big_atom_index).?;147 const big_atom = elf_file.atom(big_atom_ref).?;
140 // We now have a pointer to a live atom that has too much capacity.148 // We now have a pointer to a live atom that has too much capacity.
141 // Is it enough that we could fit this new atom?149 // Is it enough that we could fit this new atom?
142 const cap = big_atom.capacity(elf_file);150 const cap = big_atom.capacity(elf_file);
...@@ -163,50 +171,52 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void {...@@ -163,50 +171,52 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void {
163 const keep_free_list_node = remaining_capacity >= Elf.min_text_capacity;171 const keep_free_list_node = remaining_capacity >= Elf.min_text_capacity;
164172
165 // Set up the metadata to be updated, after errors are no longer possible.173 // Set up the metadata to be updated, after errors are no longer possible.
166 atom_placement = big_atom_index;174 atom_placement = big_atom_ref;
167 if (!keep_free_list_node) {175 if (!keep_free_list_node) {
168 free_list_removal = i;176 free_list_removal = i;
169 }177 }
170 break :blk @intCast(new_start_vaddr);178 break :blk @intCast(new_start_vaddr);
171 } else if (zo.atom(last_atom_index.*)) |last| {179 } else if (elf_file.atom(last_atom_ref.*)) |last_atom| {
172 const ideal_capacity = Elf.padToIdeal(last.size);180 const ideal_capacity = Elf.padToIdeal(last_atom.size);
173 const ideal_capacity_end_vaddr = @as(u64, @intCast(last.value)) + ideal_capacity;181 const ideal_capacity_end_vaddr = @as(u64, @intCast(last_atom.value)) + ideal_capacity;
174 const new_start_vaddr = self.alignment.forward(ideal_capacity_end_vaddr);182 const new_start_vaddr = self.alignment.forward(ideal_capacity_end_vaddr);
175 // Set up the metadata to be updated, after errors are no longer possible.183 // Set up the metadata to be updated, after errors are no longer possible.
176 atom_placement = last.atom_index;184 atom_placement = last_atom.ref();
177 break :blk @intCast(new_start_vaddr);185 break :blk @intCast(new_start_vaddr);
178 } else {186 } else {
179 break :blk 0;187 break :blk 0;
180 }188 }
181 };189 };
182190
183 log.debug("allocated atom({d}) : '{s}' at 0x{x} to 0x{x}", .{191 log.debug("allocated atom({}) : '{s}' at 0x{x} to 0x{x}", .{
184 self.atom_index,192 self.ref(),
185 self.name(elf_file),193 self.name(elf_file),
186 self.address(elf_file),194 self.address(elf_file),
187 self.address(elf_file) + @as(i64, @intCast(self.size)),195 self.address(elf_file) + @as(i64, @intCast(self.size)),
188 });196 });
189197
190 const expand_section = if (atom_placement) |placement_index|198 const expand_section = if (atom_placement) |placement_ref|
191 zo.atom(placement_index).?.next_index == 0199 elf_file.atom(placement_ref).?.nextAtom(elf_file) == null
192 else200 else
193 true;201 true;
194 if (expand_section) {202 if (expand_section) {
195 const needed_size: u64 = @intCast(self.value + @as(i64, @intCast(self.size)));203 const needed_size: u64 = @intCast(self.value + @as(i64, @intCast(self.size)));
196 try elf_file.growAllocSection(self.output_section_index, needed_size);204 try elf_file.growAllocSection(self.output_section_index, needed_size);
197 last_atom_index.* = self.atom_index;205 last_atom_ref.* = self.ref();
198206
199 const zig_object = elf_file.zigObjectPtr().?;207 switch (self.file(elf_file).?) {
200 if (zig_object.dwarf) |_| {208 .zig_object => |zo| if (zo.dwarf) |_| {
201 // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address209 // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address
202 // range of the compilation unit. When we expand the text section, this range changes,210 // range of the compilation unit. When we expand the text section, this range changes,
203 // so the DW_TAG.compile_unit tag of the .debug_info section becomes dirty.211 // so the DW_TAG.compile_unit tag of the .debug_info section becomes dirty.
204 zig_object.debug_info_section_dirty = true;212 zo.debug_info_section_dirty = true;
205 // This becomes dirty for the same reason. We could potentially make this more213 // This becomes dirty for the same reason. We could potentially make this more
206 // fine-grained with the addition of support for more compilation units. It is planned to214 // fine-grained with the addition of support for more compilation units. It is planned to
207 // model each package as a different compilation unit.215 // model each package as a different compilation unit.
208 zig_object.debug_aranges_section_dirty = true;216 zo.debug_aranges_section_dirty = true;
209 zig_object.debug_rnglists_section_dirty = true;217 zo.debug_rnglists_section_dirty = true;
218 },
219 else => {},
210 }220 }
211 }221 }
212 shdr.sh_addralign = @max(shdr.sh_addralign, self.alignment.toByteUnits().?);222 shdr.sh_addralign = @max(shdr.sh_addralign, self.alignment.toByteUnits().?);
...@@ -214,21 +224,21 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void {...@@ -214,21 +224,21 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void {
214 // This function can also reallocate an atom.224 // This function can also reallocate an atom.
215 // In this case we need to "unplug" it from its previous location before225 // In this case we need to "unplug" it from its previous location before
216 // plugging it in to its new location.226 // plugging it in to its new location.
217 if (zo.atom(self.prev_index)) |prev| {227 if (self.prevAtom(elf_file)) |prev| {
218 prev.next_index = self.next_index;228 prev.next_atom_ref = self.next_atom_ref;
219 }229 }
220 if (zo.atom(self.next_index)) |next| {230 if (self.nextAtom(elf_file)) |next| {
221 next.prev_index = self.prev_index;231 next.prev_atom_ref = self.prev_atom_ref;
222 }232 }
223233
224 if (atom_placement) |big_atom_index| {234 if (atom_placement) |big_atom_ref| {
225 const big_atom = zo.atom(big_atom_index).?;235 const big_atom = elf_file.atom(big_atom_ref).?;
226 self.prev_index = big_atom_index;236 self.prev_atom_ref = big_atom_ref;
227 self.next_index = big_atom.next_index;237 self.next_atom_ref = big_atom.next_atom_ref;
228 big_atom.next_index = self.atom_index;238 big_atom.next_atom_ref = self.ref();
229 } else {239 } else {
230 self.prev_index = 0;240 self.prev_atom_ref = .{ .index = 0, .file = 0 };
231 self.next_index = 0;241 self.next_atom_ref = .{ .index = 0, .file = 0 };
232 }242 }
233 if (free_list_removal) |i| {243 if (free_list_removal) |i| {
234 _ = free_list.swapRemove(i);244 _ = free_list.swapRemove(i);
...@@ -248,64 +258,70 @@ pub fn grow(self: *Atom, elf_file: *Elf) !void {...@@ -248,64 +258,70 @@ pub fn grow(self: *Atom, elf_file: *Elf) !void {
248}258}
249259
250pub fn free(self: *Atom, elf_file: *Elf) void {260pub fn free(self: *Atom, elf_file: *Elf) void {
251 log.debug("freeAtom {d} ({s})", .{ self.atom_index, self.name(elf_file) });261 log.debug("freeAtom atom({}) ({s})", .{ self.ref(), self.name(elf_file) });
252262
253 const zo = elf_file.zigObjectPtr().?;
254 const comp = elf_file.base.comp;263 const comp = elf_file.base.comp;
255 const gpa = comp.gpa;264 const gpa = comp.gpa;
256 const shndx = self.output_section_index;265 const shndx = self.output_section_index;
257 const slice = elf_file.sections.slice();266 const slice = elf_file.sections.slice();
258 const free_list = &slice.items(.free_list)[shndx];267 const free_list = &slice.items(.free_list)[shndx];
259 const last_atom_index = &slice.items(.last_atom_index)[shndx];268 const last_atom_ref = &slice.items(.last_atom)[shndx];
260 var already_have_free_list_node = false;269 var already_have_free_list_node = false;
261 {270 {
262 var i: usize = 0;271 var i: usize = 0;
263 // TODO turn free_list into a hash map272 // TODO turn free_list into a hash map
264 while (i < free_list.items.len) {273 while (i < free_list.items.len) {
265 if (free_list.items[i] == self.atom_index) {274 if (free_list.items[i].eql(self.ref())) {
266 _ = free_list.swapRemove(i);275 _ = free_list.swapRemove(i);
267 continue;276 continue;
268 }277 }
269 if (free_list.items[i] == self.prev_index) {278 if (self.prevAtom(elf_file)) |prev_atom| {
270 already_have_free_list_node = true;279 if (free_list.items[i].eql(prev_atom.ref())) {
280 already_have_free_list_node = true;
281 }
271 }282 }
272 i += 1;283 i += 1;
273 }284 }
274 }285 }
275286
276 if (zo.atom(last_atom_index.*)) |last_atom| {287 if (elf_file.atom(last_atom_ref.*)) |last_atom| {
277 if (last_atom.atom_index == self.atom_index) {288 if (last_atom.ref().eql(self.ref())) {
278 if (zo.atom(self.prev_index)) |_| {289 if (self.prevAtom(elf_file)) |prev_atom| {
279 // TODO shrink the section size here290 // TODO shrink the section size here
280 last_atom_index.* = self.prev_index;291 last_atom_ref.* = prev_atom.ref();
281 } else {292 } else {
282 last_atom_index.* = 0;293 last_atom_ref.* = .{};
283 }294 }
284 }295 }
285 }296 }
286297
287 if (zo.atom(self.prev_index)) |prev| {298 if (self.prevAtom(elf_file)) |prev_atom| {
288 prev.next_index = self.next_index;299 prev_atom.next_atom_ref = self.next_atom_ref;
289 if (!already_have_free_list_node and prev.*.freeListEligible(elf_file)) {300 if (!already_have_free_list_node and prev_atom.*.freeListEligible(elf_file)) {
290 // The free list is heuristics, it doesn't have to be perfect, so we can301 // The free list is heuristics, it doesn't have to be perfect, so we can
291 // ignore the OOM here.302 // ignore the OOM here.
292 free_list.append(gpa, prev.atom_index) catch {};303 free_list.append(gpa, prev_atom.ref()) catch {};
293 }304 }
294 } else {305 } else {
295 self.prev_index = 0;306 self.prev_atom_ref = .{};
296 }307 }
297308
298 if (zo.atom(self.next_index)) |next| {309 if (self.nextAtom(elf_file)) |next_atom| {
299 next.prev_index = self.prev_index;310 next_atom.prev_atom_ref = self.prev_atom_ref;
300 } else {311 } else {
301 self.next_index = 0;312 self.next_atom_ref = .{};
302 }313 }
303314
304 // TODO create relocs free list315 switch (self.file(elf_file).?) {
305 self.freeRelocs(zo);316 .zig_object => |zo| {
306 // TODO figure out how to free input section mappind in ZigModule317 // TODO create relocs free list
307 // const zig_object = elf_file.zigObjectPtr().?318 self.freeRelocs(zo);
308 // assert(zig_object.atoms.swapRemove(self.atom_index));319 // TODO figure out how to free input section mappind in ZigModule
320 // const zig_object = elf_file.zigObjectPtr().?
321 // assert(zig_object.atoms.swapRemove(self.atom_index));
322 },
323 else => {},
324 }
309 self.* = .{};325 self.* = .{};
310}326}
311327
src/link/Elf/Symbol.zig+1-1
...@@ -11,7 +11,7 @@ file_index: File.Index = 0,...@@ -11,7 +11,7 @@ file_index: File.Index = 0,
1111
12/// Reference to Atom or merge subsection containing this symbol if any.12/// Reference to Atom or merge subsection containing this symbol if any.
13/// Use `atom` or `mergeSubsection` to get the pointer to the atom.13/// Use `atom` or `mergeSubsection` to get the pointer to the atom.
14ref: Elf.Ref = .{ .index = 0, .file = 0 },14ref: Elf.Ref = .{},
1515
16/// Assigned output section index for this symbol.16/// Assigned output section index for this symbol.
17output_section_index: u32 = 0,17output_section_index: u32 = 0,