PureGo Version
1512f32
Operating System
Go Version (go version)
go version go1.26.3 linux/386
What steps will reproduce the problem?
- Replace
examples/libc/main.go by:
package main
import (
"github.com/ebitengine/purego"
)
func main() {
purego.Dlopen("libstdc++.so.6", 0)
}
go run main go
What is the expected result?
No output.
What happens instead?
SIGSEGV: segmentation violation
PC=0xf77fa58b m=0 sigcode=128 addr=0x0
signal arrived during cgo execution
goroutine 1 gp=0x8408148 m=0 mp=0x81b7720 [syscall]:
runtime.cgocall(0x80e56d0, 0x8532000)
/usr/lib/go/src/runtime/cgocall.go:167 +0x61 fp=0x845cb84 sp=0x845cb6c pc=0x80c60c1
github.com/ebitengine/purego.syscall_SyscallN(0xf7e7b800, {0x845cbe4, 0x20, 0x20}, {0x845cc78, 0x20, 0x20}, 0x0)
/root/purego/syscall_32bit.go:41 +0x350 fp=0x845cb98 sp=0x845cb84 pc=0x80e46c0
github.com/ebitengine/purego.RegisterFunc.func4({0x851c468, 0x2, 0x2})
/root/purego/func.go:325 +0x3da fp=0x845cda4 sp=0x845cb98 pc=0x80e358a
reflect.callReflect(0x851c378, 0x845cf78, 0x845cf70, 0x0)
/usr/lib/go/src/reflect/value.go:772 +0x3f6 fp=0x845cf60 sp=0x845cda4 pc=0x80d8ca6
reflect.makeFuncStub()
/usr/lib/go/src/reflect/asm_386.s:21 +0x28 fp=0x845cf78 sp=0x845cf60 pc=0x80dc548
github.com/ebitengine/purego.Dlopen({0x810b286, 0xe}, 0x0)
/root/purego/dlfcn.go:41 +0x36 fp=0x845cf8c sp=0x845cf78 pc=0x80e2c86
main.main()
/root/purego/examples/libc/main.go:13 +0x33 fp=0x845cfa8 sp=0x845cf8c pc=0x80ead03
runtime.main()
/usr/lib/go/src/runtime/proc.go:290 +0x2dc fp=0x845cff0 sp=0x845cfa8 pc=0x8095a7c
runtime.goexit({})
/usr/lib/go/src/runtime/asm_386.s:1389 +0x1 fp=0x845cff4 sp=0x845cff0 pc=0x80cce11
In gdb I learn more:
(gdb) bt
#0 0xf77e358b in std::ios_base::Init::Init() () from /usr/lib/libstdc++.so.6
#1 0xf77c8fdc in ?? () from /usr/lib/libstdc++.so.6
#2 0xf7e97ba2 in do_init_fini (queue=<optimized out>) at ldso/dynlink.c:1606
#3 0xf7e878ea in dlopen (file=0x9922070 "libstdc++.so.6", mode=0) at ldso/dynlink.c:2219
#4 0x080e57db in syscallX () at /root/purego/sys_386.s:126
#5 0x00000000 in ?? ()
(gdb) disassemble
[...]
0xf77e357c <+124>: movq %xmm2,-0x58(%ebp)
0xf77e3581 <+129>: movd %eax,%xmm5
0xf77e3585 <+133>: mov 0x1bf8(%esi),%eax
=> 0xf77e358b <+139>: movaps %xmm5,-0x48(%ebp)
0xf77e358f <+143>: lea 0x20(%eax),%edx
0xf77e3592 <+146>: add $0xc,%eax
0xf77e3595 <+149>: movd %eax,%xmm1
[...]
(gdb) print $ebp
$1 = (void *) 0xffb5ede4
Thus, the address computed as -0x48(%ebp) is not 16-byte aligned, which is required by this instruction.
Anything else you feel useful to add?
Digging further, the issue appears to be that Go uses 4-byte stack alignment on 386, whereas libstdc++ at least on Alpine Linux requires 16-byte stack alignment.
It seems that this means changes are required to sys_386.s to align the stack to 16 bytes. It has to be aligned right before the CALL, but of course the alignment has to be done before pushing arguments.
Quite likely the same changes are needed for sys_amd64.s; even though I did not observe any crashes yet, x86-64 C ABI requires 16 bytes stack alignment but Go only ensures 8 bytes.
In both cases, cgo has code to perform the necessary translation by aligning the stack at the call boundary - purego needs something similar.
PureGo Version
1512f32
Operating System
Go Version (
go version)go version go1.26.3 linux/386
What steps will reproduce the problem?
examples/libc/main.goby:go run main goWhat is the expected result?
No output.
What happens instead?
In gdb I learn more:
Thus, the address computed as
-0x48(%ebp)is not 16-byte aligned, which is required by this instruction.Anything else you feel useful to add?
Digging further, the issue appears to be that Go uses 4-byte stack alignment on 386, whereas libstdc++ at least on Alpine Linux requires 16-byte stack alignment.
It seems that this means changes are required to
sys_386.sto align the stack to 16 bytes. It has to be aligned right before theCALL, but of course the alignment has to be done before pushing arguments.Quite likely the same changes are needed for
sys_amd64.s; even though I did not observe any crashes yet, x86-64 C ABI requires 16 bytes stack alignment but Go only ensures 8 bytes.In both cases, cgo has code to perform the necessary translation by aligning the stack at the call boundary - purego needs something similar.