Skip to content

Commit b9224c1

Browse files
committed
wasm-linker: Fix & mangle symbol name of imports
When outputting the names section, we should output the actual symbol name rather than the import name. This makes sure that symbols with an explicit name set have the correct name but retain the import name too. We also now correctly mangle the name of an extern function with an explicit library name. This ensures that functions that have a different library name, but the same import/function name, can be resolved correctly with other modules and don't resolve to the same symbol.
1 parent f9b3e8c commit b9224c1

3 files changed

Lines changed: 31 additions & 18 deletions

File tree

src/link/Wasm.zig

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -813,7 +813,12 @@ fn checkUndefinedSymbols(wasm: *const Wasm) !void {
813813
const file_name = if (undef.file) |file_index| name: {
814814
break :name wasm.objects.items[file_index].name;
815815
} else wasm.name;
816-
log.err("could not resolve undefined symbol '{s}'", .{undef.getName(wasm)});
816+
const import_name = if (undef.file) |file_index| name: {
817+
const obj = wasm.objects.items[file_index];
818+
const name_index = obj.findImport(symbol.tag.externalType(), symbol.index).name;
819+
break :name obj.string_table.get(name_index);
820+
} else wasm.string_table.get(wasm.imports.get(undef).?.name);
821+
log.err("could not resolve undefined symbol '{s}'", .{import_name});
817822
log.err(" defined in '{s}'", .{file_name});
818823
}
819824
}
@@ -1430,18 +1435,31 @@ pub fn addOrUpdateImport(
14301435
type_index: ?u32,
14311436
) !void {
14321437
assert(symbol_index != 0);
1433-
// For the import name itwasm, we use the decl's name, rather than the fully qualified name
1434-
const decl_name_index = try wasm.string_table.put(wasm.base.allocator, name);
1438+
// For the import name, we use the decl's name, rather than the fully qualified name
1439+
// Also mangle the name when the lib name is set and not equal to "C" so imports with the same
1440+
// name but different module can be resolved correctly.
1441+
const mangle_name = lib_name != null and
1442+
!std.mem.eql(u8, std.mem.sliceTo(lib_name.?, 0), "c");
1443+
const full_name = if (mangle_name) full_name: {
1444+
break :full_name try std.fmt.allocPrint(wasm.base.allocator, "{s}|{s}", .{ name, lib_name.? });
1445+
} else name;
1446+
defer if (mangle_name) wasm.base.allocator.free(full_name);
1447+
1448+
const decl_name_index = try wasm.string_table.put(wasm.base.allocator, full_name);
14351449
const symbol: *Symbol = &wasm.symbols.items[symbol_index];
14361450
symbol.setUndefined(true);
14371451
symbol.setGlobal(true);
14381452
symbol.name = decl_name_index;
1453+
if (mangle_name) {
1454+
// we specified a specific name for the symbol that does not match the import name
1455+
symbol.setFlag(.WASM_SYM_EXPLICIT_NAME);
1456+
}
14391457
const global_gop = try wasm.globals.getOrPut(wasm.base.allocator, decl_name_index);
14401458
if (!global_gop.found_existing) {
14411459
const loc: SymbolLoc = .{ .file = null, .index = symbol_index };
14421460
global_gop.value_ptr.* = loc;
14431461
try wasm.resolved_symbols.put(wasm.base.allocator, loc, {});
1444-
try wasm.undefs.putNoClobber(wasm.base.allocator, name, loc);
1462+
try wasm.undefs.putNoClobber(wasm.base.allocator, full_name, loc);
14451463
}
14461464

14471465
if (type_index) |ty_index| {
@@ -1452,7 +1470,7 @@ pub fn addOrUpdateImport(
14521470
if (!gop.found_existing) {
14531471
gop.value_ptr.* = .{
14541472
.module_name = try wasm.string_table.put(wasm.base.allocator, module_name),
1455-
.name = decl_name_index,
1473+
.name = try wasm.string_table.put(wasm.base.allocator, name),
14561474
.kind = .{ .function = ty_index },
14571475
};
14581476
}
@@ -3130,11 +3148,7 @@ fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem
31303148

31313149
for (wasm.resolved_symbols.keys()) |sym_loc| {
31323150
const symbol = sym_loc.getSymbol(wasm).*;
3133-
const name = if (symbol.isUndefined()) blk: {
3134-
if (symbol.tag == .data) continue;
3135-
const imp = wasm.imports.get(sym_loc) orelse continue;
3136-
break :blk wasm.string_table.get(imp.name);
3137-
} else sym_loc.getName(wasm);
3151+
const name = sym_loc.getName(wasm);
31383152
switch (symbol.tag) {
31393153
.function => {
31403154
const gop = funcs.getOrPutAssumeCapacity(symbol.index);

test/link.zig

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,10 @@ fn addWasmCases(cases: *tests.StandaloneContext) void {
4747
.requires_stage2 = true,
4848
});
4949

50-
cases.addBuildFile("test/link/wasm/export-data/build.zig", .{
51-
.build_modes = true,
52-
});
50+
// TODO: Fix open handle in wasm-linker refraining rename from working on Windows.
51+
if (builtin.os.tag != .windows) {
52+
cases.addBuildFile("test/link/wasm/export-data/build.zig", .{});
53+
}
5354

5455
cases.addBuildFile("test/link/wasm/extern/build.zig", .{
5556
.build_modes = true,

test/link/wasm/export-data/build.zig

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,11 @@ const std = @import("std");
22
const Builder = std.build.Builder;
33

44
pub fn build(b: *Builder) void {
5-
const mode = b.standardReleaseOptions();
6-
75
const test_step = b.step("test", "Test");
86
test_step.dependOn(b.getInstallStep());
97

108
const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned);
11-
lib.setBuildMode(mode);
9+
lib.setBuildMode(.ReleaseSafe); // to make the output deterministic in address positions
1210
lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
1311
lib.use_lld = false;
1412
lib.export_symbol_names = &.{ "foo", "bar" };
@@ -25,8 +23,8 @@ pub fn build(b: *Builder) void {
2523
check_lib.checkNext("type i32");
2624
check_lib.checkNext("mutable false");
2725
check_lib.checkNext("i32.const {bar_address}");
28-
check_lib.checkComputeCompare("foo_address", .{ .op = .eq, .value = .{ .literal = 0x0c } });
29-
check_lib.checkComputeCompare("bar_address", .{ .op = .eq, .value = .{ .literal = 0x10 } });
26+
check_lib.checkComputeCompare("foo_address", .{ .op = .eq, .value = .{ .literal = 0 } });
27+
check_lib.checkComputeCompare("bar_address", .{ .op = .eq, .value = .{ .literal = 4 } });
3028

3129
check_lib.checkStart("Section export");
3230
check_lib.checkNext("entries 3");

0 commit comments

Comments
 (0)