purego: add //go:uintptrescapes to SyscallN (#7)
this forces the uintptr arguments to escape in SyscallN. When optimizations were enabled Go assumed that the pointer arguments in SyscallN were no longer needed. This is why functions like gl.GenFramebuffersEXT would fail. Although this change forces all of the arguments to be placed on the heap it ensures that no optimizations will remove our precious arguments. This also allows us to only have one implementation inside SyscallN (i.e. syscall_syscall9X). A potential replacement would be //go:cgo_unsafe_args but that can only be used in CGO generated files. There is also //go:uintptrkeepalive although that was not enough to stop the function from failing. That is why I went with //go:uintptrescapes I tested this change on darwin (arm64 & amd64) as well as with -gcflags=all=-N and without. All tests pass on these platforms. I have not tested this on windows. If you could that would be much appreciated.
- commit
- bda3acfed6e93f77ef1418d21a2bc2012a58fbfd
- author
- TotallyGamerJet · · 2022-06-19 03:41 UTC
- committer
- GitHub · · 2022-06-19 03:41 UTC
- parent
- cce0ae8
- signature
- GPG Unverified
4AEE18F83AFDEB23The key is unknown
4 files · +2 -40
M runtime.go +0 -3
| @@ -10,9 +10,6 @@ import ( | |||
| 10 | 10 | "unsafe" | |
| 11 | 11 | ) | |
| 12 | 12 | ||
| 13 | - | //go:linkname syscall_syscall6X syscall.syscall6X | |
| 14 | - | func syscall_syscall6X(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) // from runtime/sys_darwin_64.s | |
| 15 | - | ||
| 16 | 13 | //go:linkname runtime_libcCall runtime.libcCall | |
| 17 | 14 | //go:linkname runtime_entersyscall runtime.entersyscall | |
| 18 | 15 | //go:linkname runtime_exitsyscall runtime.exitsyscall | |
| @@ -10,9 +10,6 @@ import ( | |||||
| 10 | "unsafe" | 10 | "unsafe" | ||
| 11 | ) | 11 | ) | ||
| 12 | 12 | ||||
| 13 | - | //go:linkname syscall_syscall6X syscall.syscall6X | |||
| 14 | - | func syscall_syscall6X(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) // from runtime/sys_darwin_64.s | |||
| 15 | - | ||||
| 16 | //go:linkname runtime_libcCall runtime.libcCall | 13 | //go:linkname runtime_libcCall runtime.libcCall | ||
| 17 | //go:linkname runtime_entersyscall runtime.entersyscall | 14 | //go:linkname runtime_entersyscall runtime.entersyscall | ||
| 18 | //go:linkname runtime_exitsyscall runtime.exitsyscall | 15 | //go:linkname runtime_exitsyscall runtime.exitsyscall | ||
M syscall.go +2 -15
| @@ -8,20 +8,6 @@ package purego | |||
| 8 | 8 | ||
| 9 | 9 | const maxArgs = 9 | |
| 10 | 10 | ||
| 11 | - | // f matches argument numbers to a Syscall implementation that can take that many | |
| 12 | - | var f = map[int]func(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr){ | |
| 13 | - | 0: syscall_syscallX3, | |
| 14 | - | 1: syscall_syscallX3, | |
| 15 | - | 2: syscall_syscallX3, | |
| 16 | - | 3: syscall_syscallX3, | |
| 17 | - | 4: syscall_syscallX6, | |
| 18 | - | 5: syscall_syscallX6, | |
| 19 | - | 6: syscall_syscallX6, | |
| 20 | - | 7: syscall_syscall9X, | |
| 21 | - | 8: syscall_syscall9X, | |
| 22 | - | 9: syscall_syscall9X, | |
| 23 | - | } | |
| 24 | - | ||
| 25 | 11 | // SyscallN takes fn, a C function pointer and a list of arguments as uintptr. | |
| 26 | 12 | // There is an internal maximum number of arguments that SyscallN can take. It panics | |
| 27 | 13 | // when the maximum is exceeded. It returns the result and the libc error code if there is one. | |
| @@ -33,6 +19,7 @@ var f = map[int]func(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, er | |||
| 33 | 19 | // On amd64, if there are more than 8 floats the 9th and so on will be placed incorrectly on the | |
| 34 | 20 | // stack. | |
| 35 | 21 | //go:nosplit | |
| 22 | + | //go:uintptrescapes | |
| 36 | 23 | func SyscallN(fn uintptr, args ...uintptr) (r1, r2, err uintptr) { | |
| 37 | 24 | if len(args) > maxArgs { | |
| 38 | 25 | panic("too many arguments to SyscallN") | |
| @@ -40,5 +27,5 @@ func SyscallN(fn uintptr, args ...uintptr) (r1, r2, err uintptr) { | |||
| 40 | 27 | // add padding so there is no out-of-bounds slicing | |
| 41 | 28 | var tmp [maxArgs]uintptr | |
| 42 | 29 | copy(tmp[:], args) | |
| 43 | - | return f[len(args)](fn, tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6], tmp[7], tmp[8]) | |
| 30 | + | return syscall_syscall9X(fn, tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6], tmp[7], tmp[8]) | |
| 44 | 31 | } | |
| @@ -8,20 +8,6 @@ package purego | |||||
| 8 | 8 | ||||
| 9 | const maxArgs = 9 | 9 | const maxArgs = 9 | ||
| 10 | 10 | ||||
| 11 | - | // f matches argument numbers to a Syscall implementation that can take that many | |||
| 12 | - | var f = map[int]func(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr){ | |||
| 13 | - | 0: syscall_syscallX3, | |||
| 14 | - | 1: syscall_syscallX3, | |||
| 15 | - | 2: syscall_syscallX3, | |||
| 16 | - | 3: syscall_syscallX3, | |||
| 17 | - | 4: syscall_syscallX6, | |||
| 18 | - | 5: syscall_syscallX6, | |||
| 19 | - | 6: syscall_syscallX6, | |||
| 20 | - | 7: syscall_syscall9X, | |||
| 21 | - | 8: syscall_syscall9X, | |||
| 22 | - | 9: syscall_syscall9X, | |||
| 23 | - | } | |||
| 24 | - | ||||
| 25 | // SyscallN takes fn, a C function pointer and a list of arguments as uintptr. | 11 | // SyscallN takes fn, a C function pointer and a list of arguments as uintptr. | ||
| 26 | // There is an internal maximum number of arguments that SyscallN can take. It panics | 12 | // There is an internal maximum number of arguments that SyscallN can take. It panics | ||
| 27 | // when the maximum is exceeded. It returns the result and the libc error code if there is one. | 13 | // when the maximum is exceeded. It returns the result and the libc error code if there is one. | ||
| @@ -33,6 +19,7 @@ var f = map[int]func(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, er | |||||
| 33 | // On amd64, if there are more than 8 floats the 9th and so on will be placed incorrectly on the | 19 | // On amd64, if there are more than 8 floats the 9th and so on will be placed incorrectly on the | ||
| 34 | // stack. | 20 | // stack. | ||
| 35 | //go:nosplit | 21 | //go:nosplit | ||
| 22 | + | //go:uintptrescapes | |||
| 36 | func SyscallN(fn uintptr, args ...uintptr) (r1, r2, err uintptr) { | 23 | func SyscallN(fn uintptr, args ...uintptr) (r1, r2, err uintptr) { | ||
| 37 | if len(args) > maxArgs { | 24 | if len(args) > maxArgs { | ||
| 38 | panic("too many arguments to SyscallN") | 25 | panic("too many arguments to SyscallN") | ||
| @@ -40,5 +27,5 @@ func SyscallN(fn uintptr, args ...uintptr) (r1, r2, err uintptr) { | |||||
| 40 | // add padding so there is no out-of-bounds slicing | 27 | // add padding so there is no out-of-bounds slicing | ||
| 41 | var tmp [maxArgs]uintptr | 28 | var tmp [maxArgs]uintptr | ||
| 42 | copy(tmp[:], args) | 29 | copy(tmp[:], args) | ||
| 43 | - | return f[len(args)](fn, tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6], tmp[7], tmp[8]) | 30 | + | return syscall_syscall9X(fn, tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6], tmp[7], tmp[8]) |
| 44 | } | 31 | } | ||
M syscall_darwin.go +0 -10
| @@ -8,16 +8,6 @@ func callc(fn uintptr, args unsafe.Pointer) { | |||
| 8 | 8 | runtime_exitsyscall() | |
| 9 | 9 | } | |
| 10 | 10 | ||
| 11 | - | //go:nosplit | |
| 12 | - | func syscall_syscallX3(fn, a1, a2, a3, _, _, _, _, _, _ uintptr) (r1, r2, err uintptr) { | |
| 13 | - | return syscall_syscall6X(fn, a1, a2, a3, 0, 0, 0) | |
| 14 | - | } | |
| 15 | - | ||
| 16 | - | //go:nosplit | |
| 17 | - | func syscall_syscallX6(fn, a1, a2, a3, a4, a5, a6, _, _, _ uintptr) (r1, r2, err uintptr) { | |
| 18 | - | return syscall_syscall6X(fn, a1, a2, a3, a4, a5, a6) | |
| 19 | - | } | |
| 20 | - | ||
| 21 | 11 | var syscall9XABI0 uintptr | |
| 22 | 12 | ||
| 23 | 13 | func syscall9X() // implemented in assembly | |
| @@ -8,16 +8,6 @@ func callc(fn uintptr, args unsafe.Pointer) { | |||||
| 8 | runtime_exitsyscall() | 8 | runtime_exitsyscall() | ||
| 9 | } | 9 | } | ||
| 10 | 10 | ||||
| 11 | - | //go:nosplit | |||
| 12 | - | func syscall_syscallX3(fn, a1, a2, a3, _, _, _, _, _, _ uintptr) (r1, r2, err uintptr) { | |||
| 13 | - | return syscall_syscall6X(fn, a1, a2, a3, 0, 0, 0) | |||
| 14 | - | } | |||
| 15 | - | ||||
| 16 | - | //go:nosplit | |||
| 17 | - | func syscall_syscallX6(fn, a1, a2, a3, a4, a5, a6, _, _, _ uintptr) (r1, r2, err uintptr) { | |||
| 18 | - | return syscall_syscall6X(fn, a1, a2, a3, a4, a5, a6) | |||
| 19 | - | } | |||
| 20 | - | ||||
| 21 | var syscall9XABI0 uintptr | 11 | var syscall9XABI0 uintptr | ||
| 22 | 12 | ||||
| 23 | func syscall9X() // implemented in assembly | 13 | func syscall9X() // implemented in assembly | ||
M syscall_windows.go +0 -12
| @@ -4,18 +4,6 @@ import ( | |||
| 4 | 4 | "syscall" | |
| 5 | 5 | ) | |
| 6 | 6 | ||
| 7 | - | //go:nosplit | |
| 8 | - | func syscall_syscallX3(fn, a1, a2, a3, _, _, _, _, _, _ uintptr) (r1, r2, err uintptr) { | |
| 9 | - | r1, r2, errno := syscall.Syscall(fn, 3, a1, a2, a3) | |
| 10 | - | return r1, r2, uintptr(errno) | |
| 11 | - | } | |
| 12 | - | ||
| 13 | - | //go:nosplit | |
| 14 | - | func syscall_syscallX6(fn, a1, a2, a3, a4, a5, a6, _, _, _ uintptr) (r1, r2, err uintptr) { | |
| 15 | - | r1, r2, errno := syscall.Syscall6(fn, 6, a1, a2, a3, a4, a5, a6) | |
| 16 | - | return r1, r2, uintptr(errno) | |
| 17 | - | } | |
| 18 | - | ||
| 19 | 7 | //go:nosplit | |
| 20 | 8 | func syscall_syscall9X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr) { | |
| 21 | 9 | r1, r2, errno := syscall.Syscall9(fn, 9, a1, a2, a3, a4, a5, a6, a7, a8, a9) | |
| @@ -4,18 +4,6 @@ import ( | |||||
| 4 | "syscall" | 4 | "syscall" | ||
| 5 | ) | 5 | ) | ||
| 6 | 6 | ||||
| 7 | - | //go:nosplit | |||
| 8 | - | func syscall_syscallX3(fn, a1, a2, a3, _, _, _, _, _, _ uintptr) (r1, r2, err uintptr) { | |||
| 9 | - | r1, r2, errno := syscall.Syscall(fn, 3, a1, a2, a3) | |||
| 10 | - | return r1, r2, uintptr(errno) | |||
| 11 | - | } | |||
| 12 | - | ||||
| 13 | - | //go:nosplit | |||
| 14 | - | func syscall_syscallX6(fn, a1, a2, a3, a4, a5, a6, _, _, _ uintptr) (r1, r2, err uintptr) { | |||
| 15 | - | r1, r2, errno := syscall.Syscall6(fn, 6, a1, a2, a3, a4, a5, a6) | |||
| 16 | - | return r1, r2, uintptr(errno) | |||
| 17 | - | } | |||
| 18 | - | ||||
| 19 | //go:nosplit | 7 | //go:nosplit | ||
| 20 | func syscall_syscall9X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr) { | 8 | func syscall_syscall9X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr) { | ||
| 21 | r1, r2, errno := syscall.Syscall9(fn, 9, a1, a2, a3, a4, a5, a6, a7, a8, a9) | 9 | r1, r2, errno := syscall.Syscall9(fn, 9, a1, a2, a3, a4, a5, a6, a7, a8, a9) | ||