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 {...@@ -491,6 +491,7 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {
491 return error.SymbolCollision;491 return error.SymbolCollision;
492 }492 }
493493
494 try self.discarded.put(self.base.allocator, location, existing_loc);
494 continue; // Do not overwrite defined symbols with undefined symbols495 continue; // Do not overwrite defined symbols with undefined symbols
495 }496 }
496497
...@@ -503,6 +504,7 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {...@@ -503,6 +504,7 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {
503504
504 // when both symbols are weak, we skip overwriting505 // when both symbols are weak, we skip overwriting
505 if (existing_sym.isWeak() and symbol.isWeak()) {506 if (existing_sym.isWeak() and symbol.isWeak()) {
507 try self.discarded.put(self.base.allocator, location, existing_loc);
506 continue;508 continue;
507 }509 }
508510
...@@ -510,15 +512,13 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {...@@ -510,15 +512,13 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {
510 log.debug("Overwriting symbol '{s}'", .{sym_name});512 log.debug("Overwriting symbol '{s}'", .{sym_name});
511 log.debug(" old definition in '{s}'", .{existing_file_path});513 log.debug(" old definition in '{s}'", .{existing_file_path});
512 log.debug(" new definition in '{s}'", .{object.name});514 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);
514 maybe_existing.value_ptr.* = location;516 maybe_existing.value_ptr.* = location;
515 try self.globals.put(self.base.allocator, sym_name_index, location);517 try self.globals.put(self.base.allocator, sym_name_index, location);
516 try self.resolved_symbols.put(self.base.allocator, location, {});518 try self.resolved_symbols.put(self.base.allocator, location, {});
517 assert(self.resolved_symbols.swapRemove(existing_loc));519 assert(self.resolved_symbols.swapRemove(existing_loc));
518 if (existing_sym.isUndefined()) {520 if (existing_sym.isUndefined()) {
519 // ensure order remains intact in case we later521 assert(self.undefs.swapRemove(sym_name));
520 // resolve symbols again in a loop
521 assert(self.undefs.orderedRemove(sym_name));
522 }522 }
523 }523 }
524}524}
...@@ -1004,7 +1004,6 @@ pub fn updateDeclExports(...@@ -1004,7 +1004,6 @@ pub fn updateDeclExports(
1004 switch (exp.options.linkage) {1004 switch (exp.options.linkage) {
1005 .Internal => {1005 .Internal => {
1006 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);1006 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
1007 symbol.setFlag(.WASM_SYM_BINDING_WEAK);
1008 },1007 },
1009 .Weak => {1008 .Weak => {
1010 symbol.setFlag(.WASM_SYM_BINDING_WEAK);1009 symbol.setFlag(.WASM_SYM_BINDING_WEAK);
...@@ -1026,6 +1025,7 @@ pub fn updateDeclExports(...@@ -1026,6 +1025,7 @@ pub fn updateDeclExports(
1026 }1025 }
10271026
1028 symbol.setGlobal(true);1027 symbol.setGlobal(true);
1028 symbol.setUndefined(false);
1029 try self.globals.put(1029 try self.globals.put(
1030 self.base.allocator,1030 self.base.allocator,
1031 export_name,1031 export_name,
...@@ -1034,6 +1034,7 @@ pub fn updateDeclExports(...@@ -1034,6 +1034,7 @@ pub fn updateDeclExports(
10341034
1035 // if the symbol was previously undefined, remove it as an import1035 // if the symbol was previously undefined, remove it as an import
1036 _ = self.imports.remove(sym_loc);1036 _ = self.imports.remove(sym_loc);
1037 _ = self.undefs.swapRemove(exp.options.name);
1037 exp.link.wasm.sym_index = sym_index;1038 exp.link.wasm.sym_index = sym_index;
1038 }1039 }
1039}1040}
...@@ -1103,11 +1104,13 @@ pub fn addOrUpdateImport(...@@ -1103,11 +1104,13 @@ pub fn addOrUpdateImport(
1103 /// is asserted instead.1104 /// is asserted instead.
1104 type_index: ?u32,1105 type_index: ?u32,
1105) !void {1106) !void {
1107 assert(symbol_index != 0);
1106 // For the import name itself, we use the decl's name, rather than the fully qualified name1108 // For the import name itself, we use the decl's name, rather than the fully qualified name
1107 const decl_name_index = try self.string_table.put(self.base.allocator, name);1109 const decl_name_index = try self.string_table.put(self.base.allocator, name);
1108 const symbol: *Symbol = &self.symbols.items[symbol_index];1110 const symbol: *Symbol = &self.symbols.items[symbol_index];
1109 symbol.setUndefined(true);1111 symbol.setUndefined(true);
1110 symbol.setGlobal(true);1112 symbol.setGlobal(true);
1113 symbol.name = decl_name_index;
1111 const global_gop = try self.globals.getOrPut(self.base.allocator, decl_name_index);1114 const global_gop = try self.globals.getOrPut(self.base.allocator, decl_name_index);
1112 if (!global_gop.found_existing) {1115 if (!global_gop.found_existing) {
1113 const loc: SymbolLoc = .{ .file = null, .index = symbol_index };1116 const loc: SymbolLoc = .{ .file = null, .index = symbol_index };
...@@ -2113,7 +2116,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -2113,7 +2116,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
21132116
2114 while (true) {2117 while (true) {
2115 if (!is_obj) {2118 if (!is_obj) {
2116 try atom.resolveRelocs(self);2119 atom.resolveRelocs(self);
2117 }2120 }
2118 sorted_atoms.appendAssumeCapacity(atom);2121 sorted_atoms.appendAssumeCapacity(atom);
2119 atom = atom.next orelse break;2122 atom = atom.next orelse break;
...@@ -2172,7 +2175,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -2172,7 +2175,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
2172 var current_offset: u32 = 0;2175 var current_offset: u32 = 0;
2173 while (true) {2176 while (true) {
2174 if (!is_obj) {2177 if (!is_obj) {
2175 try atom.resolveRelocs(self);2178 atom.resolveRelocs(self);
2176 }2179 }
21772180
2178 // Pad with zeroes to ensure all segments are aligned2181 // 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 {...@@ -147,7 +147,6 @@ pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) void {
147fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wasm) u64 {147fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wasm) u64 {
148 const target_loc: Wasm.SymbolLoc = .{ .file = self.file, .index = relocation.index };148 const target_loc: Wasm.SymbolLoc = .{ .file = self.file, .index = relocation.index };
149 const symbol = target_loc.getSymbol(wasm_bin).*;149 const symbol = target_loc.getSymbol(wasm_bin).*;
150
151 switch (relocation.relocation_type) {150 switch (relocation.relocation_type) {
152 .R_WASM_FUNCTION_INDEX_LEB => return symbol.index,151 .R_WASM_FUNCTION_INDEX_LEB => return symbol.index,
153 .R_WASM_TABLE_NUMBER_LEB => return symbol.index,152 .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 {...@@ -142,6 +142,8 @@ pub fn isNoStrip(self: Symbol) bool {
142pub fn isExported(self: Symbol) bool {142pub fn isExported(self: Symbol) bool {
143 if (self.isUndefined() or self.isLocal()) return false;143 if (self.isUndefined() or self.isLocal()) return false;
144 if (self.isHidden()) return false;144 if (self.isHidden()) return false;
145 if (self.hasFlag(.WASM_SYM_EXPORTED)) return true;
146 if (self.hasFlag(.WASM_SYM_BINDING_WEAK)) return false;
145 return true;147 return true;
146}148}
147149
...@@ -165,9 +167,10 @@ pub fn format(self: Symbol, comptime fmt: []const u8, options: std.fmt.FormatOpt...@@ -165,9 +167,10 @@ pub fn format(self: Symbol, comptime fmt: []const u8, options: std.fmt.FormatOpt
165 };167 };
166 const visible: []const u8 = if (self.isVisible()) "yes" else "no";168 const visible: []const u8 = if (self.isVisible()) "yes" else "no";
167 const binding: []const u8 = if (self.isLocal()) "local" else "global";169 const binding: []const u8 = if (self.isLocal()) "local" else "global";
170 const undef: []const u8 = if (self.isUndefined()) "undefined" else "";
168171
169 try writer.print(172 try writer.print(
170 "{c} binding={s} visible={s} id={d} name_offset={d}",173 "{c} binding={s} visible={s} id={d} name_offset={d} {s}",
171 .{ kind_fmt, binding, visible, self.index, self.name },174 .{ kind_fmt, binding, visible, self.index, self.name, undef },
172 );175 );
173}176}