wasm-linker: mark symbols and its references

Symbols which are exported to the host, or contain the `NO_STRIP`
flag, will be marked. All symbols which are referenced by this symbol
are marked likewise. We achieve this by parsing all relocations of a
symbol, and then marking the symbol it points to within the relocation.
This commit is contained in:
Luuk de Gram
2023-11-13 15:25:17 +01:00
parent f3626eb816
commit 589aef1537
2 changed files with 54 additions and 1 deletions

View File

@@ -3439,12 +3439,13 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l
try wasm.setupInitFunctions();
try wasm.setupStart();
try wasm.setupImports();
for (wasm.objects.items, 0..) |*object, object_index| {
try object.parseIntoAtoms(gpa, @as(u16, @intCast(object_index)), wasm);
}
wasm.markReferences();
try wasm.setupImports();
try wasm.allocateAtoms();
try wasm.setupMemory();
wasm.allocateVirtualAddresses();
@@ -3529,6 +3530,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
try wasm.setupInitFunctions();
try wasm.setupErrorsLen();
try wasm.setupStart();
wasm.markReferences();
try wasm.setupImports();
if (wasm.base.options.module) |mod| {
var decl_it = wasm.decls.iterator();
@@ -5026,3 +5028,38 @@ pub fn storeDeclType(wasm: *Wasm, decl_index: InternPool.DeclIndex, func_type: s
try wasm.atom_types.put(wasm.base.allocator, atom_index, index);
return index;
}
/// Verifies all resolved symbols and checks whether itself needs to be marked alive,
/// as well as any of its references.
fn markReferences(wasm: *Wasm) void {
const tracy = trace(@src());
defer tracy.end();
for (wasm.resolved_symbols.keys()) |sym_loc| {
const sym = sym_loc.getSymbol(wasm);
if (sym.isExported(wasm.base.options.rdynamic) or sym.isNoStrip()) {
wasm.mark(sym_loc);
}
}
}
/// Marks a symbol as 'alive' recursively so itself and any references it contains to
/// other symbols will not be omit from the binary.
fn mark(wasm: *Wasm, loc: SymbolLoc) void {
const symbol = loc.getSymbol(wasm);
if (symbol.isAlive()) {
// Symbol is already marked alive, including its references.
// This means we can skip it so we don't end up marking the same symbols
// multiple times.
return;
}
symbol.mark();
if (wasm.symbol_atom.get(loc)) |atom_index| {
const atom = wasm.getAtom(atom_index);
const relocations: []const types.Relocation = atom.relocs.items;
for (relocations) |reloc| {
const target_loc: SymbolLoc = .{ .index = reloc.index, .file = loc.file };
wasm.mark(target_loc.finalLoc(wasm));
}
}
}

View File

@@ -79,6 +79,9 @@ pub const Flag = enum(u32) {
WASM_SYM_NO_STRIP = 0x80,
/// Indicates a symbol is TLS
WASM_SYM_TLS = 0x100,
/// Zig specific flag. Uses the most significant bit of the flag to annotate whether a symbol is
/// alive or not. Dead symbols are allowed to be garbage collected.
alive = 0x80000000,
};
/// Verifies if the given symbol should be imported from the
@@ -92,6 +95,19 @@ pub fn requiresImport(symbol: Symbol) bool {
return true;
}
/// Marks a symbol as 'alive', ensuring the garbage collector will not collect the trash.
pub fn mark(symbol: *Symbol) void {
symbol.flags |= @intFromEnum(Flag.alive);
}
pub fn isAlive(symbol: Symbol) bool {
return symbol.flags & @intFromEnum(Flag.alive) != 0;
}
pub fn isDead(symbol: Symbol) bool {
return symbol.flags & @intFromEnum(Flag.alive) == 0;
}
pub fn isTLS(symbol: Symbol) bool {
return symbol.flags & @intFromEnum(Flag.WASM_SYM_TLS) != 0;
}