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) = .{},
109109discarded: std.AutoHashMapUnmanaged(SymbolLoc, SymbolLoc) = .{},
110110/// List of all symbol locations which have been resolved by the linker and will be emit
111111/// into the final binary.
112resolved_symbols: std.ArrayListUnmanaged(SymbolLoc) = .{},
112resolved_symbols: std.AutoArrayHashMapUnmanaged(SymbolLoc, void) = .{},
113113
114114pub const Segment = struct {
115115 alignment: u32,
......@@ -134,10 +134,10 @@ pub const SymbolLoc = struct {
134134
135135 /// From a given location, returns the corresponding symbol in the wasm binary
136136 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 }
137140 if (self.file) |object_index| {
138 if (wasm_bin.discarded.get(self)) |old_loc| {
139 return old_loc.getSymbol(wasm_bin);
140 }
141141 const object = wasm_bin.objects.items[object_index];
142142 return &object.symtable[self.index];
143143 }
......@@ -245,7 +245,7 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {
245245 log.err(" symbol '{s}' defined in '{s}'", .{ symbol.name, object.name });
246246 return error.undefinedLocal;
247247 }
248 try self.resolved_symbols.append(self.base.allocator, location);
248 try self.resolved_symbols.putNoClobber(self.base.allocator, location, {});
249249 continue;
250250 }
251251
......@@ -255,6 +255,7 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {
255255 const maybe_existing = try self.globals.getOrPut(self.base.allocator, sym_name);
256256 if (!maybe_existing.found_existing) {
257257 maybe_existing.value_ptr.* = location;
258 try self.resolved_symbols.putNoClobber(self.base.allocator, location, {});
258259 continue;
259260 }
260261
......@@ -277,12 +278,14 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {
277278 }
278279
279280 // simply overwrite with the new symbol
280 log.info("Overwriting symbol '{s}'", .{symbol.name});
281 log.info(" old definition in '{s}'", .{existing_file_path});
282 log.info(" new definition in '{s}'", .{object.name});
281 log.debug("Overwriting symbol '{s}'", .{symbol.name});
282 log.debug(" old definition in '{s}'", .{existing_file_path});
283 log.debug(" new definition in '{s}'", .{object.name});
283284 try self.discarded.putNoClobber(self.base.allocator, maybe_existing.value_ptr.*, location);
284285 maybe_existing.value_ptr.* = location;
285286 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));
286289 }
287290}
288291
......@@ -360,6 +363,11 @@ pub fn allocateDeclIndexes(self: *Wasm, decl: *Module.Decl) !void {
360363 atom.sym_index = @intCast(u32, self.symbols.items.len);
361364 self.symbols.appendAssumeCapacity(symbol);
362365 }
366
367 try self.resolved_symbols.putNoClobber(self.base.allocator, .{
368 .index = atom.sym_index,
369 .file = null,
370 }, {});
363371}
364372
365373pub 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 {
454462 const atom: *Atom = &decl.link.wasm;
455463 atom.size = @intCast(u32, code.len);
456464 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);
458468 try atom.code.appendSlice(self.base.allocator, code);
459469}
460470
......@@ -566,7 +576,7 @@ pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void {
566576 if (decl.isExtern()) {
567577 assert(self.imports.remove(.{ .file = null, .index = atom.sym_index }));
568578 }
569
579 assert(self.resolved_symbols.swapRemove(.{ .index = atom.sym_index, .file = null }));
570580 atom.deinit(self.base.allocator);
571581}
572582
......@@ -594,7 +604,7 @@ fn addOrUpdateImport(self: *Wasm, decl: *Module.Decl) !void {
594604 symbol.name = try self.base.allocator.dupeZ(u8, decl_name);
595605 symbol.setUndefined(true);
596606 // 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 });
598608 switch (decl.ty.zigTypeTag()) {
599609 .Fn => {
600610 const gop = try self.imports.getOrPut(self.base.allocator, .{ .index = symbol_index, .file = null });
......@@ -620,7 +630,7 @@ const Kind = union(enum) {
620630
621631/// Parses an Atom and inserts its metadata into the corresponding sections.
622632fn 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);
624634 const final_index: u32 = switch (kind) {
625635 .function => |fn_data| result: {
626636 const index = @intCast(u32, self.functions.items.len + self.imported_functions_count);
......@@ -711,7 +721,19 @@ fn allocateAtoms(self: *Wasm) !void {
711721}
712722
713723fn 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| {
715737 if (symbol_loc.file == null) {
716738 // imports generated by Zig code are already in the `import` section
717739 continue;
......@@ -755,6 +777,12 @@ fn setupImports(self: *Wasm) !void {
755777 self.imported_functions_count = function_index;
756778 self.imported_globals_count = global_index;
757779 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 });
758786}
759787
760788/// Takes the global, function and table section from each linked object file
......@@ -770,7 +798,7 @@ fn mergeSections(self: *Wasm) !void {
770798 try self.tables.append(self.base.allocator, table);
771799 }
772800
773 for (self.resolved_symbols.items) |sym_loc| {
801 for (self.resolved_symbols.keys()) |sym_loc| {
774802 if (sym_loc.file == null) {
775803 // Zig code-generated symbols are already within the sections and do not
776804 // require to be merged
......@@ -816,7 +844,7 @@ fn mergeSections(self: *Wasm) !void {
816844/// 'types' section, while assigning the type index to the representing
817845/// section (import, export, function).
818846fn mergeTypes(self: *Wasm) !void {
819 for (self.resolved_symbols.items) |sym_loc| {
847 for (self.resolved_symbols.keys()) |sym_loc| {
820848 if (sym_loc.file == null) {
821849 // zig code-generated symbols are already present in final type section
822850 continue;
......@@ -850,7 +878,7 @@ fn setupExports(self: *Wasm) !void {
850878 try self.exports.append(self.base.allocator, .{ .name = "memory", .kind = .memory, .index = 0 });
851879 }
852880
853 for (self.resolved_symbols.items) |sym_loc| {
881 for (self.resolved_symbols.keys()) |sym_loc| {
854882 const symbol = sym_loc.getSymbol(self);
855883 if (!symbol.isExported()) continue;
856884
......@@ -1067,7 +1095,6 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
10671095 var object_index: u16 = 0;
10681096 while (object_index < self.objects.items.len) : (object_index += 1) {
10691097 try self.resolveSymbolsInObject(object_index);
1070 try self.objects.items[object_index].parseIntoAtoms(self.base.allocator, object_index, self);
10711098 }
10721099
10731100 // When we finish/error we reset the state of the linker
......@@ -1090,6 +1117,11 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
10901117 }
10911118 }
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
10931125 try self.setupMemory();
10941126 try self.allocateAtoms();
10951127 self.mapFunctionTable();
......@@ -1428,7 +1460,8 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
14281460 var segments = try std.ArrayList(Name).initCapacity(self.base.allocator, self.data_segments.count());
14291461 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).*;
14321465 switch (symbol.tag) {
14331466 .function => funcs.appendAssumeCapacity(.{ .index = symbol.index, .name = mem.sliceTo(symbol.name, 0) }),
14341467 .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 {
9797/// Resolves the relocations within the atom, writing the new value
9898/// at the calculated offset.
9999pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) !void {
100 if (self.relocs.items.len == 0) return;
100101 const loc: Wasm.SymbolLoc = .{ .file = self.file, .index = self.sym_index };
101102 const symbol = loc.getSymbol(wasm_bin).*;
102103 log.debug("Resolving relocs in atom '{s}' count({d})", .{
......@@ -172,7 +173,13 @@ fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wa
172173 const atom_index = wasm_bin.data_segments.get(segment_name).?;
173174 var target_atom = wasm_bin.atoms.getPtr(atom_index).?.*.getFirst();
174175 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 }
176183 target_atom = target_atom.next orelse break;
177184 }
178185 const segment = wasm_bin.segments.items[atom_index];