fix callbacks on go 1.15 (#34)
Go 1.16 significantly changed the way Go calls into C code in golang/go#30c1887. Purego assumed versioned 1.16 method. This commit expands support back down to 1.15. Closes #27
- commit
- 3aa994fcbff60ba81dc1120e4fd38dd6d8546c24
- author
- TotallyGamerJet · · 2022-09-07 02:42 UTC
- committer
- GitHub · · 2022-09-07 02:42 UTC
- parent
- 46cb710
- signature
- GPG Unverified
4AEE18F83AFDEB23The key is unknown
5 files · +53 -24
M .github/workflows/test.yml +0 -1
| @@ -72,6 +72,5 @@ jobs: | |||
| 72 | 72 | go build -v . | |
| 73 | 73 | ||
| 74 | 74 | - name: go test | |
| 75 | - | if: ${{ !startsWith(matrix.go, '1.15.') }} # TODO: Remove this condition after #27 is fixed. | |
| 76 | 75 | run: | | |
| 77 | 76 | go test -tags=example ${{ !startsWith(matrix.go, '1.15.') && !startsWith(matrix.go, '1.16.') && '-shuffle=on' || '' }} -v -count=10 ./... | |
| @@ -72,6 +72,5 @@ jobs: | |||||
| 72 | go build -v . | 72 | go build -v . | ||
| 73 | 73 | ||||
| 74 | - name: go test | 74 | - name: go test | ||
| 75 | - | if: ${{ !startsWith(matrix.go, '1.15.') }} # TODO: Remove this condition after #27 is fixed. | |||
| 76 | run: | | 75 | run: | | ||
| 77 | go test -tags=example ${{ !startsWith(matrix.go, '1.15.') && !startsWith(matrix.go, '1.16.') && '-shuffle=on' || '' }} -v -count=10 ./... | 76 | go test -tags=example ${{ !startsWith(matrix.go, '1.15.') && !startsWith(matrix.go, '1.16.') && '-shuffle=on' || '' }} -v -count=10 ./... | ||
A callback_go1_15_darwin_amd64.go +16 -0
| @@ -0,0 +1,16 @@ | |||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebitengine Authors | |
| 3 | + | ||
| 4 | + | //go:build !go1.16 | |
| 5 | + | // +build !go1.16 | |
| 6 | + | ||
| 7 | + | package purego | |
| 8 | + | ||
| 9 | + | // callbackWrapPicker is here because Go 1.16+ needs it | |
| 10 | + | // to decide between stack and register arguments. | |
| 11 | + | // This only has a single pointer because Go 1.15 runtime.cgocallback | |
| 12 | + | // dereferences the argument once before passing it to this | |
| 13 | + | // function. | |
| 14 | + | func callbackWrapPicker(stack, _ *callbackArgs) { | |
| 15 | + | callbackWrap(stack) | |
| 16 | + | } | |
| @@ -0,0 +1,16 @@ | |||||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |||
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebitengine Authors | |||
| 3 | + | ||||
| 4 | + | //go:build !go1.16 | |||
| 5 | + | // +build !go1.16 | |||
| 6 | + | ||||
| 7 | + | package purego | |||
| 8 | + | ||||
| 9 | + | // callbackWrapPicker is here because Go 1.16+ needs it | |||
| 10 | + | // to decide between stack and register arguments. | |||
| 11 | + | // This only has a single pointer because Go 1.15 runtime.cgocallback | |||
| 12 | + | // dereferences the argument once before passing it to this | |||
| 13 | + | // function. | |||
| 14 | + | func callbackWrapPicker(stack, _ *callbackArgs) { | |||
| 15 | + | callbackWrap(stack) | |||
| 16 | + | } | |||
A callback_go1_16_darwin_amd64.go +24 -0
| @@ -0,0 +1,24 @@ | |||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebitengine Authors | |
| 3 | + | ||
| 4 | + | //go:build go1.16 | |
| 5 | + | // +build go1.16 | |
| 6 | + | ||
| 7 | + | package purego | |
| 8 | + | ||
| 9 | + | // callbackWrapPicker gets called with whatever is on the stack and in the first register. | |
| 10 | + | // Depending on which version of Go that uses stack or register-based | |
| 11 | + | // calling it passes the respective argument to the real calbackWrap function. | |
| 12 | + | // The other argument is therefore invalid and points to undefined memory so don't use it. | |
| 13 | + | // This function is necessary since we can't use the ABIInternal selector which is only | |
| 14 | + | // valid in the runtime. | |
| 15 | + | // | |
| 16 | + | // the double indirection is because Go 1.15 will do that in runtime.cgocallback | |
| 17 | + | // but in 1.16+ it doesn't so we do it here. | |
| 18 | + | func callbackWrapPicker(stack, register **callbackArgs) { | |
| 19 | + | if stackCallingConvention { | |
| 20 | + | callbackWrap(*stack) | |
| 21 | + | } else { | |
| 22 | + | callbackWrap(*register) | |
| 23 | + | } | |
| 24 | + | } | |
| @@ -0,0 +1,24 @@ | |||||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |||
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebitengine Authors | |||
| 3 | + | ||||
| 4 | + | //go:build go1.16 | |||
| 5 | + | // +build go1.16 | |||
| 6 | + | ||||
| 7 | + | package purego | |||
| 8 | + | ||||
| 9 | + | // callbackWrapPicker gets called with whatever is on the stack and in the first register. | |||
| 10 | + | // Depending on which version of Go that uses stack or register-based | |||
| 11 | + | // calling it passes the respective argument to the real calbackWrap function. | |||
| 12 | + | // The other argument is therefore invalid and points to undefined memory so don't use it. | |||
| 13 | + | // This function is necessary since we can't use the ABIInternal selector which is only | |||
| 14 | + | // valid in the runtime. | |||
| 15 | + | // | |||
| 16 | + | // the double indirection is because Go 1.15 will do that in runtime.cgocallback | |||
| 17 | + | // but in 1.16+ it doesn't so we do it here. | |||
| 18 | + | func callbackWrapPicker(stack, register **callbackArgs) { | |||
| 19 | + | if stackCallingConvention { | |||
| 20 | + | callbackWrap(*stack) | |||
| 21 | + | } else { | |||
| 22 | + | callbackWrap(*register) | |||
| 23 | + | } | |||
| 24 | + | } | |||
M sys_darwin_amd64.s +13 -9
| @@ -115,23 +115,27 @@ TEXT callbackasm1(SB), NOSPLIT, $0 | |||
| 115 | 115 | ||
| 116 | 116 | // Create a struct callbackArgs on our stack to be passed as | |
| 117 | 117 | // the "frame" to cgocallback and on to callbackWrap. | |
| 118 | - | // $24 to make enough room for the arguments to runtime.cgocallback | |
| 119 | - | SUBQ $(24+callbackArgs__size), SP | |
| 120 | - | MOVQ AX, (24+callbackArgs_index)(SP) // callback index | |
| 121 | - | MOVQ R8, (24+callbackArgs_args)(SP) // address of args vector | |
| 122 | - | MOVQ $0, (24+callbackArgs_result)(SP) // result | |
| 123 | - | LEAQ 24(SP), AX // take the address of callbackArgs | |
| 118 | + | // $32 to make enough room for the arguments to runtime.cgocallback | |
| 119 | + | // and on Go 1.15 to take a pointer to the pointer to callbackArgs | |
| 120 | + | SUBQ $(32+callbackArgs__size), SP | |
| 121 | + | MOVQ AX, (32+callbackArgs_index)(SP) // callback index | |
| 122 | + | MOVQ R8, (32+callbackArgs_args)(SP) // address of args vector | |
| 123 | + | MOVQ $0, (32+callbackArgs_result)(SP) // result | |
| 124 | + | LEAQ 32(SP), AX // take the address of callbackArgs | |
| 125 | + | ||
| 126 | + | MOVQ AX, 24(SP) // get a **callbackArgs for go 1.15 | |
| 127 | + | LEAQ 24(SP), AX // ^^^^ | |
| 124 | 128 | ||
| 125 | 129 | // Call cgocallback, which will call callbackWrap(frame). | |
| 126 | - | MOVQ $0, 16(SP) // context | |
| 130 | + | MOVQ $8, 16(SP) // context is 0 or argsize (including return value) for Go 1.15 | |
| 127 | 131 | MOVQ AX, 8(SP) // frame (address of callbackArgs) | |
| 128 | 132 | MOVQ $callbackWrapInternal<>(SB), BX | |
| 129 | 133 | MOVQ BX, 0(SP) // PC of function value to call (callbackWrap) | |
| 130 | 134 | CALL runtime·cgocallback(SB) // runtime.cgocallback(fn, frame, ctxt uintptr) | |
| 131 | 135 | ||
| 132 | 136 | // Get callback result. | |
| 133 | - | MOVQ (24+callbackArgs_result)(SP), AX | |
| 134 | - | ADDQ $(24+callbackArgs__size), SP // remove callbackArgs struct | |
| 137 | + | MOVQ (32+callbackArgs_result)(SP), AX | |
| 138 | + | ADDQ $(32+callbackArgs__size), SP // remove callbackArgs struct | |
| 135 | 139 | ||
| 136 | 140 | POP_REGS_HOST_TO_ABI0() | |
| 137 | 141 | ||
| @@ -115,23 +115,27 @@ TEXT callbackasm1(SB), NOSPLIT, $0 | |||||
| 115 | 115 | ||||
| 116 | // Create a struct callbackArgs on our stack to be passed as | 116 | // Create a struct callbackArgs on our stack to be passed as | ||
| 117 | // the "frame" to cgocallback and on to callbackWrap. | 117 | // the "frame" to cgocallback and on to callbackWrap. | ||
| 118 | - | // $24 to make enough room for the arguments to runtime.cgocallback | 118 | + | // $32 to make enough room for the arguments to runtime.cgocallback |
| 119 | - | SUBQ $(24+callbackArgs__size), SP | 119 | + | // and on Go 1.15 to take a pointer to the pointer to callbackArgs |
| 120 | - | MOVQ AX, (24+callbackArgs_index)(SP) // callback index | 120 | + | SUBQ $(32+callbackArgs__size), SP |
| 121 | - | MOVQ R8, (24+callbackArgs_args)(SP) // address of args vector | 121 | + | MOVQ AX, (32+callbackArgs_index)(SP) // callback index |
| 122 | - | MOVQ $0, (24+callbackArgs_result)(SP) // result | 122 | + | MOVQ R8, (32+callbackArgs_args)(SP) // address of args vector |
| 123 | - | LEAQ 24(SP), AX // take the address of callbackArgs | 123 | + | MOVQ $0, (32+callbackArgs_result)(SP) // result |
| 124 | + | LEAQ 32(SP), AX // take the address of callbackArgs | |||
| 125 | + | ||||
| 126 | + | MOVQ AX, 24(SP) // get a **callbackArgs for go 1.15 | |||
| 127 | + | LEAQ 24(SP), AX // ^^^^ | |||
| 124 | 128 | ||||
| 125 | // Call cgocallback, which will call callbackWrap(frame). | 129 | // Call cgocallback, which will call callbackWrap(frame). | ||
| 126 | - | MOVQ $0, 16(SP) // context | 130 | + | MOVQ $8, 16(SP) // context is 0 or argsize (including return value) for Go 1.15 |
| 127 | MOVQ AX, 8(SP) // frame (address of callbackArgs) | 131 | MOVQ AX, 8(SP) // frame (address of callbackArgs) | ||
| 128 | MOVQ $callbackWrapInternal<>(SB), BX | 132 | MOVQ $callbackWrapInternal<>(SB), BX | ||
| 129 | MOVQ BX, 0(SP) // PC of function value to call (callbackWrap) | 133 | MOVQ BX, 0(SP) // PC of function value to call (callbackWrap) | ||
| 130 | CALL runtime·cgocallback(SB) // runtime.cgocallback(fn, frame, ctxt uintptr) | 134 | CALL runtime·cgocallback(SB) // runtime.cgocallback(fn, frame, ctxt uintptr) | ||
| 131 | 135 | ||||
| 132 | // Get callback result. | 136 | // Get callback result. | ||
| 133 | - | MOVQ (24+callbackArgs_result)(SP), AX | 137 | + | MOVQ (32+callbackArgs_result)(SP), AX |
| 134 | - | ADDQ $(24+callbackArgs__size), SP // remove callbackArgs struct | 138 | + | ADDQ $(32+callbackArgs__size), SP // remove callbackArgs struct |
| 135 | 139 | ||||
| 136 | POP_REGS_HOST_TO_ABI0() | 140 | POP_REGS_HOST_TO_ABI0() | ||
| 137 | 141 | ||||
M syscall_darwin.go +0 -14
| @@ -95,20 +95,6 @@ const callbackMaxFrame = 64 * ptrSize | |||
| 95 | 95 | // callbackasmABI0 is implemented in zcallback_GOOS_GOARCH.s | |
| 96 | 96 | var callbackasmABI0 uintptr | |
| 97 | 97 | ||
| 98 | - | // callbackWrapPicker gets whatever is on the stack and in the first register. | |
| 99 | - | // Depending on which version of Go that uses stack or register-based | |
| 100 | - | // calling it passes the respective argument to the real calbackWrap function. | |
| 101 | - | // The other argument is therefore invalid and points to undefined memory so don't use it. | |
| 102 | - | // This function is necessary since we can't use the ABIInternal selector which is only | |
| 103 | - | // valid in the runtime. | |
| 104 | - | func callbackWrapPicker(stack, register *callbackArgs) { | |
| 105 | - | if stackCallingConvention { | |
| 106 | - | callbackWrap(stack) | |
| 107 | - | } else { | |
| 108 | - | callbackWrap(register) | |
| 109 | - | } | |
| 110 | - | } | |
| 111 | - | ||
| 112 | 98 | // callbackWrap is called by assembly code which determines which Go function to call. | |
| 113 | 99 | // This function takes the arguments and passes them to the Go function and returns the result. | |
| 114 | 100 | func callbackWrap(a *callbackArgs) { | |
| @@ -95,20 +95,6 @@ const callbackMaxFrame = 64 * ptrSize | |||||
| 95 | // callbackasmABI0 is implemented in zcallback_GOOS_GOARCH.s | 95 | // callbackasmABI0 is implemented in zcallback_GOOS_GOARCH.s | ||
| 96 | var callbackasmABI0 uintptr | 96 | var callbackasmABI0 uintptr | ||
| 97 | 97 | ||||
| 98 | - | // callbackWrapPicker gets whatever is on the stack and in the first register. | |||
| 99 | - | // Depending on which version of Go that uses stack or register-based | |||
| 100 | - | // calling it passes the respective argument to the real calbackWrap function. | |||
| 101 | - | // The other argument is therefore invalid and points to undefined memory so don't use it. | |||
| 102 | - | // This function is necessary since we can't use the ABIInternal selector which is only | |||
| 103 | - | // valid in the runtime. | |||
| 104 | - | func callbackWrapPicker(stack, register *callbackArgs) { | |||
| 105 | - | if stackCallingConvention { | |||
| 106 | - | callbackWrap(stack) | |||
| 107 | - | } else { | |||
| 108 | - | callbackWrap(register) | |||
| 109 | - | } | |||
| 110 | - | } | |||
| 111 | - | ||||
| 112 | // callbackWrap is called by assembly code which determines which Go function to call. | 98 | // callbackWrap is called by assembly code which determines which Go function to call. | ||
| 113 | // This function takes the arguments and passes them to the Go function and returns the result. | 99 | // This function takes the arguments and passes them to the Go function and returns the result. | ||
| 114 | func callbackWrap(a *callbackArgs) { | 100 | func callbackWrap(a *callbackArgs) { | ||