authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-08-24 17:53:10+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-08-30 16:38:51+02:00
log414fcea162a751435f0194ed4a01785b3a0913a0
tree6cbc50a49ec716991ae93522714d682f76c86513
parentaa5568beb6931679dfa8eb498140a6a7f8858ae7
signaturelock-open Commit is signed but in an unrecognized format.

link/Wasm: handle extern variables

Generate symbols for extern variables and try to resolve them. Unresolved 'data' symbols generate an error as they cannot be exported from the Wasm runtime into a Wasm module. This means, they can only be resolved by other object files such as from other Zig or C code compiled to Wasm.

3 files changed, 36 insertions(+), 16 deletions(-)

src/link/Wasm.zig+34-11
......@@ -607,6 +607,24 @@ fn resolveSymbolsInArchives(self: *Wasm) !void {
607607 }
608608}
609609
610fn checkUndefinedSymbols(self: *const Wasm) !void {
611 var found_undefined_symbols = false;
612 for (self.undefs.values()) |undef| {
613 const symbol = undef.getSymbol(self);
614 if (symbol.tag == .data) {
615 found_undefined_symbols = true;
616 const file_name = if (undef.file) |file_index| name: {
617 break :name self.objects.items[file_index].name;
618 } else self.name;
619 log.err("could not resolve undefined symbol '{s}'", .{undef.getName(self)});
620 log.err(" defined in '{s}'", .{file_name});
621 }
622 }
623 if (found_undefined_symbols) {
624 return error.UndefinedSymbol;
625 }
626}
627
610628pub fn deinit(self: *Wasm) void {
611629 const gpa = self.base.allocator;
612630 if (build_options.have_llvm) {
......@@ -783,15 +801,17 @@ pub fn updateDecl(self: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !voi
783801
784802 decl.link.wasm.clear();
785803
786 if (decl.isExtern()) {
787 return;
788 }
789
790804 if (decl.val.castTag(.function)) |_| {
791805 return;
792806 } else if (decl.val.castTag(.extern_fn)) |_| {
793807 return;
794808 }
809
810 if (decl.isExtern()) {
811 const variable = decl.getVariable().?;
812 const name = mem.sliceTo(decl.name, 0);
813 return self.addOrUpdateImport(name, decl.link.wasm.sym_index, variable.lib_name, null);
814 }
795815 const val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val;
796816
797817 var code_writer = std.ArrayList(u8).init(self.base.allocator);
......@@ -834,19 +854,18 @@ pub fn updateDeclLineNumber(self: *Wasm, mod: *Module, decl: *const Module.Decl)
834854}
835855
836856fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, code: []const u8) !void {
837 if (code.len == 0) return;
838857 const mod = self.base.options.module.?;
839858 const atom: *Atom = &decl.link.wasm;
840 atom.size = @intCast(u32, code.len);
841 atom.alignment = decl.ty.abiAlignment(self.base.options.target);
842859 const symbol = &self.symbols.items[atom.sym_index];
843
844860 const full_name = try decl.getFullyQualifiedName(mod);
845861 defer self.base.allocator.free(full_name);
846862 symbol.name = try self.string_table.put(self.base.allocator, full_name);
847863 try atom.code.appendSlice(self.base.allocator, code);
848
849864 try self.resolved_symbols.put(self.base.allocator, atom.symbolLoc(), {});
865
866 if (code.len == 0) return;
867 atom.size = @intCast(u32, code.len);
868 atom.alignment = decl.ty.abiAlignment(self.base.options.target);
850869}
851870
852871/// From a given symbol location, returns its `wasm.GlobalType`.
......@@ -1235,7 +1254,10 @@ pub fn addOrUpdateImport(
12351254 .kind = .{ .function = ty_index },
12361255 };
12371256 }
1238 } else @panic("TODO: Implement undefined symbols for non-function declarations");
1257 } else {
1258 symbol.tag = .data;
1259 return; // non-functions will not be imported from the runtime, but only resolved during link-time
1260 }
12391261}
12401262
12411263/// Kind represents the type of an Atom, which is only
......@@ -1438,7 +1460,7 @@ fn setupImports(self: *Wasm) !void {
14381460 if (std.mem.eql(u8, symbol_loc.getName(self), "__indirect_function_table")) {
14391461 continue;
14401462 }
1441 if (symbol.tag == .data or !symbol.requiresImport()) {
1463 if (!symbol.requiresImport()) {
14421464 continue;
14431465 }
14441466
......@@ -2007,6 +2029,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
20072029 }
20082030
20092031 try self.resolveSymbolsInArchives();
2032 try self.checkUndefinedSymbols();
20102033
20112034 // When we finish/error we reset the state of the linker
20122035 // So we can rebuild the binary file on each incremental update
src/link/Wasm/Atom.zig+1-4
......@@ -172,10 +172,7 @@ fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wa
172172 .R_WASM_MEMORY_ADDR_SLEB,
173173 .R_WASM_MEMORY_ADDR_SLEB64,
174174 => {
175 if (symbol.isUndefined() and symbol.isWeak()) {
176 return 0;
177 }
178 std.debug.assert(symbol.tag == .data);
175 std.debug.assert(symbol.tag == .data and !symbol.isUndefined());
179176 const merge_segment = wasm_bin.base.options.output_mode != .Obj;
180177 const segment_info = if (self.file) |object_index| blk: {
181178 break :blk wasm_bin.objects.items[object_index].segment_info;
src/link/Wasm/Symbol.zig+1-1
......@@ -79,9 +79,9 @@ pub const Flag = enum(u32) {
7979/// Verifies if the given symbol should be imported from the
8080/// host environment or not
8181pub fn requiresImport(self: Symbol) bool {
82 if (self.tag == .data) return false;
8283 if (!self.isUndefined()) return false;
8384 if (self.isWeak()) return false;
84 if (self.tag == .data) return false;
8585 // if (self.isDefined() and self.isWeak()) return true; //TODO: Only when building shared lib
8686
8787 return true;