diff --git a/cgo/security.go b/cgo/security.go index 2fea40c8a2..28bd23ef68 100644 --- a/cgo/security.go +++ b/cgo/security.go @@ -142,6 +142,7 @@ var validLinkerFlags = []*regexp.Regexp{ re(`-L([^@\-].*)`), re(`-O`), re(`-O([^@\-].*)`), + re(`--export=(.+)`), // for wasm-ld re(`-f(no-)?(pic|PIC|pie|PIE)`), re(`-f(no-)?openmp(-simd)?`), re(`-fsanitize=([^@\-].*)`), diff --git a/compiler/compiler.go b/compiler/compiler.go index bbaf6432f4..179a0928ee 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -1059,11 +1059,12 @@ func (b *builder) createFunctionStart(intrinsic bool) { if b.info.section != "" { b.llvmFn.SetSection(b.info.section) } - if b.info.exported && strings.HasPrefix(b.Triple, "wasm") { + if b.info.exported && b.info.module != "" && strings.HasPrefix(b.Triple, "wasm") { // Set the exported name. This is necessary for WebAssembly because // otherwise the function is not exported. - functionAttr := b.ctx.CreateStringAttribute("wasm-export-name", b.info.linkName) - b.llvmFn.AddFunctionAttr(functionAttr) + b.llvmFn.AddFunctionAttr(b.ctx.CreateStringAttribute("wasm-export-name", b.info.linkName)) + // Set the export module. + b.llvmFn.AddFunctionAttr(b.ctx.CreateStringAttribute("wasm-export-module", b.info.module)) } // Some functions have a pragma controlling the inlining level. diff --git a/compiler/symbol.go b/compiler/symbol.go index c4ce6f21df..491b845c6f 100644 --- a/compiler/symbol.go +++ b/compiler/symbol.go @@ -210,8 +210,9 @@ func (c *compilerContext) getFunction(fn *ssa.Function) llvm.Value { // exported. func (c *compilerContext) getFunctionInfo(f *ssa.Function) functionInfo { info := functionInfo{ - // Pick the default linkName. - linkName: f.RelString(nil), + module: "env", + importName: f.Name(), + linkName: f.RelString(nil), // pick the default linkName } // Check for //go: pragmas, which may change the link name (among others). info.parsePragmas(f) @@ -225,10 +226,6 @@ func (info *functionInfo) parsePragmas(f *ssa.Function) { return } if decl, ok := f.Syntax().(*ast.FuncDecl); ok && decl.Doc != nil { - - // Our importName for a wasm module (if we are compiling to wasm), or llvm link name - var importName string - for _, comment := range decl.Doc.List { text := comment.Text if strings.HasPrefix(text, "//export ") { @@ -246,7 +243,8 @@ func (info *functionInfo) parsePragmas(f *ssa.Function) { continue } - importName = parts[1] + info.importName = parts[1] + info.linkName = parts[1] info.exported = true case "//go:interrupt": if hasUnsafeImport(f.Pkg.Pkg) { @@ -254,10 +252,13 @@ func (info *functionInfo) parsePragmas(f *ssa.Function) { } case "//go:wasm-module": // Alternative comment for setting the import module. - if len(parts) != 2 { - continue + if len(parts) == 1 { + // Function must not be exported outside of the WebAssembly + // module (but only be made available for linking). + info.module = "" + } else if len(parts) == 2 { + info.module = parts[1] } - info.module = parts[1] case "//go:inline": info.inline = inlineHint case "//go:noinline": @@ -297,17 +298,6 @@ func (info *functionInfo) parsePragmas(f *ssa.Function) { } } } - - // Set the importName for our exported function if we have one - if importName != "" { - if info.module == "" { - info.linkName = importName - } else { - // WebAssembly import - info.importName = importName - } - } - } } diff --git a/compiler/testdata/pragma.ll b/compiler/testdata/pragma.ll index b243602d30..403f4f4a07 100644 --- a/compiler/testdata/pragma.ll +++ b/compiler/testdata/pragma.ll @@ -62,7 +62,7 @@ declare void @main.undefinedFunctionNotInSection(i8*) #0 attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } -attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="extern_func" "wasm-import-module"="env" "wasm-import-name"="extern_func" } +attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "wasm-export-module"="env" "wasm-export-name"="extern_func" "wasm-import-module"="env" "wasm-import-name"="extern_func" } attributes #3 = { inlinehint nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #4 = { noinline nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } -attributes #5 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="exportedFunctionInSection" "wasm-import-module"="env" "wasm-import-name"="exportedFunctionInSection" } +attributes #5 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "wasm-export-module"="env" "wasm-export-name"="exportedFunctionInSection" "wasm-import-module"="env" "wasm-import-name"="exportedFunctionInSection" } diff --git a/src/runtime/arch_tinygowasm.go b/src/runtime/arch_tinygowasm.go index 910858be6d..eb5ba5b588 100644 --- a/src/runtime/arch_tinygowasm.go +++ b/src/runtime/arch_tinygowasm.go @@ -68,6 +68,7 @@ func growHeap() bool { var allocs = make(map[uintptr][]byte) //export malloc +//go:wasm-module func libc_malloc(size uintptr) unsafe.Pointer { buf := make([]byte, size) ptr := unsafe.Pointer(&buf[0]) @@ -76,6 +77,7 @@ func libc_malloc(size uintptr) unsafe.Pointer { } //export free +//go:wasm-module func libc_free(ptr unsafe.Pointer) { if ptr == nil { return @@ -88,12 +90,14 @@ func libc_free(ptr unsafe.Pointer) { } //export calloc +//go:wasm-module func libc_calloc(nmemb, size uintptr) unsafe.Pointer { // No difference between calloc and malloc. return libc_malloc(nmemb * size) } //export realloc +//go:wasm-module func libc_realloc(oldPtr unsafe.Pointer, size uintptr) unsafe.Pointer { // It's hard to optimize this to expand the current buffer with our GC, but // it is theoretically possible. For now, just always allocate fresh.