authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-08-31 13:44:06+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-08-31 13:44:06+02:00
log4ba0ad295f2b3ff9cc28fd45939f849c3ace5dda
treebfd4f18623b20b582fd97dd5885d7e57e397cfba
parent9f9e51eb110289850ee30c0b4ca895a8861f4c40
parent8627858bbc2fee848e2f3e3ca64dc944f39591e5
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #12687 from Luukdegram/wasm-extern-globals

wasm-linker: implement non-function extern variables

8 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 {
23942394 const decl_index = decl_ref_mut.data.decl_index;
23952395 return self.lowerDeclRefValue(.{ .ty = ty, .val = val }, decl_index);
23962396 }
2397
23982397 const target = self.target;
2399
24002398 switch (ty.zigTypeTag()) {
24012399 .Void => return WValue{ .none = {} },
24022400 .Int => {
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+6-8
......@@ -172,18 +172,16 @@ 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;
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: {
181180 break :blk wasm_bin.objects.items[object_index].segment_info;
182181 } else wasm_bin.segment_info.items;
183182 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];
187185 return target_atom.offset + segment.offset + (relocation.addend orelse 0);
188186 },
189187 .R_WASM_EVENT_INDEX_LEB => return symbol.index,
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;
test/link.zig+6
......@@ -52,6 +52,12 @@ fn addWasmCases(cases: *tests.StandaloneContext) void {
5252 .build_modes = true,
5353 .requires_stage2 = true,
5454 });
55
56 cases.addBuildFile("test/link/wasm/extern/build.zig", .{
57 .build_modes = true,
58 .requires_stage2 = true,
59 .use_emulation = true,
60 });
5561}
5662
5763fn addMachOCases(cases: *tests.StandaloneContext) void {
test/link/wasm/extern/build.zig created+17
......@@ -0,0 +1,17 @@
1const std = @import("std");
2
3pub 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 @@
1int foo = 30;
test/link/wasm/extern/main.zig created+8
......@@ -0,0 +1,8 @@
1const std = @import("std");
2
3extern const foo: u32;
4
5pub fn main() void {
6 const std_out = std.io.getStdOut();
7 std_out.writer().print("Result: {d}", .{foo}) catch {};
8}