Skip to content

Commit

Permalink
elf: fix pruning special symbols _init and _fini
Browse files Browse the repository at this point in the history
  • Loading branch information
kubkon committed Jun 18, 2022
1 parent 94b628d commit f0de089
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 28 deletions.
57 changes: 31 additions & 26 deletions src/Elf.zig
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},
atoms: std.AutoHashMapUnmanaged(u16, ?*Atom) = .{},
atom_table: std.AutoHashMapUnmanaged(u32, *Atom) = .{},

/// Special st_other value used internally by zld to mark symbol
/// as GCed.
pub const STV_GC: u8 = std.math.maxInt(u8);

pub const SymbolWithLoc = struct {
/// Index in the respective symbol table.
sym_index: u32,
Expand Down Expand Up @@ -229,8 +233,6 @@ pub fn flush(self: *Elf) !void {
try self.resolveSymbolsInArchives();
try self.resolveSpecialSymbols();

// self.logSymtab();

for (self.unresolved.keys()) |ndx| {
const global = self.globals.values()[ndx];
const object = self.objects.items[global.file.?];
Expand Down Expand Up @@ -998,12 +1000,13 @@ fn gcAtoms(self: *Elf) !void {
var retained = std.AutoHashMap(*Atom, void).init(self.base.allocator);
defer retained.deinit();

{
const global = self.globals.get("_start").?;
for (&[_][]const u8{ "_start", "_init", "_fini" }) |sym_name| {
const global = self.globals.get(sym_name) orelse continue;
const atom: *Atom = if (global.file) |file|
self.objects.items[file].atom_table.get(global.sym_index).?
else
self.atom_table.get(global.sym_index).?;
log.debug("marking '{s}' as GC root", .{atom.getName(self)});
try retained.putNoClobber(atom, {});
try stack.append(atom);
}
Expand Down Expand Up @@ -1031,24 +1034,33 @@ fn gcAtoms(self: *Elf) !void {
var atom: *Atom = entry.value_ptr.*.?;

while (true) {
try retained.putNoClobber(atom, {});
try stack.append(atom);
const gop = try retained.getOrPut(atom);
if (!gop.found_existing) {
log.debug("marking '{s}' as GC root", .{atom.getName(self)});
try stack.append(atom);
}
if (atom.prev) |prev| {
atom = prev;
} else break;
}
}

while (stack.popOrNull()) |src_atom| {
const src_name = src_atom.getName(self);
log.debug("alive: '{s}'", .{src_name});
log.debug("source atom '{s}'", .{src_atom.getName(self)});

for (src_atom.relocs.items) |rel| {
if (src_atom.getTargetAtom(self, rel)) |target_atom| {
const gop = try retained.getOrPut(target_atom);
if (!gop.found_existing) {
log.debug(" (reached target atom '{s}')", .{target_atom.getName(self)});
try stack.append(target_atom);
}
} else {
const tsym_name = self.getSymbolName(.{
.sym_index = rel.r_sym(),
.file = src_atom.file,
});
log.debug(" (dead link to symbol %{d}: {s})", .{ rel.r_sym(), tsym_name });
}
}
}
Expand All @@ -1069,37 +1081,27 @@ fn gcAtoms(self: *Elf) !void {

if (!retained.contains(atom)) {
// Dead atom; remove.
log.debug("dead: '{s}'", .{atom.getName(self)});
log.debug("dead atom '{s}'", .{atom.getName(self)});
if (atom.file) |file| {
const object = self.objects.items[file];
log.debug(" (defined in {s})", .{object.name});
}

{
const sym = atom.getSymbolPtr(self);
sym.* = .{
.st_name = 0,
.st_info = 0,
.st_other = 0,
.st_shndx = 0,
.st_value = 0,
.st_size = 0,
};
sym.st_other = STV_GC; // repurposed for GC
}

for (atom.contained.items) |contained| {
const contained_sym = self.getSymbolPtr(.{
.sym_index = contained.local_sym_index,
.file = atom.file,
});
contained_sym.* = .{
.st_name = 0,
.st_info = 0,
.st_other = 0,
.st_shndx = 0,
.st_value = 0,
.st_size = 0,
};
log.debug(" (pruning contained symbol '{s}')", .{self.getSymbolName(.{
.sym_index = contained.local_sym_index,
.file = atom.file,
})});
contained_sym.st_other = STV_GC; // repurposed for GC
}

shdr.sh_size -= atom.size;
Expand Down Expand Up @@ -1871,6 +1873,7 @@ fn writeSymtab(self: *Elf) !void {
if (st_type == elf.STT_NOTYPE) continue;
if (sym.st_other == @enumToInt(elf.STV.INTERNAL)) continue;
if (sym.st_other == @enumToInt(elf.STV.HIDDEN)) continue;
if (sym.st_other == STV_GC) continue;

const sym_name = object.getString(sym.st_name);
var out_sym = sym;
Expand All @@ -1884,6 +1887,7 @@ fn writeSymtab(self: *Elf) !void {
if (st_bind != elf.STB_LOCAL) continue;
if (sym.st_other == @enumToInt(elf.STV.INTERNAL)) continue;
if (sym.st_other == @enumToInt(elf.STV.HIDDEN)) continue;
if (sym.st_other == STV_GC) continue;
try symtab.append(sym);
}

Expand All @@ -1892,7 +1896,8 @@ fn writeSymtab(self: *Elf) !void {
try symtab.ensureUnusedCapacity(self.globals.count());
for (self.globals.values()) |global| {
var sym = self.getSymbol(global);
if (sym.st_name == 0) continue;
assert(sym.st_name > 0);
if (sym.st_other == STV_GC) continue;
// TODO refactor
if (sym.st_info >> 4 == elf.STB_LOCAL) continue;
const sym_name = self.getSymbolName(global);
Expand Down
4 changes: 3 additions & 1 deletion src/Elf/Atom.zig
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,10 @@ pub fn getTargetAtom(self: Atom, elf_file: *Elf, rel: elf.Elf64_Rela) ?*Atom {
.sym_index = r_sym,
.file = self.file,
});
log.debug(" (getTargetAtom: %{d}: {s}, r_type={d})", .{ r_sym, tsym_name, r_type });

switch (r_type) {
elf.R_X86_64_GOTPCRELX, elf.R_X86_64_GOTPCREL => {
elf.R_X86_64_REX_GOTPCRELX, elf.R_X86_64_GOTPCRELX, elf.R_X86_64_GOTPCREL => {
const global = elf_file.globals.get(tsym_name).?;
const got_atom = elf_file.got_entries_map.get(global).?;
return got_atom;
Expand Down Expand Up @@ -184,6 +185,7 @@ fn getTargetAddress(self: Atom, r_sym: u32, elf_file: *Elf) u64 {
const tsym_st_type = tsym.st_info & 0xf;
const is_section = tsym_st_type == elf.STT_SECTION;
const is_local = is_section or tsym_st_bind == elf.STB_LOCAL;
log.debug(" (getTargetAddress: %{d}: {s}, local? {})", .{ r_sym, tsym_name, is_local });

if (!is_local) {
const global = elf_file.globals.get(tsym_name).?;
Expand Down
2 changes: 1 addition & 1 deletion src/test.zig
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ pub const TestContext = struct {
.lib_dirs = &[0][]const u8{},
.framework_dirs = &[0][]const u8{},
.rpath_list = &[0][]const u8{},
.gc_sections = false,
.gc_sections = true,
});
defer zld.deinit();

Expand Down

0 comments on commit f0de089

Please sign in to comment.