authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-01-02 15:45:35+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-01-03 10:02:54+01:00
logb9224c172fea2399623bd707a10e021e776329bc
tree0b219ff58e217077a274f98cf0453a4938464c90
parentf9b3e8c762450c20a13c3f51ab91398f409781fb
signaturelock-open Commit is signed but in an unrecognized format.

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.

3 files changed, 31 insertions(+), 18 deletions(-)

src/link/Wasm.zig+24-10
......@@ -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+4-3
......@@ -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+3-5
......@@ -2,13 +2,11 @@ const std = @import("std");
22const Builder = std.build.Builder;
33
44pub 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");