From 8bbfb1ee68245a967081165c7161cd3421f97823 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Sun, 4 Sep 2022 14:44:28 +0200 Subject: [PATCH] wasm: do not allow undefined symbols --allow-undefined can be a problem: it allows compiling code that will fail when loaded. This change makes sure that if some symbols are undefined, they are reported as an error by the linker. Previously, people could get away with importing a function that was not defined, like this: func add(int a, int b) int func test() { println(add(3, 5)) } This was always unintended but mostly worked. With this change, it isn't possible anymore. Now every function needs to be marked with //export explicitly: //export add func add(int a, int b) int func test() { println(add(3, 5)) } As before, functions will be placed in the `env` module with the name set from the `//export` tag. This can be overridden with `//go:import-module`: //go:import-module math //export add func add(int a, int b) int func test() { println(add(3, 5)) } For the syscall/js package, I needed to give a list of symbols that are undefined. This list is based on the JavaScript functions defined in targets/wasm_exec.js. --- compiler/symbol.go | 22 ++++++++++++---------- compiler/testdata/pragma.ll | 4 ++-- src/internal/task/task_asyncify_wasm.S | 4 ++++ targets/wasi.json | 1 - targets/wasm-undefined.txt | 16 ++++++++++++++++ targets/wasm.json | 2 +- 6 files changed, 35 insertions(+), 14 deletions(-) create mode 100644 targets/wasm-undefined.txt diff --git a/compiler/symbol.go b/compiler/symbol.go index 03db781e..c4ce6f21 100644 --- a/compiler/symbol.go +++ b/compiler/symbol.go @@ -163,18 +163,20 @@ func (c *compilerContext) getFunction(fn *ssa.Function) llvm.Value { // External/exported functions may not retain pointer values. // https://golang.org/cmd/cgo/#hdr-Passing_pointers if info.exported { - // Set the wasm-import-module attribute if the function's module is set. - if info.module != "" { - + if c.archFamily() == "wasm32" { // We need to add the wasm-import-module and the wasm-import-name - wasmImportModuleAttr := c.ctx.CreateStringAttribute("wasm-import-module", info.module) - llvmFn.AddFunctionAttr(wasmImportModuleAttr) - - // Add the Wasm Import Name, if we are a named wasm import - if info.importName != "" { - wasmImportNameAttr := c.ctx.CreateStringAttribute("wasm-import-name", info.importName) - llvmFn.AddFunctionAttr(wasmImportNameAttr) + // attributes. + module := info.module + if module == "" { + module = "env" } + llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("wasm-import-module", module)) + + name := info.importName + if name == "" { + name = info.linkName + } + llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("wasm-import-name", name)) } nocaptureKind := llvm.AttributeKindID("nocapture") nocapture := c.ctx.CreateEnumAttribute(nocaptureKind, 0) diff --git a/compiler/testdata/pragma.ll b/compiler/testdata/pragma.ll index 9aa1de3d..b243602d 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" } +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 #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" } +attributes #5 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="exportedFunctionInSection" "wasm-import-module"="env" "wasm-import-name"="exportedFunctionInSection" } diff --git a/src/internal/task/task_asyncify_wasm.S b/src/internal/task/task_asyncify_wasm.S index 3b00ede1..9aafc110 100644 --- a/src/internal/task/task_asyncify_wasm.S +++ b/src/internal/task/task_asyncify_wasm.S @@ -2,12 +2,16 @@ .functype start_unwind (i32) -> () .import_module start_unwind, asyncify +.import_name start_unwind, start_unwind .functype stop_unwind () -> () .import_module stop_unwind, asyncify +.import_name stop_unwind, stop_unwind .functype start_rewind (i32) -> () .import_module start_rewind, asyncify +.import_name start_rewind, start_rewind .functype stop_rewind () -> () .import_module stop_rewind, asyncify +.import_name stop_rewind, stop_rewind .global tinygo_unwind .hidden tinygo_unwind diff --git a/targets/wasi.json b/targets/wasi.json index dfec729e..e710b4bb 100644 --- a/targets/wasi.json +++ b/targets/wasi.json @@ -15,7 +15,6 @@ "-msign-ext" ], "ldflags": [ - "--allow-undefined", "--stack-first", "--no-demangle" ], diff --git a/targets/wasm-undefined.txt b/targets/wasm-undefined.txt new file mode 100644 index 00000000..ccb6c13c --- /dev/null +++ b/targets/wasm-undefined.txt @@ -0,0 +1,16 @@ +syscall/js.copyBytesToGo +syscall/js.copyBytesToJS +syscall/js.finalizeRef +syscall/js.stringVal +syscall/js.valueCall +syscall/js.valueDelete +syscall/js.valueGet +syscall/js.valueIndex +syscall/js.valueInstanceOf +syscall/js.valueInvoke +syscall/js.valueLength +syscall/js.valueLoadString +syscall/js.valueNew +syscall/js.valuePrepareString +syscall/js.valueSet +syscall/js.valueSetIndex diff --git a/targets/wasm.json b/targets/wasm.json index c668a68c..2bcada5f 100644 --- a/targets/wasm.json +++ b/targets/wasm.json @@ -15,7 +15,7 @@ "-msign-ext" ], "ldflags": [ - "--allow-undefined", + "--allow-undefined-file={root}/targets/wasm-undefined.txt", "--stack-first", "--no-demangle" ],