Repository navigation
Description
PureGo Version
Operating System
- Windows
- macOS
- Linux
- FreeBSD
- NetBSD
- Android
- iOS
Go Version (go version)
go1.26.7 linux/amd64
What steps will reproduce the problem?
SyscallN's third return value is documented as "the libc error code", but on every OS except darwin it just echoes the input a3 argument. The errno write-back in sys_*.s is guarded by #ifdef GOOS_darwin:
// sys_amd64.s:125-129
#ifdef GOOS_darwin
CALL purego_error(SB)
MOVQ PTR_ADDRESS(SP), DI // reload (DI clobbered by call)
MOVQ (AX), AX
MOVQ AX, syscallArgs_a3(DI) // save errno
#endifwhile syscall.go:89 unconditionally returns it:
s := syscall_SyscallN(fn, tmp[:], floats[:], 0)
defer thePool.Put(s)
return s.a1, s.a2, s.a3 // s.a3 was never written on Linux/FreeBSD/NetBSDRepro (Linux):
libc, _ := purego.Dlopen("libc.so.6", purego.RTLD_NOW|purego.RTLD_GLOBAL)
sym, _ := purego.Dlsym(libc, "open")
path := append([]byte("/nonexistent_purego_xyz"), 0)
r1, _, errno := purego.SyscallN(sym, uintptr(unsafe.Pointer(&path[0])), 0)
runtime.KeepAlive(path)
fmt.Println(r1, errno) // r1 = -1 (0xffff...), errno = 0; want errno = ENOENT (2)Actual output on Linux: open: r1=-1 errno=0. The 0 is just the input a3 (flags=0) echoed back, not ENOENT.
The same #ifdef GOOS_darwin pattern exists in sys_arm64.s:126-129 (and the other arch trampolines), so Linux/arm64, FreeBSD, and NetBSD are all affected.
What is the expected result?
On Linux/FreeBSD/NetBSD the third return value should be the libc error code (ENOENT == 2 in the repro above), matching the documented behavior of SyscallN and the darwin behavior.
What happens instead?
The caller observes whatever was passed as the third argument (a3). Any errno != 0 check against a successful call, or any error-code inspection after a failed call, reads garbage on non-darwin platforms.
Anything else you feel useful to add?
Minimized reproducible case: the program above (needs only libc.so.6, no C toolchain). Verified failing on main @ 63002e0, Linux/amd64, Go 1.26.7.
Suggested fix: on non-darwin either return 0 explicitly and document that SyscallN only reports errno on darwin, or fetch the error via __errno_location / ___errno per-OS like the darwin path does with purego_error. At minimum the doc comment on SyscallN ("It returns the result and the libc error code if there is one") must stop promising errno on platforms that never set it.
Activity
hajimehoshi commentedon Sep 5, 2026
I feel like a fix is worth back-porting. Thoughts? @TotallyGamerJet
purego: stop echoing input a3 as errno on non-darwin
TotallyGamerJet commentedon Sep 17, 2026
I agree I think it should be backported
purego: clear uncaptured errno in syscall trampolines (#510)
purego: clear uncaptured errno in syscall trampolines (ebitengine#510)
purego: clear uncaptured errno in syscall trampolines