| author | |
| committer | |
| log | 4ba0ad295f2b3ff9cc28fd45939f849c3ace5dda |
| tree | bfd4f18623b20b582fd97dd5885d7e57e397cfba |
| parent | 9f9e51eb110289850ee30c0b4ca895a8861f4c40 |
| parent | 8627858bbc2fee848e2f3e3ca64dc944f39591e5 |
| signature |
wasm-linker: implement non-function extern variables8 files changed, 73 insertions(+), 22 deletions(-)
src/arch/wasm/CodeGen.zig-2| ... | ... | @@ -2394,9 +2394,7 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { |
| 2394 | 2394 | const decl_index = decl_ref_mut.data.decl_index; |
| 2395 | 2395 | return self.lowerDeclRefValue(.{ .ty = ty, .val = val }, decl_index); |
| 2396 | 2396 | } |
| 2397 | ||
| 2398 | 2397 | const target = self.target; |
| 2399 | ||
| 2400 | 2398 | switch (ty.zigTypeTag()) { |
| 2401 | 2399 | .Void => return WValue{ .none = {} }, |
| 2402 | 2400 | .Int => { |
src/link/Wasm.zig+34-11| ... | ... | @@ -607,6 +607,24 @@ fn resolveSymbolsInArchives(self: *Wasm) !void { |
| 607 | 607 | } |
| 608 | 608 | } |
| 609 | 609 | |
| 610 | fn 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 | ||
| 610 | 628 | pub fn deinit(self: *Wasm) void { |
| 611 | 629 | const gpa = self.base.allocator; |
| 612 | 630 | if (build_options.have_llvm) { |
| ... | ... | @@ -783,15 +801,17 @@ pub fn updateDecl(self: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !voi |
| 783 | 801 | |
| 784 | 802 | decl.link.wasm.clear(); |
| 785 | 803 | |
| 786 | if (decl.isExtern()) { | |
| 787 | return; | |
| 788 | } | |
| 789 | ||
| 790 | 804 | if (decl.val.castTag(.function)) |_| { |
| 791 | 805 | return; |
| 792 | 806 | } else if (decl.val.castTag(.extern_fn)) |_| { |
| 793 | 807 | return; |
| 794 | 808 | } |
| 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 | } | |
| 795 | 815 | const val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val; |
| 796 | 816 | |
| 797 | 817 | 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) |
| 834 | 854 | } |
| 835 | 855 | |
| 836 | 856 | fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, code: []const u8) !void { |
| 837 | if (code.len == 0) return; | |
| 838 | 857 | const mod = self.base.options.module.?; |
| 839 | 858 | const atom: *Atom = &decl.link.wasm; |
| 840 | atom.size = @intCast(u32, code.len); | |
| 841 | atom.alignment = decl.ty.abiAlignment(self.base.options.target); | |
| 842 | 859 | const symbol = &self.symbols.items[atom.sym_index]; |
| 843 | ||
| 844 | 860 | const full_name = try decl.getFullyQualifiedName(mod); |
| 845 | 861 | defer self.base.allocator.free(full_name); |
| 846 | 862 | symbol.name = try self.string_table.put(self.base.allocator, full_name); |
| 847 | 863 | try atom.code.appendSlice(self.base.allocator, code); |
| 848 | ||
| 849 | 864 | 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); | |
| 850 | 869 | } |
| 851 | 870 | |
| 852 | 871 | /// From a given symbol location, returns its `wasm.GlobalType`. |
| ... | ... | @@ -1235,7 +1254,10 @@ pub fn addOrUpdateImport( |
| 1235 | 1254 | .kind = .{ .function = ty_index }, |
| 1236 | 1255 | }; |
| 1237 | 1256 | } |
| 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 | } | |
| 1239 | 1261 | } |
| 1240 | 1262 | |
| 1241 | 1263 | /// Kind represents the type of an Atom, which is only |
| ... | ... | @@ -1438,7 +1460,7 @@ fn setupImports(self: *Wasm) !void { |
| 1438 | 1460 | if (std.mem.eql(u8, symbol_loc.getName(self), "__indirect_function_table")) { |
| 1439 | 1461 | continue; |
| 1440 | 1462 | } |
| 1441 | if (symbol.tag == .data or !symbol.requiresImport()) { | |
| 1463 | if (!symbol.requiresImport()) { | |
| 1442 | 1464 | continue; |
| 1443 | 1465 | } |
| 1444 | 1466 | |
| ... | ... | @@ -2007,6 +2029,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2007 | 2029 | } |
| 2008 | 2030 | |
| 2009 | 2031 | try self.resolveSymbolsInArchives(); |
| 2032 | try self.checkUndefinedSymbols(); | |
| 2010 | 2033 | |
| 2011 | 2034 | // When we finish/error we reset the state of the linker |
| 2012 | 2035 | // So we can rebuild the binary file on each incremental update |
src/link/Wasm/Atom.zig+6-8| ... | ... | @@ -172,18 +172,16 @@ fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wa |
| 172 | 172 | .R_WASM_MEMORY_ADDR_SLEB, |
| 173 | 173 | .R_WASM_MEMORY_ADDR_SLEB64, |
| 174 | 174 | => { |
| 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()); | |
| 179 | 176 | const merge_segment = wasm_bin.base.options.output_mode != .Obj; |
| 180 | const segment_info = if (self.file) |object_index| blk: { | |
| 177 | const target_atom_loc = wasm_bin.discarded.get(target_loc) orelse target_loc; | |
| 178 | const target_atom = wasm_bin.symbol_atom.get(target_atom_loc).?; | |
| 179 | const segment_info = if (target_atom.file) |object_index| blk: { | |
| 181 | 180 | break :blk wasm_bin.objects.items[object_index].segment_info; |
| 182 | 181 | } else wasm_bin.segment_info.items; |
| 183 | 182 | const segment_name = segment_info[symbol.index].outputName(merge_segment); |
| 184 | const atom_index = wasm_bin.data_segments.get(segment_name).?; | |
| 185 | const target_atom = wasm_bin.symbol_atom.get(target_loc).?; | |
| 186 | const segment = wasm_bin.segments.items[atom_index]; | |
| 183 | const segment_index = wasm_bin.data_segments.get(segment_name).?; | |
| 184 | const segment = wasm_bin.segments.items[segment_index]; | |
| 187 | 185 | return target_atom.offset + segment.offset + (relocation.addend orelse 0); |
| 188 | 186 | }, |
| 189 | 187 | .R_WASM_EVENT_INDEX_LEB => return symbol.index, |
src/link/Wasm/Symbol.zig+1-1| ... | ... | @@ -79,9 +79,9 @@ pub const Flag = enum(u32) { |
| 79 | 79 | /// Verifies if the given symbol should be imported from the |
| 80 | 80 | /// host environment or not |
| 81 | 81 | pub fn requiresImport(self: Symbol) bool { |
| 82 | if (self.tag == .data) return false; | |
| 82 | 83 | if (!self.isUndefined()) return false; |
| 83 | 84 | if (self.isWeak()) return false; |
| 84 | if (self.tag == .data) return false; | |
| 85 | 85 | // if (self.isDefined() and self.isWeak()) return true; //TODO: Only when building shared lib |
| 86 | 86 | |
| 87 | 87 | return true; |
test/link.zig+6| ... | ... | @@ -52,6 +52,12 @@ fn addWasmCases(cases: *tests.StandaloneContext) void { |
| 52 | 52 | .build_modes = true, |
| 53 | 53 | .requires_stage2 = true, |
| 54 | 54 | }); |
| 55 | ||
| 56 | cases.addBuildFile("test/link/wasm/extern/build.zig", .{ | |
| 57 | .build_modes = true, | |
| 58 | .requires_stage2 = true, | |
| 59 | .use_emulation = true, | |
| 60 | }); | |
| 55 | 61 | } |
| 56 | 62 | |
| 57 | 63 | fn addMachOCases(cases: *tests.StandaloneContext) void { |
test/link/wasm/extern/build.zig created+17| ... | ... | @@ -0,0 +1,17 @@ |
| 1 | const std = @import("std"); | |
| 2 | ||
| 3 | pub fn build(b: *std.build.Builder) void { | |
| 4 | const mode = b.standardReleaseOptions(); | |
| 5 | const exe = b.addExecutable("extern", "main.zig"); | |
| 6 | exe.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .wasi }); | |
| 7 | exe.setBuildMode(mode); | |
| 8 | exe.addCSourceFile("foo.c", &.{}); | |
| 9 | exe.use_llvm = false; | |
| 10 | exe.use_lld = false; | |
| 11 | ||
| 12 | const run = exe.runEmulatable(); | |
| 13 | run.expectStdOutEqual("Result: 30"); | |
| 14 | ||
| 15 | const test_step = b.step("test", "Run linker test"); | |
| 16 | test_step.dependOn(&run.step); | |
| 17 | } |
test/link/wasm/extern/foo.c created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | int foo = 30; |
test/link/wasm/extern/main.zig created+8| ... | ... | @@ -0,0 +1,8 @@ |
| 1 | const std = @import("std"); | |
| 2 | ||
| 3 | extern const foo: u32; | |
| 4 | ||
| 5 | pub fn main() void { | |
| 6 | const std_out = std.io.getStdOut(); | |
| 7 | std_out.writer().print("Result: {d}", .{foo}) catch {}; | |
| 8 | } |