From 66bcc55e0196bd43746a09a79668c7c495bd1f89 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Tue, 1 Nov 2022 15:05:09 +0100 Subject: [PATCH 1/2] llvm: mangle extern Wasm functions When Wasm extern functions contain the same name, but have a different module name such as `extern "a"` vs `extern "b"` LLVM will currently resolve the two functions to the same symbol. By mangling the name of the symbol, we ensure the functions are resolved seperately. We mangle the name by applying | where module is also known as the library name. --- src/codegen/llvm.zig | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index d92a444ea4..1290a207be 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1279,8 +1279,18 @@ pub const Object = struct { const llvm_global = self.decl_map.get(decl_index) orelse return; const decl = module.declPtr(decl_index); if (decl.isExtern()) { - llvm_global.setValueName(decl.name); - if (self.getLlvmGlobal(decl.name)) |other_global| { + const is_wasm_fn = module.getTarget().isWasm() and try decl.isFunction(); + const mangle_name = is_wasm_fn and + decl.getExternFn().?.lib_name != null and + !std.mem.eql(u8, std.mem.sliceTo(decl.getExternFn().?.lib_name.?, 0), "c"); + const decl_name = if (mangle_name) name: { + const tmp = try std.fmt.allocPrintZ(module.gpa, "{s}|{s}", .{ decl.name, decl.getExternFn().?.lib_name.? }); + break :name tmp.ptr; + } else decl.name; + defer if (mangle_name) module.gpa.free(std.mem.sliceTo(decl_name, 0)); + + llvm_global.setValueName(decl_name); + if (self.getLlvmGlobal(decl_name)) |other_global| { if (other_global != llvm_global) { log.debug("updateDeclExports isExtern()=true setValueName({s}) conflict", .{decl.name}); try self.extern_collisions.put(module.gpa, decl_index, {}); From ef0df24626cc6c3cfe0b87164d481eeb26cc57fb Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Tue, 1 Nov 2022 15:43:34 +0100 Subject: [PATCH 2/2] test/link: add linker test to verify mangling This adds a simple linker test to ensure the built library contains an import entry for each extern function call that was mangled. --- test/link.zig | 5 +++++ test/link/wasm/extern-mangle/a.zig | 1 + test/link/wasm/extern-mangle/b.zig | 1 + test/link/wasm/extern-mangle/build.zig | 24 ++++++++++++++++++++++++ test/link/wasm/extern-mangle/lib.zig | 6 ++++++ 5 files changed, 37 insertions(+) create mode 100644 test/link/wasm/extern-mangle/a.zig create mode 100644 test/link/wasm/extern-mangle/b.zig create mode 100644 test/link/wasm/extern-mangle/build.zig create mode 100644 test/link/wasm/extern-mangle/lib.zig diff --git a/test/link.zig b/test/link.zig index 40635b86a0..c94dc2f4a0 100644 --- a/test/link.zig +++ b/test/link.zig @@ -48,6 +48,11 @@ fn addWasmCases(cases: *tests.StandaloneContext) void { .use_emulation = true, }); + cases.addBuildFile("test/link/wasm/extern-mangle/build.zig", .{ + .build_modes = true, + .requires_stage2 = true, + }); + cases.addBuildFile("test/link/wasm/infer-features/build.zig", .{ .requires_stage2 = true, }); diff --git a/test/link/wasm/extern-mangle/a.zig b/test/link/wasm/extern-mangle/a.zig new file mode 100644 index 0000000000..98ff08774c --- /dev/null +++ b/test/link/wasm/extern-mangle/a.zig @@ -0,0 +1 @@ +pub extern "a" fn hello() i32; diff --git a/test/link/wasm/extern-mangle/b.zig b/test/link/wasm/extern-mangle/b.zig new file mode 100644 index 0000000000..06dfb5b79f --- /dev/null +++ b/test/link/wasm/extern-mangle/b.zig @@ -0,0 +1 @@ +pub extern "b" fn hello() i32; diff --git a/test/link/wasm/extern-mangle/build.zig b/test/link/wasm/extern-mangle/build.zig new file mode 100644 index 0000000000..f2916c35a7 --- /dev/null +++ b/test/link/wasm/extern-mangle/build.zig @@ -0,0 +1,24 @@ +const std = @import("std"); +const Builder = std.build.Builder; + +pub fn build(b: *Builder) void { + const mode = b.standardReleaseOptions(); + + const test_step = b.step("test", "Test"); + test_step.dependOn(b.getInstallStep()); + + const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned); + lib.setBuildMode(mode); + lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }); + lib.install(); + + const check_lib = lib.checkObject(.wasm); + check_lib.checkStart("Section import"); + check_lib.checkNext("entries 2"); // a.hello & b.hello + check_lib.checkNext("module a"); + check_lib.checkNext("name hello"); + check_lib.checkNext("module b"); + check_lib.checkNext("name hello"); + + test_step.dependOn(&check_lib.step); +} diff --git a/test/link/wasm/extern-mangle/lib.zig b/test/link/wasm/extern-mangle/lib.zig new file mode 100644 index 0000000000..d2f2126c7c --- /dev/null +++ b/test/link/wasm/extern-mangle/lib.zig @@ -0,0 +1,6 @@ +const a = @import("a.zig").hello; +const b = @import("b.zig").hello; +export fn foo() void { + _ = a(); + _ = b(); +}