From 414fcea162a751435f0194ed4a01785b3a0913a0 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Wed, 24 Aug 2022 17:53:10 +0200 Subject: 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. --- src/link/Wasm.zig | 45 ++++++++++++++++++++++++++++++++++----------- src/link/Wasm/Atom.zig | 5 +---- src/link/Wasm/Symbol.zig | 2 +- 3 files changed, 36 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index e20703cb2b..050d9287a5 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -607,6 +607,24 @@ fn resolveSymbolsInArchives(self: *Wasm) !void { } } +fn checkUndefinedSymbols(self: *const Wasm) !void { + var found_undefined_symbols = false; + for (self.undefs.values()) |undef| { + const symbol = undef.getSymbol(self); + if (symbol.tag == .data) { + found_undefined_symbols = true; + const file_name = if (undef.file) |file_index| name: { + break :name self.objects.items[file_index].name; + } else self.name; + log.err("could not resolve undefined symbol '{s}'", .{undef.getName(self)}); + log.err(" defined in '{s}'", .{file_name}); + } + } + if (found_undefined_symbols) { + return error.UndefinedSymbol; + } +} + pub fn deinit(self: *Wasm) void { const gpa = self.base.allocator; if (build_options.have_llvm) { @@ -783,15 +801,17 @@ pub fn updateDecl(self: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !voi decl.link.wasm.clear(); - if (decl.isExtern()) { - return; - } - if (decl.val.castTag(.function)) |_| { return; } else if (decl.val.castTag(.extern_fn)) |_| { return; } + + if (decl.isExtern()) { + const variable = decl.getVariable().?; + const name = mem.sliceTo(decl.name, 0); + return self.addOrUpdateImport(name, decl.link.wasm.sym_index, variable.lib_name, null); + } const val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val; 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) } fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, code: []const u8) !void { - if (code.len == 0) return; const mod = self.base.options.module.?; const atom: *Atom = &decl.link.wasm; - atom.size = @intCast(u32, code.len); - atom.alignment = decl.ty.abiAlignment(self.base.options.target); const symbol = &self.symbols.items[atom.sym_index]; - const full_name = try decl.getFullyQualifiedName(mod); defer self.base.allocator.free(full_name); symbol.name = try self.string_table.put(self.base.allocator, full_name); try atom.code.appendSlice(self.base.allocator, code); - try self.resolved_symbols.put(self.base.allocator, atom.symbolLoc(), {}); + + if (code.len == 0) return; + atom.size = @intCast(u32, code.len); + atom.alignment = decl.ty.abiAlignment(self.base.options.target); } /// From a given symbol location, returns its `wasm.GlobalType`. @@ -1235,7 +1254,10 @@ pub fn addOrUpdateImport( .kind = .{ .function = ty_index }, }; } - } else @panic("TODO: Implement undefined symbols for non-function declarations"); + } else { + symbol.tag = .data; + return; // non-functions will not be imported from the runtime, but only resolved during link-time + } } /// Kind represents the type of an Atom, which is only @@ -1438,7 +1460,7 @@ fn setupImports(self: *Wasm) !void { if (std.mem.eql(u8, symbol_loc.getName(self), "__indirect_function_table")) { continue; } - if (symbol.tag == .data or !symbol.requiresImport()) { + if (!symbol.requiresImport()) { continue; } @@ -2007,6 +2029,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod } try self.resolveSymbolsInArchives(); + try self.checkUndefinedSymbols(); // When we finish/error we reset the state of the linker // So we can rebuild the binary file on each incremental update diff --git a/src/link/Wasm/Atom.zig b/src/link/Wasm/Atom.zig index d5bb4509f6..ed512f466c 100644 --- a/src/link/Wasm/Atom.zig +++ b/src/link/Wasm/Atom.zig @@ -172,10 +172,7 @@ fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wa .R_WASM_MEMORY_ADDR_SLEB, .R_WASM_MEMORY_ADDR_SLEB64, => { - if (symbol.isUndefined() and symbol.isWeak()) { - return 0; - } - std.debug.assert(symbol.tag == .data); + std.debug.assert(symbol.tag == .data and !symbol.isUndefined()); const merge_segment = wasm_bin.base.options.output_mode != .Obj; const segment_info = if (self.file) |object_index| blk: { break :blk wasm_bin.objects.items[object_index].segment_info; diff --git a/src/link/Wasm/Symbol.zig b/src/link/Wasm/Symbol.zig index fa6ea89d69..5e13456605 100644 --- a/src/link/Wasm/Symbol.zig +++ b/src/link/Wasm/Symbol.zig @@ -79,9 +79,9 @@ pub const Flag = enum(u32) { /// Verifies if the given symbol should be imported from the /// host environment or not pub fn requiresImport(self: Symbol) bool { + if (self.tag == .data) return false; if (!self.isUndefined()) return false; if (self.isWeak()) return false; - if (self.tag == .data) return false; // if (self.isDefined() and self.isWeak()) return true; //TODO: Only when building shared lib return true; -- cgit v1.2.3 From 4f72ac265acac682541f170a1189a06350009431 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Fri, 26 Aug 2022 08:11:17 +0200 Subject: wasm: create relocations for extern decls This also fixes performing relocations for data symbols of which the target symbol exists in an external object file. We do this by checking if the target symbol was discarded, and if so: get the new location so that we can find the corresponding atom that belongs to said new location. Previously it would always assume the symbol would live in the same file as the atom/symbol that is doing the relocation. --- src/arch/wasm/CodeGen.zig | 4 +--- src/link/Wasm/Atom.zig | 9 +++++---- 2 files changed, 6 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 95a0a8e4aa..e09822c208 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -2355,7 +2355,7 @@ fn lowerDeclRefValue(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) const module = self.bin_file.base.options.module.?; const decl = module.declPtr(decl_index); - if (decl.ty.zigTypeTag() != .Fn and !decl.ty.hasRuntimeBitsIgnoreComptime()) { + if (!decl.ty.hasRuntimeBitsIgnoreComptime()) { return WValue{ .imm32 = 0xaaaaaaaa }; } @@ -2394,9 +2394,7 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { const decl_index = decl_ref_mut.data.decl_index; return self.lowerDeclRefValue(.{ .ty = ty, .val = val }, decl_index); } - const target = self.target; - switch (ty.zigTypeTag()) { .Void => return WValue{ .none = {} }, .Int => { diff --git a/src/link/Wasm/Atom.zig b/src/link/Wasm/Atom.zig index ed512f466c..9e7f7a5a76 100644 --- a/src/link/Wasm/Atom.zig +++ b/src/link/Wasm/Atom.zig @@ -174,13 +174,14 @@ fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wa => { std.debug.assert(symbol.tag == .data and !symbol.isUndefined()); const merge_segment = wasm_bin.base.options.output_mode != .Obj; - const segment_info = if (self.file) |object_index| blk: { + const target_atom_loc = wasm_bin.discarded.get(target_loc) orelse target_loc; + const target_atom = wasm_bin.symbol_atom.get(target_atom_loc).?; + const segment_info = if (target_atom.file) |object_index| blk: { break :blk wasm_bin.objects.items[object_index].segment_info; } else wasm_bin.segment_info.items; const segment_name = segment_info[symbol.index].outputName(merge_segment); - const atom_index = wasm_bin.data_segments.get(segment_name).?; - const target_atom = wasm_bin.symbol_atom.get(target_loc).?; - const segment = wasm_bin.segments.items[atom_index]; + const segment_index = wasm_bin.data_segments.get(segment_name).?; + const segment = wasm_bin.segments.items[segment_index]; return target_atom.offset + segment.offset + (relocation.addend orelse 0); }, .R_WASM_EVENT_INDEX_LEB => return symbol.index, -- cgit v1.2.3 From 8627858bbc2fee848e2f3e3ca64dc944f39591e5 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Fri, 26 Aug 2022 17:29:43 +0200 Subject: test/link: add test for extern resolution Adds a linker tests to verify extern/undefined symbols representing non-functions are being resolved correctly. --- src/arch/wasm/CodeGen.zig | 2 +- test/link.zig | 6 ++++++ test/link/wasm/extern/build.zig | 17 +++++++++++++++++ test/link/wasm/extern/foo.c | 1 + test/link/wasm/extern/main.zig | 8 ++++++++ 5 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 test/link/wasm/extern/build.zig create mode 100644 test/link/wasm/extern/foo.c create mode 100644 test/link/wasm/extern/main.zig (limited to 'src') diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index e09822c208..b9637bf8e3 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -2355,7 +2355,7 @@ fn lowerDeclRefValue(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) const module = self.bin_file.base.options.module.?; const decl = module.declPtr(decl_index); - if (!decl.ty.hasRuntimeBitsIgnoreComptime()) { + if (decl.ty.zigTypeTag() != .Fn and !decl.ty.hasRuntimeBitsIgnoreComptime()) { return WValue{ .imm32 = 0xaaaaaaaa }; } diff --git a/test/link.zig b/test/link.zig index 215a0511fc..b68353122c 100644 --- a/test/link.zig +++ b/test/link.zig @@ -52,6 +52,12 @@ fn addWasmCases(cases: *tests.StandaloneContext) void { .build_modes = true, .requires_stage2 = true, }); + + cases.addBuildFile("test/link/wasm/extern/build.zig", .{ + .build_modes = true, + .requires_stage2 = true, + .use_emulation = true, + }); } fn addMachOCases(cases: *tests.StandaloneContext) void { diff --git a/test/link/wasm/extern/build.zig b/test/link/wasm/extern/build.zig new file mode 100644 index 0000000000..88cce88d98 --- /dev/null +++ b/test/link/wasm/extern/build.zig @@ -0,0 +1,17 @@ +const std = @import("std"); + +pub fn build(b: *std.build.Builder) void { + const mode = b.standardReleaseOptions(); + const exe = b.addExecutable("extern", "main.zig"); + exe.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .wasi }); + exe.setBuildMode(mode); + exe.addCSourceFile("foo.c", &.{}); + exe.use_llvm = false; + exe.use_lld = false; + + const run = exe.runEmulatable(); + run.expectStdOutEqual("Result: 30"); + + const test_step = b.step("test", "Run linker test"); + test_step.dependOn(&run.step); +} diff --git a/test/link/wasm/extern/foo.c b/test/link/wasm/extern/foo.c new file mode 100644 index 0000000000..0dafd7e112 --- /dev/null +++ b/test/link/wasm/extern/foo.c @@ -0,0 +1 @@ +int foo = 30; diff --git a/test/link/wasm/extern/main.zig b/test/link/wasm/extern/main.zig new file mode 100644 index 0000000000..b9fa1226eb --- /dev/null +++ b/test/link/wasm/extern/main.zig @@ -0,0 +1,8 @@ +const std = @import("std"); + +extern const foo: u32; + +pub fn main() void { + const std_out = std.io.getStdOut(); + std_out.writer().print("Result: {d}", .{foo}) catch {}; +} -- cgit v1.2.3