Skip to content

Commit 4ba0ad2

Browse files
authored
Merge pull request #12687 from Luukdegram/wasm-extern-globals
wasm-linker: implement non-function extern variables
2 parents 9f9e51e + 8627858 commit 4ba0ad2

File tree

8 files changed

+73
-22
lines changed

8 files changed

+73
-22
lines changed

src/arch/wasm/CodeGen.zig

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,24 @@ fn resolveSymbolsInArchives(self: *Wasm) !void {
607607
}
608608
}
609609

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+
610628
pub 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

836856
fn 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

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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
8181
pub 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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

5763
fn addMachOCases(cases: *tests.StandaloneContext) void {

test/link/wasm/extern/build.zig

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
int foo = 30;

test/link/wasm/extern/main.zig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
}

0 commit comments

Comments
 (0)