asmcall macos/arm64: traceback: unexpected SPWRITE function
Obligatory thanks for an amazing lib!
To repro:
cd examples/example-bidirectional
Seems like the asmcall is broken, seemingly for functions that return a value. Pure one-way functions work (see start() below) Even a primitive (bool) triggers the issue.
Annotated with #[cgo_callback] fixes the issue, at least in my app:
#[rust2go::r2g]
pub trait Runner {
fn start();
#[cgo_callback]
fn next() -> Output;
Stack trace:
cargo run
Compiling example-bi v0.1.0 (/Users/didrik/Code/rust2go/examples/example-bidirectional)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 2.45s
Running `/Users/didrik/Code/rust2go/target/debug/example-bi`
========== Start oneway demo ==========
[Rust Caller] will call golang with user name=chihai, age=28
[Go Callee] Received name: chihai, age: 28, will log them by calling rust
traceback: unexpected SPWRITE function github.com/ihciah/rust2go/asmcall.CallFuncG0P1
fatal error: traceback
runtime stack:
runtime.throw({0x1002b6b2f?, 0x100278600?})
/usr/local/go/src/runtime/panic.go:1067 +0x38 fp=0x16fc26380 sp=0x16fc26350 pc=0x1002429a8
runtime.(*unwinder).resolveInternal(0x16fc264d0, 0x0?, 0xa0?)
/usr/local/go/src/runtime/traceback.go:365 +0x318 fp=0x16fc26400 sp=0x16fc26380 pc=0x1002312f8
runtime.(*unwinder).next(0x16fc264d0)
/usr/local/go/src/runtime/traceback.go:513 +0x160 fp=0x16fc26490 sp=0x16fc26400 pc=0x1002314a0
runtime.(*_panic).nextFrame.func1()
/usr/local/go/src/runtime/panic.go:962 +0x8c fp=0x16fc26550 sp=0x16fc26490 pc=0x10020d74c
runtime.systemstack(0x0)
/usr/local/go/src/runtime/asm_arm64.s:244 +0x6c fp=0x16fc26560 sp=0x16fc26550 pc=0x10024765c
goroutine 17 gp=0x14000002700 m=1 mp=0x1400005a008 [running, locked to thread]:
runtime.systemstack_switch()
/usr/local/go/src/runtime/asm_arm64.s:201 +0x8 fp=0x14000068ad0 sp=0x14000068ac0 pc=0x1002475d8
runtime.(*_panic).nextFrame(0x14000068b28?)
/usr/local/go/src/runtime/panic.go:935 +0x64 fp=0x14000068b10 sp=0x14000068ad0 pc=0x10020d694
runtime.(*_panic).start(0x14000068b68?, 0x10026e014?, 0x14000068b68?)
/usr/local/go/src/runtime/panic.go:849 +0x14c fp=0x14000068b30 sp=0x14000068b10 pc=0x10020d43c
panic({0x1002f7cc0?, 0x1003845e0?})
/usr/local/go/src/runtime/panic.go:779 +0x114 fp=0x14000068be0 sp=0x14000068b30 pc=0x100242714
runtime.panicmem(...)
/usr/local/go/src/runtime/panic.go:262
runtime.sigpanic()
/usr/local/go/src/runtime/signal_unix.go:917 +0x300 fp=0x14000068c40 sp=0x14000068be0 pc=0x100243fd0
github.com/ihciah/rust2go/asmcall.CallFuncG0P1(0x1001dc85c, 0x14000068cc8)
/Users/didrik/Code/rust2go/asmcall/arm64.s:44 +0x18 fp=0x14000068c50 sp=0x14000068c50 pc=0x100278618
goroutine 18 gp=0x1400008a380 m=nil [force gc (idle)]:
runtime.gopark(0x0?, 0x0?, 0x0?, 0x0?, 0x0?)
/usr/local/go/src/runtime/proc.go:424 +0xc8 fp=0x14000050790 sp=0x14000050770 pc=0x100242a88
runtime.goparkunlock(...)
/usr/local/go/src/runtime/proc.go:430
runtime.forcegchelper()
/usr/local/go/src/runtime/proc.go:337 +0xb8 fp=0x140000507d0 sp=0x14000050790 pc=0x1002113a8
runtime.goexit({})
/usr/local/go/src/runtime/asm_arm64.s:1223 +0x4 fp=0x140000507d0 sp=0x140000507d0 pc=0x100249b34
created by runtime.init.7 in goroutine 1
/usr/local/go/src/runtime/proc.go:325 +0x24
goroutine 19 gp=0x1400008a540 m=nil [GC sweep wait]:
runtime.gopark(0x0?, 0x0?, 0x0?, 0x0?, 0x0?)
/usr/local/go/src/runtime/proc.go:424 +0xc8 fp=0x14000050f60 sp=0x14000050f40 pc=0x100242a88
runtime.goparkunlock(...)
/usr/local/go/src/runtime/proc.go:430
runtime.bgsweep(0x1400009a000)
/usr/local/go/src/runtime/mgcsweep.go:277 +0xa0 fp=0x14000050fb0 sp=0x14000050f60 pc=0x1001fd410
runtime.gcenable.gowrap1()
/usr/local/go/src/runtime/mgc.go:204 +0x28 fp=0x14000050fd0 sp=0x14000050fb0 pc=0x1001f1658
runtime.goexit({})
/usr/local/go/src/runtime/asm_arm64.s:1223 +0x4 fp=0x14000050fd0 sp=0x14000050fd0 pc=0x100249b34
created by runtime.gcenable in goroutine 1
/usr/local/go/src/runtime/mgc.go:204 +0x6c
goroutine 20 gp=0x1400008a700 m=nil [GC scavenge wait]:
runtime.gopark(0x1400009a000?, 0x1002db7a8?, 0x1?, 0x0?, 0x1400008a700?)
/usr/local/go/src/runtime/proc.go:424 +0xc8 fp=0x14000051760 sp=0x14000051740 pc=0x100242a88
runtime.goparkunlock(...)
/usr/local/go/src/runtime/proc.go:430
runtime.(*scavengerState).park(0x10038f080)
/usr/local/go/src/runtime/mgcscavenge.go:425 +0x5c fp=0x14000051790 sp=0x14000051760 pc=0x1001fae0c
runtime.bgscavenge(0x1400009a000)
/usr/local/go/src/runtime/mgcscavenge.go:653 +0x44 fp=0x140000517b0 sp=0x14000051790 pc=0x1001fb354
runtime.gcenable.gowrap2()
/usr/local/go/src/runtime/mgc.go:205 +0x28 fp=0x140000517d0 sp=0x140000517b0 pc=0x1001f15f8
runtime.goexit({})
/usr/local/go/src/runtime/asm_arm64.s:1223 +0x4 fp=0x140000517d0 sp=0x140000517d0 pc=0x100249b34
created by runtime.gcenable in goroutine 1
/usr/local/go/src/runtime/mgc.go:205 +0xac
goroutine 35 gp=0x14000106380 m=nil [finalizer wait]:
runtime.gopark(0x0?, 0x100221ee8?, 0xc8?, 0x45?, 0x10021b7e8?)
/usr/local/go/src/runtime/proc.go:424 +0xc8 fp=0x14000054580 sp=0x14000054560 pc=0x100242a88
runtime.runfinq()
/usr/local/go/src/runtime/mfinal.go:193 +0x108 fp=0x140000547d0 sp=0x14000054580 pc=0x1001f0758
runtime.goexit({})
/usr/local/go/src/runtime/asm_arm64.s:1223 +0x4 fp=0x140000547d0 sp=0x140000547d0 pc=0x100249b34
created by runtime.createfing in goroutine 1
/usr/local/go/src/runtime/mfinal.go:163 +0x80
[1] 71355 abort cargo run
go version go1.23.4 darwin/arm64 rustc 1.84.0 (9fc6b4312 2025-01-07) Darwin MacBook-Air.local 23.5.0 Darwin Kernel Version 23.5.0: Wed May 1 20:16:51 PDT 2024; root:xnu-10063.121.3~5/RELEASE_ARM64_T8103 arm64
Also, running with GODEBUG=invalidptr=0,cgocheck=0 cargo run makes no difference.
There may be something wrong here at arm64. Please use cgo_callback instead at this time.
There may be something wrong here at arm64. Please use
cgo_callbackinstead at this time.
Is there related doc update?
The issue for me was iOS dev. I turned on cgo_callback and the Go runtime panics. I didn't know how to get the GODEBUG env bars through. Setting them in XCode didn't work for me.
I was able to fix it by tweaking the generated Go code with runtime.Pinner (but only for G2R calls, the callbacks I failed with). Is there a reason pinning isn't used?
For my calls the perf implications are not important, so it would be nice if the slow (cgo) version works without disabling runtime checks. I don't care about speed or memory copies, just communication. The only "perf" I care about is memory leaks.
Full disclosure, I might go down a different route anyway (using swift/kotlin bridges for mobile) so I may not use Rust at all going forward.
Thanks!
Perhaps we can allow users to specify whether to copy or not to enhance ease of use.
I don't see runtime.Pinner in the project, I guess it's for compatibility with older versions. I think it's a good idea to avoid disabling cgocheck via runtime.Pinner.