purego: support pure go C call on macOS (#1)
- commit
- d5a24be3d13fe5609c069f2b9c1dcf1580fae31f
- author
- TotallyGamerJet · · 2022-05-17 12:05 UTC
- committer
- GitHub · · 2022-05-17 12:05 UTC
- parent
- e8e2aab
- signature
- GPG Unverified
4AEE18F83AFDEB23The key is unknown
7 files · +290 -0
A dl_darwin.go +46 -0
| @@ -0,0 +1,46 @@ | |||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebiten Authors | |
| 3 | + | ||
| 4 | + | package purego | |
| 5 | + | ||
| 6 | + | import ( | |
| 7 | + | "runtime" | |
| 8 | + | "strings" | |
| 9 | + | "unsafe" | |
| 10 | + | ) | |
| 11 | + | ||
| 12 | + | const RTLD_DEFAULT = ^uintptr(1) | |
| 13 | + | ||
| 14 | + | func cString(name string) *byte { | |
| 15 | + | if strings.HasSuffix(name, "\x00") { | |
| 16 | + | return &[]byte(name)[0] | |
| 17 | + | } | |
| 18 | + | var b = make([]byte, len(name)+1) | |
| 19 | + | copy(b, name) | |
| 20 | + | return &b[0] | |
| 21 | + | } | |
| 22 | + | ||
| 23 | + | func Dlopen(name string, mode int) uintptr { | |
| 24 | + | bs := cString(name) | |
| 25 | + | ret, _, _ := SyscallN(dlopenABI0, uintptr(unsafe.Pointer(bs)), uintptr(mode), 0) | |
| 26 | + | runtime.KeepAlive(bs) | |
| 27 | + | return ret | |
| 28 | + | } | |
| 29 | + | ||
| 30 | + | func Dlsym(handle uintptr, name string) uintptr { | |
| 31 | + | bs := cString(name) | |
| 32 | + | ret, _, _ := SyscallN(dlsymABI0, handle, uintptr(unsafe.Pointer(bs)), 0) | |
| 33 | + | runtime.KeepAlive(bs) | |
| 34 | + | return ret | |
| 35 | + | } | |
| 36 | + | ||
| 37 | + | //go:cgo_import_dynamic _dlopen dlopen "/usr/lib/libSystem.B.dylib" | |
| 38 | + | //go:cgo_import_dynamic _dlsym dlsym "/usr/lib/libSystem.B.dylib" | |
| 39 | + | ||
| 40 | + | var dlopenABI0 uintptr | |
| 41 | + | ||
| 42 | + | func dlopen() // implemented in assembly | |
| 43 | + | ||
| 44 | + | var dlsymABI0 uintptr | |
| 45 | + | ||
| 46 | + | func dlsym() // implemented in assembly | |
| @@ -0,0 +1,46 @@ | |||||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |||
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebiten Authors | |||
| 3 | + | ||||
| 4 | + | package purego | |||
| 5 | + | ||||
| 6 | + | import ( | |||
| 7 | + | "runtime" | |||
| 8 | + | "strings" | |||
| 9 | + | "unsafe" | |||
| 10 | + | ) | |||
| 11 | + | ||||
| 12 | + | const RTLD_DEFAULT = ^uintptr(1) | |||
| 13 | + | ||||
| 14 | + | func cString(name string) *byte { | |||
| 15 | + | if strings.HasSuffix(name, "\x00") { | |||
| 16 | + | return &[]byte(name)[0] | |||
| 17 | + | } | |||
| 18 | + | var b = make([]byte, len(name)+1) | |||
| 19 | + | copy(b, name) | |||
| 20 | + | return &b[0] | |||
| 21 | + | } | |||
| 22 | + | ||||
| 23 | + | func Dlopen(name string, mode int) uintptr { | |||
| 24 | + | bs := cString(name) | |||
| 25 | + | ret, _, _ := SyscallN(dlopenABI0, uintptr(unsafe.Pointer(bs)), uintptr(mode), 0) | |||
| 26 | + | runtime.KeepAlive(bs) | |||
| 27 | + | return ret | |||
| 28 | + | } | |||
| 29 | + | ||||
| 30 | + | func Dlsym(handle uintptr, name string) uintptr { | |||
| 31 | + | bs := cString(name) | |||
| 32 | + | ret, _, _ := SyscallN(dlsymABI0, handle, uintptr(unsafe.Pointer(bs)), 0) | |||
| 33 | + | runtime.KeepAlive(bs) | |||
| 34 | + | return ret | |||
| 35 | + | } | |||
| 36 | + | ||||
| 37 | + | //go:cgo_import_dynamic _dlopen dlopen "/usr/lib/libSystem.B.dylib" | |||
| 38 | + | //go:cgo_import_dynamic _dlsym dlsym "/usr/lib/libSystem.B.dylib" | |||
| 39 | + | ||||
| 40 | + | var dlopenABI0 uintptr | |||
| 41 | + | ||||
| 42 | + | func dlopen() // implemented in assembly | |||
| 43 | + | ||||
| 44 | + | var dlsymABI0 uintptr | |||
| 45 | + | ||||
| 46 | + | func dlsym() // implemented in assembly | |||
A go.mod +3 -0
| @@ -0,0 +1,3 @@ | |||
| 1 | + | module github.com/ebiten/purego | |
| 2 | + | ||
| 3 | + | go 1.15 | |
| @@ -0,0 +1,3 @@ | |||||
| 1 | + | module github.com/ebiten/purego | |||
| 2 | + | ||||
| 3 | + | go 1.15 | |||
A runtime.go +18 -0
| @@ -0,0 +1,18 @@ | |||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebiten Authors | |
| 3 | + | ||
| 4 | + | //go:build darwin | |
| 5 | + | // +build darwin | |
| 6 | + | ||
| 7 | + | package purego | |
| 8 | + | ||
| 9 | + | import ( | |
| 10 | + | "unsafe" | |
| 11 | + | ) | |
| 12 | + | ||
| 13 | + | //go:linkname runtime_libcCall runtime.libcCall | |
| 14 | + | //go:linkname runtime_entersyscall runtime.entersyscall | |
| 15 | + | //go:linkname runtime_exitsyscall runtime.exitsyscall | |
| 16 | + | func runtime_libcCall(fn, arg unsafe.Pointer) int32 // from runtime/sys_libc.go | |
| 17 | + | func runtime_entersyscall() // from runtime/proc.go | |
| 18 | + | func runtime_exitsyscall() // from runtime/proc.go | |
| @@ -0,0 +1,18 @@ | |||||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |||
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebiten Authors | |||
| 3 | + | ||||
| 4 | + | //go:build darwin | |||
| 5 | + | // +build darwin | |||
| 6 | + | ||||
| 7 | + | package purego | |||
| 8 | + | ||||
| 9 | + | import ( | |||
| 10 | + | "unsafe" | |||
| 11 | + | ) | |||
| 12 | + | ||||
| 13 | + | //go:linkname runtime_libcCall runtime.libcCall | |||
| 14 | + | //go:linkname runtime_entersyscall runtime.entersyscall | |||
| 15 | + | //go:linkname runtime_exitsyscall runtime.exitsyscall | |||
| 16 | + | func runtime_libcCall(fn, arg unsafe.Pointer) int32 // from runtime/sys_libc.go | |||
| 17 | + | func runtime_entersyscall() // from runtime/proc.go | |||
| 18 | + | func runtime_exitsyscall() // from runtime/proc.go | |||
A stubs.s +22 -0
| @@ -0,0 +1,22 @@ | |||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebiten Authors | |
| 3 | + | ||
| 4 | + | //go:build darwin | |
| 5 | + | // +build darwin | |
| 6 | + | ||
| 7 | + | #include "textflag.h" | |
| 8 | + | ||
| 9 | + | // func dlopen(path *byte, mode int) (ret uintptr) | |
| 10 | + | GLOBL ·dlopenABI0(SB), NOPTR|RODATA, $8 | |
| 11 | + | DATA ·dlopenABI0(SB)/8, $·dlopen(SB) | |
| 12 | + | TEXT ·dlopen(SB), NOSPLIT, $0-0 | |
| 13 | + | JMP _dlopen(SB) | |
| 14 | + | RET | |
| 15 | + | ||
| 16 | + | // func dlsym(handle uintptr, symbol *byte) (ret uintptr) | |
| 17 | + | GLOBL ·dlsymABI0(SB), NOPTR|RODATA, $8 | |
| 18 | + | DATA ·dlsymABI0(SB)/8, $·dlsym(SB) | |
| 19 | + | TEXT ·dlsym(SB), NOSPLIT, $0-0 | |
| 20 | + | JMP _dlsym(SB) | |
| 21 | + | RET | |
| 22 | + | ||
| @@ -0,0 +1,22 @@ | |||||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |||
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebiten Authors | |||
| 3 | + | ||||
| 4 | + | //go:build darwin | |||
| 5 | + | // +build darwin | |||
| 6 | + | ||||
| 7 | + | #include "textflag.h" | |||
| 8 | + | ||||
| 9 | + | // func dlopen(path *byte, mode int) (ret uintptr) | |||
| 10 | + | GLOBL ·dlopenABI0(SB), NOPTR|RODATA, $8 | |||
| 11 | + | DATA ·dlopenABI0(SB)/8, $·dlopen(SB) | |||
| 12 | + | TEXT ·dlopen(SB), NOSPLIT, $0-0 | |||
| 13 | + | JMP _dlopen(SB) | |||
| 14 | + | RET | |||
| 15 | + | ||||
| 16 | + | // func dlsym(handle uintptr, symbol *byte) (ret uintptr) | |||
| 17 | + | GLOBL ·dlsymABI0(SB), NOPTR|RODATA, $8 | |||
| 18 | + | DATA ·dlsymABI0(SB)/8, $·dlsym(SB) | |||
| 19 | + | TEXT ·dlsym(SB), NOSPLIT, $0-0 | |||
| 20 | + | JMP _dlsym(SB) | |||
| 21 | + | RET | |||
| 22 | + | ||||
A sys_darwin_amd64.s +82 -0
| @@ -0,0 +1,82 @@ | |||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebiten Authors | |
| 3 | + | ||
| 4 | + | #include "textflag.h" | |
| 5 | + | ||
| 6 | + | // syscall9X calls a function in libc on behalf of the syscall package. | |
| 7 | + | // syscall9X takes a pointer to a struct like: | |
| 8 | + | // struct { | |
| 9 | + | // fn uintptr | |
| 10 | + | // a1 uintptr | |
| 11 | + | // a2 uintptr | |
| 12 | + | // a3 uintptr | |
| 13 | + | // a4 uintptr | |
| 14 | + | // a5 uintptr | |
| 15 | + | // a6 uintptr | |
| 16 | + | // a7 uintptr | |
| 17 | + | // a8 uintptr | |
| 18 | + | // a9 uintptr | |
| 19 | + | // r1 uintptr | |
| 20 | + | // r2 uintptr | |
| 21 | + | // err uintptr | |
| 22 | + | // } | |
| 23 | + | // syscall9X must be called on the g0 stack with the | |
| 24 | + | // C calling convention (use libcCall). | |
| 25 | + | GLOBL ·syscall9XABI0(SB), NOPTR|RODATA, $8 | |
| 26 | + | DATA ·syscall9XABI0(SB)/8, $·syscall9X(SB) | |
| 27 | + | TEXT ·syscall9X(SB), NOSPLIT, $0 | |
| 28 | + | PUSHQ BP | |
| 29 | + | MOVQ SP, BP | |
| 30 | + | SUBQ $32, SP | |
| 31 | + | MOVQ DI, -8(BP) // save the pointer | |
| 32 | + | MOVQ (0*8)(DI), R10 // fn | |
| 33 | + | MOVQ (2*8)(DI), SI // a2 | |
| 34 | + | MOVQ (3*8)(DI), DX // a3 | |
| 35 | + | MOVQ (4*8)(DI), CX // a4 | |
| 36 | + | MOVQ (5*8)(DI), R8 // a5 | |
| 37 | + | MOVQ (6*8)(DI), R9 // a6 | |
| 38 | + | MOVQ (7*8)(DI), R11 // a7 | |
| 39 | + | MOVQ (8*8)(DI), R12 // a8 | |
| 40 | + | MOVQ (9*8)(DI), R13 // a9 | |
| 41 | + | MOVQ (1*8)(DI), DI // a1 | |
| 42 | + | ||
| 43 | + | // these may be float arguments | |
| 44 | + | // so we put them also where C expects floats | |
| 45 | + | MOVQ DI, X0 // a1 | |
| 46 | + | MOVQ SI, X1 // a2 | |
| 47 | + | MOVQ DX, X2 // a3 | |
| 48 | + | MOVQ CX, X3 // a4 | |
| 49 | + | MOVQ R8, X4 // a5 | |
| 50 | + | MOVQ R9, X5 // a6 | |
| 51 | + | MOVQ R11, X6 // a7 | |
| 52 | + | MOVQ R12, X7 // a8 | |
| 53 | + | ||
| 54 | + | // push the remainding paramters onto the stack | |
| 55 | + | MOVQ R11, 0(SP) // push a7 | |
| 56 | + | MOVQ R12, 8(SP) // push a8 | |
| 57 | + | MOVQ R13, 16(SP) // push a9 | |
| 58 | + | XORL AX, AX // vararg: say "no float args" | |
| 59 | + | ||
| 60 | + | CALL R10 | |
| 61 | + | ||
| 62 | + | MOVQ -8(BP), DI // get the pointer back | |
| 63 | + | MOVQ AX, (10*8)(DI) // r1 | |
| 64 | + | MOVQ DX, (11*8)(DI) // r2 | |
| 65 | + | ||
| 66 | + | // Standard libc functions return -1 on error | |
| 67 | + | // and set errno. | |
| 68 | + | CMPQ AX, $-1 | |
| 69 | + | JNE ok | |
| 70 | + | ||
| 71 | + | // Get error code from libc. | |
| 72 | + | CALL libc_error(SB) | |
| 73 | + | MOVLQSX (AX), AX | |
| 74 | + | MOVQ (SP), DI | |
| 75 | + | MOVQ AX, (12*8)(DI) // err | |
| 76 | + | ||
| 77 | + | ok: | |
| 78 | + | XORL AX, AX // no error (it's ignored anyway) | |
| 79 | + | ADDQ $32, SP | |
| 80 | + | MOVQ BP, SP | |
| 81 | + | POPQ BP | |
| 82 | + | RET | |
| @@ -0,0 +1,82 @@ | |||||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |||
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebiten Authors | |||
| 3 | + | ||||
| 4 | + | #include "textflag.h" | |||
| 5 | + | ||||
| 6 | + | // syscall9X calls a function in libc on behalf of the syscall package. | |||
| 7 | + | // syscall9X takes a pointer to a struct like: | |||
| 8 | + | // struct { | |||
| 9 | + | // fn uintptr | |||
| 10 | + | // a1 uintptr | |||
| 11 | + | // a2 uintptr | |||
| 12 | + | // a3 uintptr | |||
| 13 | + | // a4 uintptr | |||
| 14 | + | // a5 uintptr | |||
| 15 | + | // a6 uintptr | |||
| 16 | + | // a7 uintptr | |||
| 17 | + | // a8 uintptr | |||
| 18 | + | // a9 uintptr | |||
| 19 | + | // r1 uintptr | |||
| 20 | + | // r2 uintptr | |||
| 21 | + | // err uintptr | |||
| 22 | + | // } | |||
| 23 | + | // syscall9X must be called on the g0 stack with the | |||
| 24 | + | // C calling convention (use libcCall). | |||
| 25 | + | GLOBL ·syscall9XABI0(SB), NOPTR|RODATA, $8 | |||
| 26 | + | DATA ·syscall9XABI0(SB)/8, $·syscall9X(SB) | |||
| 27 | + | TEXT ·syscall9X(SB), NOSPLIT, $0 | |||
| 28 | + | PUSHQ BP | |||
| 29 | + | MOVQ SP, BP | |||
| 30 | + | SUBQ $32, SP | |||
| 31 | + | MOVQ DI, -8(BP) // save the pointer | |||
| 32 | + | MOVQ (0*8)(DI), R10 // fn | |||
| 33 | + | MOVQ (2*8)(DI), SI // a2 | |||
| 34 | + | MOVQ (3*8)(DI), DX // a3 | |||
| 35 | + | MOVQ (4*8)(DI), CX // a4 | |||
| 36 | + | MOVQ (5*8)(DI), R8 // a5 | |||
| 37 | + | MOVQ (6*8)(DI), R9 // a6 | |||
| 38 | + | MOVQ (7*8)(DI), R11 // a7 | |||
| 39 | + | MOVQ (8*8)(DI), R12 // a8 | |||
| 40 | + | MOVQ (9*8)(DI), R13 // a9 | |||
| 41 | + | MOVQ (1*8)(DI), DI // a1 | |||
| 42 | + | ||||
| 43 | + | // these may be float arguments | |||
| 44 | + | // so we put them also where C expects floats | |||
| 45 | + | MOVQ DI, X0 // a1 | |||
| 46 | + | MOVQ SI, X1 // a2 | |||
| 47 | + | MOVQ DX, X2 // a3 | |||
| 48 | + | MOVQ CX, X3 // a4 | |||
| 49 | + | MOVQ R8, X4 // a5 | |||
| 50 | + | MOVQ R9, X5 // a6 | |||
| 51 | + | MOVQ R11, X6 // a7 | |||
| 52 | + | MOVQ R12, X7 // a8 | |||
| 53 | + | ||||
| 54 | + | // push the remainding paramters onto the stack | |||
| 55 | + | MOVQ R11, 0(SP) // push a7 | |||
| 56 | + | MOVQ R12, 8(SP) // push a8 | |||
| 57 | + | MOVQ R13, 16(SP) // push a9 | |||
| 58 | + | XORL AX, AX // vararg: say "no float args" | |||
| 59 | + | ||||
| 60 | + | CALL R10 | |||
| 61 | + | ||||
| 62 | + | MOVQ -8(BP), DI // get the pointer back | |||
| 63 | + | MOVQ AX, (10*8)(DI) // r1 | |||
| 64 | + | MOVQ DX, (11*8)(DI) // r2 | |||
| 65 | + | ||||
| 66 | + | // Standard libc functions return -1 on error | |||
| 67 | + | // and set errno. | |||
| 68 | + | CMPQ AX, $-1 | |||
| 69 | + | JNE ok | |||
| 70 | + | ||||
| 71 | + | // Get error code from libc. | |||
| 72 | + | CALL libc_error(SB) | |||
| 73 | + | MOVLQSX (AX), AX | |||
| 74 | + | MOVQ (SP), DI | |||
| 75 | + | MOVQ AX, (12*8)(DI) // err | |||
| 76 | + | ||||
| 77 | + | ok: | |||
| 78 | + | XORL AX, AX // no error (it's ignored anyway) | |||
| 79 | + | ADDQ $32, SP | |||
| 80 | + | MOVQ BP, SP | |||
| 81 | + | POPQ BP | |||
| 82 | + | RET | |||
A sys_darwin_arm64.s +71 -0
| @@ -0,0 +1,71 @@ | |||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebiten Authors | |
| 3 | + | ||
| 4 | + | #include "textflag.h" | |
| 5 | + | ||
| 6 | + | // syscall9X calls a function in libc on behalf of the syscall package. | |
| 7 | + | // syscall9X takes a pointer to a struct like: | |
| 8 | + | // struct { | |
| 9 | + | // fn uintptr | |
| 10 | + | // a1 uintptr | |
| 11 | + | // a2 uintptr | |
| 12 | + | // a3 uintptr | |
| 13 | + | // a4 uintptr | |
| 14 | + | // a5 uintptr | |
| 15 | + | // a6 uintptr | |
| 16 | + | // a7 uintptr | |
| 17 | + | // a8 uintptr | |
| 18 | + | // a9 uintptr | |
| 19 | + | // r1 uintptr | |
| 20 | + | // r2 uintptr | |
| 21 | + | // err uintptr | |
| 22 | + | // } | |
| 23 | + | // syscall9X must be called on the g0 stack with the | |
| 24 | + | // C calling convention (use libcCall). | |
| 25 | + | GLOBL ·syscall9XABI0(SB), NOPTR|RODATA, $8 | |
| 26 | + | DATA ·syscall9XABI0(SB)/8, $·syscall9X(SB) | |
| 27 | + | TEXT ·syscall9X(SB), NOSPLIT, $0 | |
| 28 | + | SUB $16, RSP // push structure pointer | |
| 29 | + | MOVD R0, 8(RSP) | |
| 30 | + | ||
| 31 | + | MOVD 0(R0), R12 // fn | |
| 32 | + | MOVD 16(R0), R1 // a2 | |
| 33 | + | MOVD 24(R0), R2 // a3 | |
| 34 | + | MOVD 32(R0), R3 // a4 | |
| 35 | + | MOVD 40(R0), R4 // a5 | |
| 36 | + | MOVD 48(R0), R5 // a6 | |
| 37 | + | MOVD 56(R0), R6 // a7 | |
| 38 | + | MOVD 64(R0), R7 // a8 | |
| 39 | + | MOVD 72(R0), R8 // a9 | |
| 40 | + | ||
| 41 | + | // these may be float arguments | |
| 42 | + | // so we put them also where C expects floats | |
| 43 | + | FMOVD R0, F0 // a1 | |
| 44 | + | FMOVD R1, F1 // a2 | |
| 45 | + | FMOVD R2, F2 // a3 | |
| 46 | + | FMOVD R3, F3 // a4 | |
| 47 | + | FMOVD R4, F4 // a5 | |
| 48 | + | FMOVD R5, F5 // a6 | |
| 49 | + | FMOVD R6, F6 // a4 | |
| 50 | + | FMOVD R7, F7 // a5 | |
| 51 | + | ||
| 52 | + | MOVD R8, (RSP) // push a9 onto stack | |
| 53 | + | MOVD 8(R0), R0 // a1 | |
| 54 | + | BL (R12) | |
| 55 | + | ||
| 56 | + | MOVD 8(RSP), R2 // pop structure pointer | |
| 57 | + | ADD $16, RSP | |
| 58 | + | MOVD R0, 80(R2) // save r1 | |
| 59 | + | MOVD R1, 88(R2) // save r2 | |
| 60 | + | CMP $-1, R0 | |
| 61 | + | BNE ok | |
| 62 | + | SUB $16, RSP // push structure pointer | |
| 63 | + | MOVD R2, (RSP) | |
| 64 | + | BL libc_error(SB) | |
| 65 | + | MOVW (R0), R0 | |
| 66 | + | MOVD (RSP), R2 // pop structure pointer | |
| 67 | + | ADD $16, RSP | |
| 68 | + | MOVD R0, 96(R2) // save err | |
| 69 | + | ||
| 70 | + | ok: | |
| 71 | + | RET | |
| @@ -0,0 +1,71 @@ | |||||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |||
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebiten Authors | |||
| 3 | + | ||||
| 4 | + | #include "textflag.h" | |||
| 5 | + | ||||
| 6 | + | // syscall9X calls a function in libc on behalf of the syscall package. | |||
| 7 | + | // syscall9X takes a pointer to a struct like: | |||
| 8 | + | // struct { | |||
| 9 | + | // fn uintptr | |||
| 10 | + | // a1 uintptr | |||
| 11 | + | // a2 uintptr | |||
| 12 | + | // a3 uintptr | |||
| 13 | + | // a4 uintptr | |||
| 14 | + | // a5 uintptr | |||
| 15 | + | // a6 uintptr | |||
| 16 | + | // a7 uintptr | |||
| 17 | + | // a8 uintptr | |||
| 18 | + | // a9 uintptr | |||
| 19 | + | // r1 uintptr | |||
| 20 | + | // r2 uintptr | |||
| 21 | + | // err uintptr | |||
| 22 | + | // } | |||
| 23 | + | // syscall9X must be called on the g0 stack with the | |||
| 24 | + | // C calling convention (use libcCall). | |||
| 25 | + | GLOBL ·syscall9XABI0(SB), NOPTR|RODATA, $8 | |||
| 26 | + | DATA ·syscall9XABI0(SB)/8, $·syscall9X(SB) | |||
| 27 | + | TEXT ·syscall9X(SB), NOSPLIT, $0 | |||
| 28 | + | SUB $16, RSP // push structure pointer | |||
| 29 | + | MOVD R0, 8(RSP) | |||
| 30 | + | ||||
| 31 | + | MOVD 0(R0), R12 // fn | |||
| 32 | + | MOVD 16(R0), R1 // a2 | |||
| 33 | + | MOVD 24(R0), R2 // a3 | |||
| 34 | + | MOVD 32(R0), R3 // a4 | |||
| 35 | + | MOVD 40(R0), R4 // a5 | |||
| 36 | + | MOVD 48(R0), R5 // a6 | |||
| 37 | + | MOVD 56(R0), R6 // a7 | |||
| 38 | + | MOVD 64(R0), R7 // a8 | |||
| 39 | + | MOVD 72(R0), R8 // a9 | |||
| 40 | + | ||||
| 41 | + | // these may be float arguments | |||
| 42 | + | // so we put them also where C expects floats | |||
| 43 | + | FMOVD R0, F0 // a1 | |||
| 44 | + | FMOVD R1, F1 // a2 | |||
| 45 | + | FMOVD R2, F2 // a3 | |||
| 46 | + | FMOVD R3, F3 // a4 | |||
| 47 | + | FMOVD R4, F4 // a5 | |||
| 48 | + | FMOVD R5, F5 // a6 | |||
| 49 | + | FMOVD R6, F6 // a4 | |||
| 50 | + | FMOVD R7, F7 // a5 | |||
| 51 | + | ||||
| 52 | + | MOVD R8, (RSP) // push a9 onto stack | |||
| 53 | + | MOVD 8(R0), R0 // a1 | |||
| 54 | + | BL (R12) | |||
| 55 | + | ||||
| 56 | + | MOVD 8(RSP), R2 // pop structure pointer | |||
| 57 | + | ADD $16, RSP | |||
| 58 | + | MOVD R0, 80(R2) // save r1 | |||
| 59 | + | MOVD R1, 88(R2) // save r2 | |||
| 60 | + | CMP $-1, R0 | |||
| 61 | + | BNE ok | |||
| 62 | + | SUB $16, RSP // push structure pointer | |||
| 63 | + | MOVD R2, (RSP) | |||
| 64 | + | BL libc_error(SB) | |||
| 65 | + | MOVW (R0), R0 | |||
| 66 | + | MOVD (RSP), R2 // pop structure pointer | |||
| 67 | + | ADD $16, RSP | |||
| 68 | + | MOVD R0, 96(R2) // save err | |||
| 69 | + | ||||
| 70 | + | ok: | |||
| 71 | + | RET | |||
A syscall.go +48 -0
| @@ -0,0 +1,48 @@ | |||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebiten Authors | |
| 3 | + | ||
| 4 | + | //go:build darwin | |
| 5 | + | // +build darwin | |
| 6 | + | ||
| 7 | + | package purego | |
| 8 | + | ||
| 9 | + | import "unsafe" | |
| 10 | + | ||
| 11 | + | const maxArgs = 9 | |
| 12 | + | ||
| 13 | + | // SyscallN takes fn, a C function pointer and a list of arguments as uintptr. | |
| 14 | + | // There is an internal maximum number of arguments that SyscallN can take. It panics | |
| 15 | + | // when the maximum is exceeded. It returns the result and the libc error code if there is one. | |
| 16 | + | // | |
| 17 | + | // NOTE: SyscallN does not properly call functions that have both integer and float parameters. | |
| 18 | + | // See discussion comment https://github.com/ebiten/purego/pull/1#issuecomment-1128057607 | |
| 19 | + | // for an explanation of why that is. | |
| 20 | + | func SyscallN(fn uintptr, args ...uintptr) (r1, r2, err uintptr) { | |
| 21 | + | if len(args) > maxArgs { | |
| 22 | + | panic("too many arguments to SyscallN") | |
| 23 | + | } | |
| 24 | + | if len(args) < maxArgs { | |
| 25 | + | // add padding so there is no out-of-bounds slicing | |
| 26 | + | var tmp = make([]uintptr, maxArgs) | |
| 27 | + | copy(tmp, args) | |
| 28 | + | args = tmp | |
| 29 | + | } | |
| 30 | + | return syscall_syscall9X(fn, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]) | |
| 31 | + | } | |
| 32 | + | ||
| 33 | + | func callc(fn uintptr, args unsafe.Pointer) { | |
| 34 | + | runtime_entersyscall() | |
| 35 | + | runtime_libcCall(unsafe.Pointer(fn), args) | |
| 36 | + | runtime_exitsyscall() | |
| 37 | + | } | |
| 38 | + | ||
| 39 | + | var syscall9XABI0 uintptr | |
| 40 | + | ||
| 41 | + | func syscall9X() // implemented in assembly | |
| 42 | + | ||
| 43 | + | func syscall_syscall9X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr) { | |
| 44 | + | args := struct{ fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, r1, r2, err uintptr }{ | |
| 45 | + | fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, r1, r2, err} | |
| 46 | + | callc(syscall9XABI0, unsafe.Pointer(&args)) | |
| 47 | + | return args.r1, args.r2, args.err | |
| 48 | + | } | |
| @@ -0,0 +1,48 @@ | |||||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |||
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebiten Authors | |||
| 3 | + | ||||
| 4 | + | //go:build darwin | |||
| 5 | + | // +build darwin | |||
| 6 | + | ||||
| 7 | + | package purego | |||
| 8 | + | ||||
| 9 | + | import "unsafe" | |||
| 10 | + | ||||
| 11 | + | const maxArgs = 9 | |||
| 12 | + | ||||
| 13 | + | // SyscallN takes fn, a C function pointer and a list of arguments as uintptr. | |||
| 14 | + | // There is an internal maximum number of arguments that SyscallN can take. It panics | |||
| 15 | + | // when the maximum is exceeded. It returns the result and the libc error code if there is one. | |||
| 16 | + | // | |||
| 17 | + | // NOTE: SyscallN does not properly call functions that have both integer and float parameters. | |||
| 18 | + | // See discussion comment https://github.com/ebiten/purego/pull/1#issuecomment-1128057607 | |||
| 19 | + | // for an explanation of why that is. | |||
| 20 | + | func SyscallN(fn uintptr, args ...uintptr) (r1, r2, err uintptr) { | |||
| 21 | + | if len(args) > maxArgs { | |||
| 22 | + | panic("too many arguments to SyscallN") | |||
| 23 | + | } | |||
| 24 | + | if len(args) < maxArgs { | |||
| 25 | + | // add padding so there is no out-of-bounds slicing | |||
| 26 | + | var tmp = make([]uintptr, maxArgs) | |||
| 27 | + | copy(tmp, args) | |||
| 28 | + | args = tmp | |||
| 29 | + | } | |||
| 30 | + | return syscall_syscall9X(fn, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]) | |||
| 31 | + | } | |||
| 32 | + | ||||
| 33 | + | func callc(fn uintptr, args unsafe.Pointer) { | |||
| 34 | + | runtime_entersyscall() | |||
| 35 | + | runtime_libcCall(unsafe.Pointer(fn), args) | |||
| 36 | + | runtime_exitsyscall() | |||
| 37 | + | } | |||
| 38 | + | ||||
| 39 | + | var syscall9XABI0 uintptr | |||
| 40 | + | ||||
| 41 | + | func syscall9X() // implemented in assembly | |||
| 42 | + | ||||
| 43 | + | func syscall_syscall9X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr) { | |||
| 44 | + | args := struct{ fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, r1, r2, err uintptr }{ | |||
| 45 | + | fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, r1, r2, err} | |||
| 46 | + | callc(syscall9XABI0, unsafe.Pointer(&args)) | |||
| 47 | + | return args.r1, args.r2, args.err | |||
| 48 | + | } | |||