mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
cmd/internal/obj/wasm, runtime: detect wasmexport call before runtime initialization
If a wasmexport function is called from the host before initializing the Go Wasm module, currently it will likely fail with a bounds error, because the uninitialized SP is 0, and any SP decrement will make it out of bounds. As at least some Wasm runtime doesn't call _initialize by default, This error can be common. And the bounds error looks confusing to the users. Therefore, we detect this case and emit a clearer error. Fixes #71240. Updates #65199. Change-Id: I107095f08c76cdceb7781ab0304218eab7029ab6 Reviewed-on: https://go-review.googlesource.com/c/go/+/643115 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Michael Knyszek <mknyszek@google.com>
This commit is contained in:
parent
6a4effa08b
commit
0b632d26b9
4 changed files with 48 additions and 2 deletions
|
|
@ -129,6 +129,7 @@ var (
|
|||
morestackNoCtxt *obj.LSym
|
||||
sigpanic *obj.LSym
|
||||
wasm_pc_f_loop_export *obj.LSym
|
||||
runtimeNotInitialized *obj.LSym
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -149,6 +150,7 @@ func instinit(ctxt *obj.Link) {
|
|||
morestackNoCtxt = ctxt.Lookup("runtime.morestack_noctxt")
|
||||
sigpanic = ctxt.LookupABI("runtime.sigpanic", obj.ABIInternal)
|
||||
wasm_pc_f_loop_export = ctxt.Lookup("wasm_pc_f_loop_export")
|
||||
runtimeNotInitialized = ctxt.Lookup("runtime.notInitialized")
|
||||
}
|
||||
|
||||
func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) {
|
||||
|
|
@ -255,7 +257,7 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) {
|
|||
p = appendp(p, AEnd)
|
||||
}
|
||||
|
||||
if framesize > 0 {
|
||||
if framesize > 0 && s.Func().WasmExport == nil { // genWasmExportWrapper has its own prologue generation
|
||||
p := s.Func().Text
|
||||
p = appendp(p, AGet, regAddr(REG_SP))
|
||||
p = appendp(p, AI32Const, constAddr(framesize))
|
||||
|
|
@ -935,6 +937,23 @@ func genWasmExportWrapper(s *obj.LSym, appendp func(p *obj.Prog, as obj.As, args
|
|||
panic("wrapper functions for WASM export should not have a body")
|
||||
}
|
||||
|
||||
// Detect and error out if called before runtime initialization
|
||||
// SP is 0 if not initialized
|
||||
p = appendp(p, AGet, regAddr(REG_SP))
|
||||
p = appendp(p, AI32Eqz)
|
||||
p = appendp(p, AIf)
|
||||
p = appendp(p, ACall, obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: runtimeNotInitialized})
|
||||
p = appendp(p, AEnd)
|
||||
|
||||
// Now that we've checked the SP, generate the prologue
|
||||
if framesize > 0 {
|
||||
p = appendp(p, AGet, regAddr(REG_SP))
|
||||
p = appendp(p, AI32Const, constAddr(framesize))
|
||||
p = appendp(p, AI32Sub)
|
||||
p = appendp(p, ASet, regAddr(REG_SP))
|
||||
p.Spadj = int32(framesize)
|
||||
}
|
||||
|
||||
// Store args
|
||||
for i, f := range we.Params {
|
||||
p = appendp(p, AGet, regAddr(REG_SP))
|
||||
|
|
@ -1056,6 +1075,7 @@ var notUsePC_B = map[string]bool{
|
|||
"runtime.gcWriteBarrier6": true,
|
||||
"runtime.gcWriteBarrier7": true,
|
||||
"runtime.gcWriteBarrier8": true,
|
||||
"runtime.notInitialized": true,
|
||||
"runtime.wasmDiv": true,
|
||||
"runtime.wasmTruncS": true,
|
||||
"runtime.wasmTruncU": true,
|
||||
|
|
@ -1121,7 +1141,8 @@ func assemble(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) {
|
|||
"runtime.gcWriteBarrier5",
|
||||
"runtime.gcWriteBarrier6",
|
||||
"runtime.gcWriteBarrier7",
|
||||
"runtime.gcWriteBarrier8":
|
||||
"runtime.gcWriteBarrier8",
|
||||
"runtime.notInitialized":
|
||||
// no locals
|
||||
useAssemblyRegMap()
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ var wasmFuncTypes = map[string]*wasmFuncType{
|
|||
"runtime.gcWriteBarrier6": {Results: []byte{I64}}, // -> bufptr
|
||||
"runtime.gcWriteBarrier7": {Results: []byte{I64}}, // -> bufptr
|
||||
"runtime.gcWriteBarrier8": {Results: []byte{I64}}, // -> bufptr
|
||||
"runtime.notInitialized": {}, //
|
||||
"cmpbody": {Params: []byte{I64, I64, I64, I64}, Results: []byte{I64}}, // a, alen, b, blen -> -1/0/1
|
||||
"memeqbody": {Params: []byte{I64, I64, I64}, Results: []byte{I64}}, // a, b, len -> 0/1
|
||||
"memcmp": {Params: []byte{I32, I32, I32}, Results: []byte{I32}}, // a, b, len -> <0/0/>0
|
||||
|
|
|
|||
|
|
@ -614,3 +614,13 @@ TEXT runtime·pause(SB), NOSPLIT, $0-8
|
|||
I32Const $1
|
||||
Set PAUSE
|
||||
RETUNWIND
|
||||
|
||||
// Called if a wasmexport function is called before runtime initialization
|
||||
TEXT runtime·notInitialized(SB), NOSPLIT, $0
|
||||
MOVD $runtime·wasmStack+(m0Stack__size-16-8)(SB), SP
|
||||
I32Const $0 // entry PC_B
|
||||
Call runtime·notInitialized1(SB)
|
||||
Drop
|
||||
I32Const $0 // entry PC_B
|
||||
Call runtime·abort(SB)
|
||||
UNDEF
|
||||
|
|
|
|||
|
|
@ -34,3 +34,17 @@ func gostartcall(buf *gobuf, fn, ctxt unsafe.Pointer) {
|
|||
buf.pc = uintptr(fn)
|
||||
buf.ctxt = ctxt
|
||||
}
|
||||
|
||||
func notInitialized() // defined in assembly, call notInitialized1
|
||||
|
||||
// Called if a wasmexport function is called before runtime initialization
|
||||
//
|
||||
//go:nosplit
|
||||
func notInitialized1() {
|
||||
writeErrStr("runtime: wasmexport function called before runtime initialization\n")
|
||||
if isarchive || islibrary {
|
||||
writeErrStr("\tcall _initialize first\n")
|
||||
} else {
|
||||
writeErrStr("\tcall _start first\n")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue