authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-05-26 16:25:26+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-06-24 08:12:17+02:00
log16daf3f3bcb27b2e0b0f45ee48c69824a4804981
tree20d218d50128c1cbf3ecb09fa59dab44fcc02e57
parent1a3f58f5e56ad166eb5441c6960a8fac36b4ff5f

wasm-link: Discard old symbols correctly

When a new symbol is resolved to an existing symbol where it doesn't overwrite the existing symbol, we now add this symbol to the discarded list. This is required so when any relocation points to the symbol, we can retrieve the correct symbol it's resolved by instead.

3 files changed, 15 insertions(+), 10 deletions(-)

src/link/Wasm.zig+10-7
......@@ -491,6 +491,7 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {
491491 return error.SymbolCollision;
492492 }
493493
494 try self.discarded.put(self.base.allocator, location, existing_loc);
494495 continue; // Do not overwrite defined symbols with undefined symbols
495496 }
496497
......@@ -503,6 +504,7 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {
503504
504505 // when both symbols are weak, we skip overwriting
505506 if (existing_sym.isWeak() and symbol.isWeak()) {
507 try self.discarded.put(self.base.allocator, location, existing_loc);
506508 continue;
507509 }
508510
......@@ -510,15 +512,13 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {
510512 log.debug("Overwriting symbol '{s}'", .{sym_name});
511513 log.debug(" old definition in '{s}'", .{existing_file_path});
512514 log.debug(" new definition in '{s}'", .{object.name});
513 try self.discarded.putNoClobber(self.base.allocator, maybe_existing.value_ptr.*, location);
515 try self.discarded.putNoClobber(self.base.allocator, existing_loc, location);
514516 maybe_existing.value_ptr.* = location;
515517 try self.globals.put(self.base.allocator, sym_name_index, location);
516518 try self.resolved_symbols.put(self.base.allocator, location, {});
517519 assert(self.resolved_symbols.swapRemove(existing_loc));
518520 if (existing_sym.isUndefined()) {
519 // ensure order remains intact in case we later
520 // resolve symbols again in a loop
521 assert(self.undefs.orderedRemove(sym_name));
521 assert(self.undefs.swapRemove(sym_name));
522522 }
523523 }
524524}
......@@ -1004,7 +1004,6 @@ pub fn updateDeclExports(
10041004 switch (exp.options.linkage) {
10051005 .Internal => {
10061006 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
1007 symbol.setFlag(.WASM_SYM_BINDING_WEAK);
10081007 },
10091008 .Weak => {
10101009 symbol.setFlag(.WASM_SYM_BINDING_WEAK);
......@@ -1026,6 +1025,7 @@ pub fn updateDeclExports(
10261025 }
10271026
10281027 symbol.setGlobal(true);
1028 symbol.setUndefined(false);
10291029 try self.globals.put(
10301030 self.base.allocator,
10311031 export_name,
......@@ -1034,6 +1034,7 @@ pub fn updateDeclExports(
10341034
10351035 // if the symbol was previously undefined, remove it as an import
10361036 _ = self.imports.remove(sym_loc);
1037 _ = self.undefs.swapRemove(exp.options.name);
10371038 exp.link.wasm.sym_index = sym_index;
10381039 }
10391040}
......@@ -1103,11 +1104,13 @@ pub fn addOrUpdateImport(
11031104 /// is asserted instead.
11041105 type_index: ?u32,
11051106) !void {
1107 assert(symbol_index != 0);
11061108 // For the import name itself, we use the decl's name, rather than the fully qualified name
11071109 const decl_name_index = try self.string_table.put(self.base.allocator, name);
11081110 const symbol: *Symbol = &self.symbols.items[symbol_index];
11091111 symbol.setUndefined(true);
11101112 symbol.setGlobal(true);
1113 symbol.name = decl_name_index;
11111114 const global_gop = try self.globals.getOrPut(self.base.allocator, decl_name_index);
11121115 if (!global_gop.found_existing) {
11131116 const loc: SymbolLoc = .{ .file = null, .index = symbol_index };
......@@ -2113,7 +2116,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
21132116
21142117 while (true) {
21152118 if (!is_obj) {
2116 try atom.resolveRelocs(self);
2119 atom.resolveRelocs(self);
21172120 }
21182121 sorted_atoms.appendAssumeCapacity(atom);
21192122 atom = atom.next orelse break;
......@@ -2172,7 +2175,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
21722175 var current_offset: u32 = 0;
21732176 while (true) {
21742177 if (!is_obj) {
2175 try atom.resolveRelocs(self);
2178 atom.resolveRelocs(self);
21762179 }
21772180
21782181 // Pad with zeroes to ensure all segments are aligned
src/link/Wasm/Atom.zig-1
......@@ -147,7 +147,6 @@ pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) void {
147147fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wasm) u64 {
148148 const target_loc: Wasm.SymbolLoc = .{ .file = self.file, .index = relocation.index };
149149 const symbol = target_loc.getSymbol(wasm_bin).*;
150
151150 switch (relocation.relocation_type) {
152151 .R_WASM_FUNCTION_INDEX_LEB => return symbol.index,
153152 .R_WASM_TABLE_NUMBER_LEB => return symbol.index,
src/link/Wasm/Symbol.zig+5-2
......@@ -142,6 +142,8 @@ pub fn isNoStrip(self: Symbol) bool {
142142pub fn isExported(self: Symbol) bool {
143143 if (self.isUndefined() or self.isLocal()) return false;
144144 if (self.isHidden()) return false;
145 if (self.hasFlag(.WASM_SYM_EXPORTED)) return true;
146 if (self.hasFlag(.WASM_SYM_BINDING_WEAK)) return false;
145147 return true;
146148}
147149
......@@ -165,9 +167,10 @@ pub fn format(self: Symbol, comptime fmt: []const u8, options: std.fmt.FormatOpt
165167 };
166168 const visible: []const u8 = if (self.isVisible()) "yes" else "no";
167169 const binding: []const u8 = if (self.isLocal()) "local" else "global";
170 const undef: []const u8 = if (self.isUndefined()) "undefined" else "";
168171
169172 try writer.print(
170 "{c} binding={s} visible={s} id={d} name_offset={d}",
171 .{ kind_fmt, binding, visible, self.index, self.name },
173 "{c} binding={s} visible={s} id={d} name_offset={d} {s}",
174 .{ kind_fmt, binding, visible, self.index, self.name, undef },
172175 );
173176}