authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-02-16 22:13:25+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-02-17 18:11:48+01:00
log4ebe8a53cab2c218657090f984b8ba10ef06b23a
tree1ae5bf2d15c0b9eebf7ae9b10570dda094977c11
parenta4622501bdae96d43f26d1897c1f4de87b8daa31

wasm-linker: Fix symbol resolving and relocs

- Correctly get discard symbol by first checking if it was discarded or not. - Remove imports if extern symbols were resolved by an object file. - Correctly relocate data symbols by ensuring the atom is from the correct file. - Fix the `Names` section by using the resolved symbols, rather than the ones defined in Zig code.

2 files changed, 59 insertions(+), 19 deletions(-)

src/link/Wasm.zig+51-18
...@@ -109,7 +109,7 @@ globals: std.StringHashMapUnmanaged(SymbolLoc) = .{},...@@ -109,7 +109,7 @@ globals: std.StringHashMapUnmanaged(SymbolLoc) = .{},
109discarded: std.AutoHashMapUnmanaged(SymbolLoc, SymbolLoc) = .{},109discarded: std.AutoHashMapUnmanaged(SymbolLoc, SymbolLoc) = .{},
110/// List of all symbol locations which have been resolved by the linker and will be emit110/// List of all symbol locations which have been resolved by the linker and will be emit
111/// into the final binary.111/// into the final binary.
112resolved_symbols: std.ArrayListUnmanaged(SymbolLoc) = .{},112resolved_symbols: std.AutoArrayHashMapUnmanaged(SymbolLoc, void) = .{},
113113
114pub const Segment = struct {114pub const Segment = struct {
115 alignment: u32,115 alignment: u32,
...@@ -134,10 +134,10 @@ pub const SymbolLoc = struct {...@@ -134,10 +134,10 @@ pub const SymbolLoc = struct {
134134
135 /// From a given location, returns the corresponding symbol in the wasm binary135 /// From a given location, returns the corresponding symbol in the wasm binary
136 pub fn getSymbol(self: SymbolLoc, wasm_bin: *const Wasm) *Symbol {136 pub fn getSymbol(self: SymbolLoc, wasm_bin: *const Wasm) *Symbol {
137 if (wasm_bin.discarded.get(self)) |new_loc| {
138 return new_loc.getSymbol(wasm_bin);
139 }
137 if (self.file) |object_index| {140 if (self.file) |object_index| {
138 if (wasm_bin.discarded.get(self)) |old_loc| {
139 return old_loc.getSymbol(wasm_bin);
140 }
141 const object = wasm_bin.objects.items[object_index];141 const object = wasm_bin.objects.items[object_index];
142 return &object.symtable[self.index];142 return &object.symtable[self.index];
143 }143 }
...@@ -245,7 +245,7 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {...@@ -245,7 +245,7 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {
245 log.err(" symbol '{s}' defined in '{s}'", .{ symbol.name, object.name });245 log.err(" symbol '{s}' defined in '{s}'", .{ symbol.name, object.name });
246 return error.undefinedLocal;246 return error.undefinedLocal;
247 }247 }
248 try self.resolved_symbols.append(self.base.allocator, location);248 try self.resolved_symbols.putNoClobber(self.base.allocator, location, {});
249 continue;249 continue;
250 }250 }
251251
...@@ -255,6 +255,7 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {...@@ -255,6 +255,7 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {
255 const maybe_existing = try self.globals.getOrPut(self.base.allocator, sym_name);255 const maybe_existing = try self.globals.getOrPut(self.base.allocator, sym_name);
256 if (!maybe_existing.found_existing) {256 if (!maybe_existing.found_existing) {
257 maybe_existing.value_ptr.* = location;257 maybe_existing.value_ptr.* = location;
258 try self.resolved_symbols.putNoClobber(self.base.allocator, location, {});
258 continue;259 continue;
259 }260 }
260261
...@@ -277,12 +278,14 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {...@@ -277,12 +278,14 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {
277 }278 }
278279
279 // simply overwrite with the new symbol280 // simply overwrite with the new symbol
280 log.info("Overwriting symbol '{s}'", .{symbol.name});281 log.debug("Overwriting symbol '{s}'", .{symbol.name});
281 log.info(" old definition in '{s}'", .{existing_file_path});282 log.debug(" old definition in '{s}'", .{existing_file_path});
282 log.info(" new definition in '{s}'", .{object.name});283 log.debug(" new definition in '{s}'", .{object.name});
283 try self.discarded.putNoClobber(self.base.allocator, maybe_existing.value_ptr.*, location);284 try self.discarded.putNoClobber(self.base.allocator, maybe_existing.value_ptr.*, location);
284 maybe_existing.value_ptr.* = location;285 maybe_existing.value_ptr.* = location;
285 try self.globals.put(self.base.allocator, sym_name, location);286 try self.globals.put(self.base.allocator, sym_name, location);
287 try self.resolved_symbols.put(self.base.allocator, location, {});
288 assert(self.resolved_symbols.swapRemove(existing_loc));
286 }289 }
287}290}
288291
...@@ -360,6 +363,11 @@ pub fn allocateDeclIndexes(self: *Wasm, decl: *Module.Decl) !void {...@@ -360,6 +363,11 @@ pub fn allocateDeclIndexes(self: *Wasm, decl: *Module.Decl) !void {
360 atom.sym_index = @intCast(u32, self.symbols.items.len);363 atom.sym_index = @intCast(u32, self.symbols.items.len);
361 self.symbols.appendAssumeCapacity(symbol);364 self.symbols.appendAssumeCapacity(symbol);
362 }365 }
366
367 try self.resolved_symbols.putNoClobber(self.base.allocator, .{
368 .index = atom.sym_index,
369 .file = null,
370 }, {});
363}371}
364372
365pub fn updateFunc(self: *Wasm, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {373pub fn updateFunc(self: *Wasm, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
...@@ -454,7 +462,9 @@ fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, code: []const u8) !void {...@@ -454,7 +462,9 @@ fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, code: []const u8) !void {
454 const atom: *Atom = &decl.link.wasm;462 const atom: *Atom = &decl.link.wasm;
455 atom.size = @intCast(u32, code.len);463 atom.size = @intCast(u32, code.len);
456 atom.alignment = decl.ty.abiAlignment(self.base.options.target);464 atom.alignment = decl.ty.abiAlignment(self.base.options.target);
457 self.symbols.items[atom.sym_index].name = try self.base.allocator.dupeZ(u8, std.mem.sliceTo(decl.name, 0));465 const symbol = &self.symbols.items[atom.sym_index];
466 symbol.name = try self.base.allocator.dupeZ(u8, std.mem.sliceTo(decl.name, 0));
467 symbol.setFlag(.WASM_SYM_BINDING_LOCAL);
458 try atom.code.appendSlice(self.base.allocator, code);468 try atom.code.appendSlice(self.base.allocator, code);
459}469}
460470
...@@ -566,7 +576,7 @@ pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void {...@@ -566,7 +576,7 @@ pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void {
566 if (decl.isExtern()) {576 if (decl.isExtern()) {
567 assert(self.imports.remove(.{ .file = null, .index = atom.sym_index }));577 assert(self.imports.remove(.{ .file = null, .index = atom.sym_index }));
568 }578 }
569579 assert(self.resolved_symbols.swapRemove(.{ .index = atom.sym_index, .file = null }));
570 atom.deinit(self.base.allocator);580 atom.deinit(self.base.allocator);
571}581}
572582
...@@ -594,7 +604,7 @@ fn addOrUpdateImport(self: *Wasm, decl: *Module.Decl) !void {...@@ -594,7 +604,7 @@ fn addOrUpdateImport(self: *Wasm, decl: *Module.Decl) !void {
594 symbol.name = try self.base.allocator.dupeZ(u8, decl_name);604 symbol.name = try self.base.allocator.dupeZ(u8, decl_name);
595 symbol.setUndefined(true);605 symbol.setUndefined(true);
596 // also add it as a global so it can be resolved606 // also add it as a global so it can be resolved
597 try self.globals.put(self.base.allocator, decl_name, .{ .file = null, .index = symbol_index });607 try self.globals.putNoClobber(self.base.allocator, decl_name, .{ .file = null, .index = symbol_index });
598 switch (decl.ty.zigTypeTag()) {608 switch (decl.ty.zigTypeTag()) {
599 .Fn => {609 .Fn => {
600 const gop = try self.imports.getOrPut(self.base.allocator, .{ .index = symbol_index, .file = null });610 const gop = try self.imports.getOrPut(self.base.allocator, .{ .index = symbol_index, .file = null });
...@@ -620,7 +630,7 @@ const Kind = union(enum) {...@@ -620,7 +630,7 @@ const Kind = union(enum) {
620630
621/// Parses an Atom and inserts its metadata into the corresponding sections.631/// Parses an Atom and inserts its metadata into the corresponding sections.
622fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void {632fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void {
623 const symbol: *Symbol = &self.symbols.items[atom.sym_index];633 const symbol = (SymbolLoc{ .file = null, .index = atom.sym_index }).getSymbol(self);
624 const final_index: u32 = switch (kind) {634 const final_index: u32 = switch (kind) {
625 .function => |fn_data| result: {635 .function => |fn_data| result: {
626 const index = @intCast(u32, self.functions.items.len + self.imported_functions_count);636 const index = @intCast(u32, self.functions.items.len + self.imported_functions_count);
...@@ -711,7 +721,19 @@ fn allocateAtoms(self: *Wasm) !void {...@@ -711,7 +721,19 @@ fn allocateAtoms(self: *Wasm) !void {
711}721}
712722
713fn setupImports(self: *Wasm) !void {723fn setupImports(self: *Wasm) !void {
714 for (self.resolved_symbols.items) |symbol_loc| {724 log.debug("Merging imports", .{});
725 var discarded_it = self.discarded.keyIterator();
726 while (discarded_it.next()) |discarded| {
727 if (discarded.file == null) {
728 // remove an import if it was resolved
729 if (self.imports.remove(discarded.*)) {
730 log.debug("Removed symbol '{s}' as an import", .{
731 discarded.getSymbol(self).name,
732 });
733 }
734 }
735 }
736 for (self.resolved_symbols.keys()) |symbol_loc| {
715 if (symbol_loc.file == null) {737 if (symbol_loc.file == null) {
716 // imports generated by Zig code are already in the `import` section738 // imports generated by Zig code are already in the `import` section
717 continue;739 continue;
...@@ -755,6 +777,12 @@ fn setupImports(self: *Wasm) !void {...@@ -755,6 +777,12 @@ fn setupImports(self: *Wasm) !void {
755 self.imported_functions_count = function_index;777 self.imported_functions_count = function_index;
756 self.imported_globals_count = global_index;778 self.imported_globals_count = global_index;
757 self.imported_tables_count = table_index;779 self.imported_tables_count = table_index;
780
781 log.debug("Merged ({d}) functions, ({d}) globals, and ({d}) tables into import section", .{
782 function_index,
783 global_index,
784 table_index,
785 });
758}786}
759787
760/// Takes the global, function and table section from each linked object file788/// Takes the global, function and table section from each linked object file
...@@ -770,7 +798,7 @@ fn mergeSections(self: *Wasm) !void {...@@ -770,7 +798,7 @@ fn mergeSections(self: *Wasm) !void {
770 try self.tables.append(self.base.allocator, table);798 try self.tables.append(self.base.allocator, table);
771 }799 }
772800
773 for (self.resolved_symbols.items) |sym_loc| {801 for (self.resolved_symbols.keys()) |sym_loc| {
774 if (sym_loc.file == null) {802 if (sym_loc.file == null) {
775 // Zig code-generated symbols are already within the sections and do not803 // Zig code-generated symbols are already within the sections and do not
776 // require to be merged804 // require to be merged
...@@ -816,7 +844,7 @@ fn mergeSections(self: *Wasm) !void {...@@ -816,7 +844,7 @@ fn mergeSections(self: *Wasm) !void {
816/// 'types' section, while assigning the type index to the representing844/// 'types' section, while assigning the type index to the representing
817/// section (import, export, function).845/// section (import, export, function).
818fn mergeTypes(self: *Wasm) !void {846fn mergeTypes(self: *Wasm) !void {
819 for (self.resolved_symbols.items) |sym_loc| {847 for (self.resolved_symbols.keys()) |sym_loc| {
820 if (sym_loc.file == null) {848 if (sym_loc.file == null) {
821 // zig code-generated symbols are already present in final type section849 // zig code-generated symbols are already present in final type section
822 continue;850 continue;
...@@ -850,7 +878,7 @@ fn setupExports(self: *Wasm) !void {...@@ -850,7 +878,7 @@ fn setupExports(self: *Wasm) !void {
850 try self.exports.append(self.base.allocator, .{ .name = "memory", .kind = .memory, .index = 0 });878 try self.exports.append(self.base.allocator, .{ .name = "memory", .kind = .memory, .index = 0 });
851 }879 }
852880
853 for (self.resolved_symbols.items) |sym_loc| {881 for (self.resolved_symbols.keys()) |sym_loc| {
854 const symbol = sym_loc.getSymbol(self);882 const symbol = sym_loc.getSymbol(self);
855 if (!symbol.isExported()) continue;883 if (!symbol.isExported()) continue;
856884
...@@ -1067,7 +1095,6 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {...@@ -1067,7 +1095,6 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
1067 var object_index: u16 = 0;1095 var object_index: u16 = 0;
1068 while (object_index < self.objects.items.len) : (object_index += 1) {1096 while (object_index < self.objects.items.len) : (object_index += 1) {
1069 try self.resolveSymbolsInObject(object_index);1097 try self.resolveSymbolsInObject(object_index);
1070 try self.objects.items[object_index].parseIntoAtoms(self.base.allocator, object_index, self);
1071 }1098 }
10721099
1073 // When we finish/error we reset the state of the linker1100 // When we finish/error we reset the state of the linker
...@@ -1090,6 +1117,11 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {...@@ -1090,6 +1117,11 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
1090 }1117 }
1091 }1118 }
10921119
1120 while (object_index > 0) {
1121 object_index -= 1;
1122 try self.objects.items[object_index].parseIntoAtoms(self.base.allocator, object_index, self);
1123 }
1124
1093 try self.setupMemory();1125 try self.setupMemory();
1094 try self.allocateAtoms();1126 try self.allocateAtoms();
1095 self.mapFunctionTable();1127 self.mapFunctionTable();
...@@ -1428,7 +1460,8 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {...@@ -1428,7 +1460,8 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
1428 var segments = try std.ArrayList(Name).initCapacity(self.base.allocator, self.data_segments.count());1460 var segments = try std.ArrayList(Name).initCapacity(self.base.allocator, self.data_segments.count());
1429 defer segments.deinit();1461 defer segments.deinit();
14301462
1431 for (self.symbols.items) |symbol| {1463 for (self.resolved_symbols.keys()) |sym_loc| {
1464 const symbol = sym_loc.getSymbol(self).*;
1432 switch (symbol.tag) {1465 switch (symbol.tag) {
1433 .function => funcs.appendAssumeCapacity(.{ .index = symbol.index, .name = mem.sliceTo(symbol.name, 0) }),1466 .function => funcs.appendAssumeCapacity(.{ .index = symbol.index, .name = mem.sliceTo(symbol.name, 0) }),
1434 .global => globals.appendAssumeCapacity(.{ .index = symbol.index, .name = mem.sliceTo(symbol.name, 0) }),1467 .global => globals.appendAssumeCapacity(.{ .index = symbol.index, .name = mem.sliceTo(symbol.name, 0) }),
src/link/Wasm/Atom.zig+8-1
...@@ -97,6 +97,7 @@ pub fn symbolAtom(self: *Atom, symbol_index: u32) *Atom {...@@ -97,6 +97,7 @@ pub fn symbolAtom(self: *Atom, symbol_index: u32) *Atom {
97/// Resolves the relocations within the atom, writing the new value97/// Resolves the relocations within the atom, writing the new value
98/// at the calculated offset.98/// at the calculated offset.
99pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) !void {99pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) !void {
100 if (self.relocs.items.len == 0) return;
100 const loc: Wasm.SymbolLoc = .{ .file = self.file, .index = self.sym_index };101 const loc: Wasm.SymbolLoc = .{ .file = self.file, .index = self.sym_index };
101 const symbol = loc.getSymbol(wasm_bin).*;102 const symbol = loc.getSymbol(wasm_bin).*;
102 log.debug("Resolving relocs in atom '{s}' count({d})", .{103 log.debug("Resolving relocs in atom '{s}' count({d})", .{
...@@ -172,7 +173,13 @@ fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wa...@@ -172,7 +173,13 @@ fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wa
172 const atom_index = wasm_bin.data_segments.get(segment_name).?;173 const atom_index = wasm_bin.data_segments.get(segment_name).?;
173 var target_atom = wasm_bin.atoms.getPtr(atom_index).?.*.getFirst();174 var target_atom = wasm_bin.atoms.getPtr(atom_index).?.*.getFirst();
174 while (true) {175 while (true) {
175 if (target_atom.sym_index == relocation.index) break;176 // TODO: Can we simplify this by providing the ability to find and atom
177 // based on a symbol location.
178 if (target_atom.sym_index == relocation.index) {
179 if (target_atom.file) |file| {
180 if (self.file != null and self.file.? == file) break;
181 } else if (self.file == null) break;
182 }
176 target_atom = target_atom.next orelse break;183 target_atom = target_atom.next orelse break;
177 }184 }
178 const segment = wasm_bin.segments.items[atom_index];185 const segment = wasm_bin.segments.items[atom_index];