Skip to content

Commit 30b4a87

Browse files
authored
Merge pull request #20873 from ziglang/elf-ownership
elf: clean up resource ownership in the linker
2 parents f219286 + 3f10217 commit 30b4a87

17 files changed

+1203
-1006
lines changed

src/link/Elf.zig

Lines changed: 166 additions & 532 deletions
Large diffs are not rendered by default.

src/link/Elf/Atom.zig

Lines changed: 54 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -30,23 +30,25 @@ atom_index: Index = 0,
3030
prev_index: Index = 0,
3131
next_index: Index = 0,
3232

33-
/// Flags we use for state tracking.
34-
flags: Flags = .{},
33+
/// Specifies whether this atom is alive or has been garbage collected.
34+
alive: bool = true,
35+
36+
/// Specifies if the atom has been visited during garbage collection.
37+
visited: bool = false,
3538

3639
extra_index: u32 = 0,
3740

3841
pub const Alignment = @import("../../InternPool.zig").Alignment;
3942

40-
pub fn name(self: Atom, elf_file: *Elf) []const u8 {
43+
pub fn name(self: Atom, elf_file: *Elf) [:0]const u8 {
4144
const file_ptr = self.file(elf_file).?;
4245
return switch (file_ptr) {
4346
inline else => |x| x.getString(self.name_offset),
4447
};
4548
}
4649

4750
pub fn address(self: Atom, elf_file: *Elf) i64 {
48-
const shndx = self.outputShndx() orelse return self.value;
49-
const shdr = elf_file.shdrs.items[shndx];
51+
const shdr = elf_file.shdrs.items[self.output_section_index];
5052
return @as(i64, @intCast(shdr.sh_addr)) + self.value;
5153
}
5254

@@ -55,7 +57,7 @@ pub fn debugTombstoneValue(self: Atom, target: Symbol, elf_file: *Elf) ?u64 {
5557
if (msub.alive) return null;
5658
}
5759
if (target.atom(elf_file)) |atom_ptr| {
58-
if (atom_ptr.flags.alive) return null;
60+
if (atom_ptr.alive) return null;
5961
}
6062
const atom_name = self.name(elf_file);
6163
if (!mem.startsWith(u8, atom_name, ".debug")) return null;
@@ -67,8 +69,7 @@ pub fn file(self: Atom, elf_file: *Elf) ?File {
6769
}
6870

6971
pub fn thunk(self: Atom, elf_file: *Elf) *Thunk {
70-
assert(self.flags.thunk);
71-
const extras = self.extra(elf_file).?;
72+
const extras = self.extra(elf_file);
7273
return elf_file.thunk(extras.thunk);
7374
}
7475

@@ -85,11 +86,6 @@ pub fn relocsShndx(self: Atom) ?u32 {
8586
return self.relocs_section_index;
8687
}
8788

88-
pub fn outputShndx(self: Atom) ?u32 {
89-
if (self.output_section_index == 0) return null;
90-
return self.output_section_index;
91-
}
92-
9389
pub fn priority(self: Atom, elf_file: *Elf) u64 {
9490
const index = self.file(elf_file).?.index();
9591
return (@as(u64, @intCast(index)) << 32) | @as(u64, @intCast(self.input_section_index));
@@ -99,16 +95,18 @@ pub fn priority(self: Atom, elf_file: *Elf) u64 {
9995
/// File offset relocation happens transparently, so it is not included in
10096
/// this calculation.
10197
pub fn capacity(self: Atom, elf_file: *Elf) u64 {
102-
const next_addr = if (elf_file.atom(self.next_index)) |next|
98+
const zo = elf_file.zigObjectPtr().?;
99+
const next_addr = if (zo.atom(self.next_index)) |next|
103100
next.address(elf_file)
104101
else
105102
std.math.maxInt(u32);
106103
return @intCast(next_addr - self.address(elf_file));
107104
}
108105

109106
pub fn freeListEligible(self: Atom, elf_file: *Elf) bool {
107+
const zo = elf_file.zigObjectPtr().?;
110108
// No need to keep a free list node for the last block.
111-
const next = elf_file.atom(self.next_index) orelse return false;
109+
const next = zo.atom(self.next_index) orelse return false;
112110
const cap: u64 = @intCast(next.address(elf_file) - self.address(elf_file));
113111
const ideal_cap = Elf.padToIdeal(self.size);
114112
if (cap <= ideal_cap) return false;
@@ -117,8 +115,9 @@ pub fn freeListEligible(self: Atom, elf_file: *Elf) bool {
117115
}
118116

119117
pub fn allocate(self: *Atom, elf_file: *Elf) !void {
120-
const shdr = &elf_file.shdrs.items[self.outputShndx().?];
121-
const meta = elf_file.last_atom_and_free_list_table.getPtr(self.outputShndx().?).?;
118+
const zo = elf_file.zigObjectPtr().?;
119+
const shdr = &elf_file.shdrs.items[self.output_section_index];
120+
const meta = elf_file.last_atom_and_free_list_table.getPtr(self.output_section_index).?;
122121
const free_list = &meta.free_list;
123122
const last_atom_index = &meta.last_atom_index;
124123
const new_atom_ideal_capacity = Elf.padToIdeal(self.size);
@@ -137,7 +136,7 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void {
137136
var i: usize = if (elf_file.base.child_pid == null) 0 else free_list.items.len;
138137
while (i < free_list.items.len) {
139138
const big_atom_index = free_list.items[i];
140-
const big_atom = elf_file.atom(big_atom_index).?;
139+
const big_atom = zo.atom(big_atom_index).?;
141140
// We now have a pointer to a live atom that has too much capacity.
142141
// Is it enough that we could fit this new atom?
143142
const cap = big_atom.capacity(elf_file);
@@ -169,7 +168,7 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void {
169168
free_list_removal = i;
170169
}
171170
break :blk @intCast(new_start_vaddr);
172-
} else if (elf_file.atom(last_atom_index.*)) |last| {
171+
} else if (zo.atom(last_atom_index.*)) |last| {
173172
const ideal_capacity = Elf.padToIdeal(last.size);
174173
const ideal_capacity_end_vaddr = @as(u64, @intCast(last.value)) + ideal_capacity;
175174
const new_start_vaddr = self.alignment.forward(ideal_capacity_end_vaddr);
@@ -189,12 +188,12 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void {
189188
});
190189

191190
const expand_section = if (atom_placement) |placement_index|
192-
elf_file.atom(placement_index).?.next_index == 0
191+
zo.atom(placement_index).?.next_index == 0
193192
else
194193
true;
195194
if (expand_section) {
196195
const needed_size: u64 = @intCast(self.value + @as(i64, @intCast(self.size)));
197-
try elf_file.growAllocSection(self.outputShndx().?, needed_size);
196+
try elf_file.growAllocSection(self.output_section_index, needed_size);
198197
last_atom_index.* = self.atom_index;
199198

200199
const zig_object = elf_file.zigObjectPtr().?;
@@ -214,15 +213,15 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void {
214213
// This function can also reallocate an atom.
215214
// In this case we need to "unplug" it from its previous location before
216215
// plugging it in to its new location.
217-
if (elf_file.atom(self.prev_index)) |prev| {
216+
if (zo.atom(self.prev_index)) |prev| {
218217
prev.next_index = self.next_index;
219218
}
220-
if (elf_file.atom(self.next_index)) |next| {
219+
if (zo.atom(self.next_index)) |next| {
221220
next.prev_index = self.prev_index;
222221
}
223222

224223
if (atom_placement) |big_atom_index| {
225-
const big_atom = elf_file.atom(big_atom_index).?;
224+
const big_atom = zo.atom(big_atom_index).?;
226225
self.prev_index = big_atom_index;
227226
self.next_index = big_atom.next_index;
228227
big_atom.next_index = self.atom_index;
@@ -234,7 +233,7 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void {
234233
_ = free_list.swapRemove(i);
235234
}
236235

237-
self.flags.alive = true;
236+
self.alive = true;
238237
}
239238

240239
pub fn shrink(self: *Atom, elf_file: *Elf) void {
@@ -250,9 +249,10 @@ pub fn grow(self: *Atom, elf_file: *Elf) !void {
250249
pub fn free(self: *Atom, elf_file: *Elf) void {
251250
log.debug("freeAtom {d} ({s})", .{ self.atom_index, self.name(elf_file) });
252251

252+
const zo = elf_file.zigObjectPtr().?;
253253
const comp = elf_file.base.comp;
254254
const gpa = comp.gpa;
255-
const shndx = self.outputShndx().?;
255+
const shndx = self.output_section_index;
256256
const meta = elf_file.last_atom_and_free_list_table.getPtr(shndx).?;
257257
const free_list = &meta.free_list;
258258
const last_atom_index = &meta.last_atom_index;
@@ -272,9 +272,9 @@ pub fn free(self: *Atom, elf_file: *Elf) void {
272272
}
273273
}
274274

275-
if (elf_file.atom(last_atom_index.*)) |last_atom| {
275+
if (zo.atom(last_atom_index.*)) |last_atom| {
276276
if (last_atom.atom_index == self.atom_index) {
277-
if (elf_file.atom(self.prev_index)) |_| {
277+
if (zo.atom(self.prev_index)) |_| {
278278
// TODO shrink the section size here
279279
last_atom_index.* = self.prev_index;
280280
} else {
@@ -283,7 +283,7 @@ pub fn free(self: *Atom, elf_file: *Elf) void {
283283
}
284284
}
285285

286-
if (elf_file.atom(self.prev_index)) |prev| {
286+
if (zo.atom(self.prev_index)) |prev| {
287287
prev.next_index = self.next_index;
288288
if (!already_have_free_list_node and prev.*.freeListEligible(elf_file)) {
289289
// The free list is heuristics, it doesn't have to be perfect, so we can
@@ -294,7 +294,7 @@ pub fn free(self: *Atom, elf_file: *Elf) void {
294294
self.prev_index = 0;
295295
}
296296

297-
if (elf_file.atom(self.next_index)) |next| {
297+
if (zo.atom(self.next_index)) |next| {
298298
next.prev_index = self.prev_index;
299299
} else {
300300
self.next_index = 0;
@@ -313,7 +313,7 @@ pub fn relocs(self: Atom, elf_file: *Elf) []const elf.Elf64_Rela {
313313
switch (self.file(elf_file).?) {
314314
.zig_object => |x| return x.relocs.items[shndx].items,
315315
.object => |x| {
316-
const extras = self.extra(elf_file).?;
316+
const extras = self.extra(elf_file);
317317
return x.relocs.items[extras.rel_index..][0..extras.rel_count];
318318
},
319319
else => unreachable,
@@ -337,12 +337,12 @@ pub fn writeRelocs(self: Atom, elf_file: *Elf, out_relocs: *std.ArrayList(elf.El
337337
var r_addend = rel.r_addend;
338338
var r_sym: u32 = 0;
339339
switch (target.type(elf_file)) {
340-
elf.STT_SECTION => if (target.mergeSubsection(elf_file)) |msub| {
341-
r_addend += @intCast(target.address(.{}, elf_file));
342-
r_sym = elf_file.sectionSymbolOutputSymtabIndex(msub.mergeSection(elf_file).output_section_index);
343-
} else {
340+
elf.STT_SECTION => {
344341
r_addend += @intCast(target.address(.{}, elf_file));
345-
r_sym = elf_file.sectionSymbolOutputSymtabIndex(target.outputShndx().?);
342+
r_sym = if (target.outputShndx(elf_file)) |osec|
343+
elf_file.sectionSymbolOutputSymtabIndex(osec)
344+
else
345+
0;
346346
},
347347
else => {
348348
r_sym = target.outputSymtabIndex(elf_file) orelse 0;
@@ -366,10 +366,12 @@ pub fn writeRelocs(self: Atom, elf_file: *Elf, out_relocs: *std.ArrayList(elf.El
366366
}
367367

368368
pub fn fdes(self: Atom, elf_file: *Elf) []Fde {
369-
if (!self.flags.fde) return &[0]Fde{};
370-
const extras = self.extra(elf_file).?;
371-
const object = self.file(elf_file).?.object;
372-
return object.fdes.items[extras.fde_start..][0..extras.fde_count];
369+
const extras = self.extra(elf_file);
370+
return switch (self.file(elf_file).?) {
371+
.shared_object => unreachable,
372+
.linker_defined, .zig_object => &[0]Fde{},
373+
.object => |x| x.fdes.items[extras.fde_start..][0..extras.fde_count],
374+
};
373375
}
374376

375377
pub fn markFdesDead(self: Atom, elf_file: *Elf) void {
@@ -712,9 +714,9 @@ fn reportUndefined(
712714
{
713715
const gop = try undefs.getOrPut(sym_index);
714716
if (!gop.found_existing) {
715-
gop.value_ptr.* = std.ArrayList(Atom.Index).init(gpa);
717+
gop.value_ptr.* = std.ArrayList(Elf.Ref).init(gpa);
716718
}
717-
try gop.value_ptr.append(self.atom_index);
719+
try gop.value_ptr.append(.{ .index = self.atom_index, .file = self.file_index });
718720
return true;
719721
}
720722

@@ -1001,25 +1003,23 @@ const AddExtraOpts = struct {
10011003
rel_count: ?u32 = null,
10021004
};
10031005

1004-
pub fn addExtra(atom: *Atom, opts: AddExtraOpts, elf_file: *Elf) !void {
1005-
if (atom.extra(elf_file) == null) {
1006-
atom.extra_index = try elf_file.addAtomExtra(.{});
1007-
}
1008-
var extras = atom.extra(elf_file).?;
1006+
pub fn addExtra(atom: *Atom, opts: AddExtraOpts, elf_file: *Elf) void {
1007+
const file_ptr = atom.file(elf_file).?;
1008+
var extras = file_ptr.atomExtra(atom.extra_index);
10091009
inline for (@typeInfo(@TypeOf(opts)).Struct.fields) |field| {
10101010
if (@field(opts, field.name)) |x| {
10111011
@field(extras, field.name) = x;
10121012
}
10131013
}
1014-
atom.setExtra(extras, elf_file);
1014+
file_ptr.setAtomExtra(atom.extra_index, extras);
10151015
}
10161016

1017-
pub inline fn extra(atom: Atom, elf_file: *Elf) ?Extra {
1018-
return elf_file.atomExtra(atom.extra_index);
1017+
pub inline fn extra(atom: Atom, elf_file: *Elf) Extra {
1018+
return atom.file(elf_file).?.atomExtra(atom.extra_index);
10191019
}
10201020

10211021
pub inline fn setExtra(atom: Atom, extras: Extra, elf_file: *Elf) void {
1022-
elf_file.setAtomExtra(atom.extra_index, extras);
1022+
atom.file(elf_file).?.setAtomExtra(atom.extra_index, extras);
10231023
}
10241024

10251025
pub fn format(
@@ -1061,37 +1061,23 @@ fn format2(
10611061
atom.atom_index, atom.name(elf_file), atom.address(elf_file),
10621062
atom.output_section_index, atom.alignment, atom.size,
10631063
});
1064-
if (atom.flags.fde) {
1064+
if (atom.fdes(elf_file).len > 0) {
10651065
try writer.writeAll(" : fdes{ ");
1066-
const extras = atom.extra(elf_file).?;
1066+
const extras = atom.extra(elf_file);
10671067
for (atom.fdes(elf_file), extras.fde_start..) |fde, i| {
10681068
try writer.print("{d}", .{i});
10691069
if (!fde.alive) try writer.writeAll("([*])");
10701070
if (i - extras.fde_start < extras.fde_count - 1) try writer.writeAll(", ");
10711071
}
10721072
try writer.writeAll(" }");
10731073
}
1074-
if (!atom.flags.alive) {
1074+
if (!atom.alive) {
10751075
try writer.writeAll(" : [*]");
10761076
}
10771077
}
10781078

10791079
pub const Index = u32;
10801080

1081-
pub const Flags = packed struct {
1082-
/// Specifies whether this atom is alive or has been garbage collected.
1083-
alive: bool = true,
1084-
1085-
/// Specifies if the atom has been visited during garbage collection.
1086-
visited: bool = false,
1087-
1088-
/// Whether this atom has a range extension thunk.
1089-
thunk: bool = false,
1090-
1091-
/// Whether this atom has FDE records.
1092-
fde: bool = false,
1093-
};
1094-
10951081
const x86_64 = struct {
10961082
fn scanReloc(
10971083
atom: Atom,

0 commit comments

Comments
 (0)