internal/fakecgo: support on macOS arm64 and amd64 (#20)
This commit reimplements the Cgo runtime found at runtime/cgo entirely in Go. It does this by using //go:linkname to replace the symbols in the Go runtime with assembly trampolines into Go functions. The symbols that are replaced are: iscgo bool // runtime/cgo.go _cgo_init unsafe.Pointer _cgo_thread_start unsafe.Pointer _cgo_notify_runtime_init_done unsafe.Pointer _cgo_setenv unsafe.Pointer // these two are in runtime/env_posix.go _cgo_unsetenv unsafe.Pointer This commit also changes which trampoline is used to move from Go to C. It changes from runtime.libcCall to runtime.cgocall which is the function used when calling C functions with import "C". Doing this also brings purego more in line with Cgo. This will allow us to more easily add platforms and remove C from Go programs. This commit adds support for fakecgo on macOS (arm64 and amd64). In the future, support for linux and iOS can be added with this commit as the basis for adding platforms. The platform specific parts include symbols_GOOS.go and go_GOOS_GOARCH.go. Implementing those for other GOOSs and GOARCHs should be trivial. The current limitations is that fakecgo does no work on iOS nor any other GOOS but macOS. It can also not be used with -buildmode=c-archive as that requires extra initialization that isn't done in this commit. Updates #12 Co-authored-by: Hajime Hoshi <hajimehoshi@gmail.com>
- commit
- c1f3f967508f30ee1511b806afef1ee87f3aa5e9
- author
- TotallyGamerJet · · 2022-08-22 02:26 UTC
- committer
- GitHub · · 2022-08-22 02:26 UTC
- parent
- ffec451
- signature
- GPG Unverified
4AEE18F83AFDEB23The key is unknown
21 files · +786 -24
M README.md +4 -1
| @@ -10,4 +10,7 @@ License that can be found [in the Go Source](https://github.com/golang/go/blob/m | |||
| 10 | 10 | This is a list of the copied files: | |
| 11 | 11 | ||
| 12 | 12 | * `zcallback_darwin_*.s` from package `runtime` | |
| 13 | - | * `internal/abi/abi_*.h` from package `runtime/cgo`⏎̸ | |
| 13 | + | * `internal/abi/abi_*.h` from package `runtime/cgo` | |
| 14 | + | * `internal/fakecgo/callbacks.go` from package `runtime/cgo` | |
| 15 | + | * `internal/fakecgo/iscgo.go` from package `runtime/cgo` | |
| 16 | + | * `internal/fakecgo/setenv.go` from package `runtime/cgo`⏎̸ | |
| @@ -10,4 +10,7 @@ License that can be found [in the Go Source](https://github.com/golang/go/blob/m | |||||
| 10 | This is a list of the copied files: | 10 | This is a list of the copied files: | ||
| 11 | 11 | ||||
| 12 | * `zcallback_darwin_*.s` from package `runtime` | 12 | * `zcallback_darwin_*.s` from package `runtime` | ||
| 13 | - | * `internal/abi/abi_*.h` from package `runtime/cgo`⏎̸ | 13 | + | * `internal/abi/abi_*.h` from package `runtime/cgo` |
| 14 | + | * `internal/fakecgo/callbacks.go` from package `runtime/cgo` | |||
| 15 | + | * `internal/fakecgo/iscgo.go` from package `runtime/cgo` | |||
| 16 | + | * `internal/fakecgo/setenv.go` from package `runtime/cgo`⏎̸ | |||
A cgo.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 cgo | |
| 5 | + | // +build cgo | |
| 6 | + | ||
| 7 | + | package purego | |
| 8 | + | ||
| 9 | + | // if CGO_ENABLED=1 import the Cgo runtime to ensure that it is set up properly. | |
| 10 | + | // This is required since some frameworks need TLS setup the C way which Go doesn't do. | |
| 11 | + | // We currently don't support ios in fakecgo mode so force Cgo or fail | |
| 12 | + | // Even if CGO_ENABLED=1 the Cgo runtime is not imported unless `import "C"` is used. | |
| 13 | + | // which will import this package automatically. Normally this isn't an issue since it | |
| 14 | + | // usually isn't possible to call into C without using that import. However, with purego | |
| 15 | + | // it is since we don't use `import "C"`! | |
| 16 | + | import _ "runtime/cgo" | |
| @@ -0,0 +1,16 @@ | |||||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |||
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebitengine Authors | |||
| 3 | + | ||||
| 4 | + | //go:build cgo | |||
| 5 | + | // +build cgo | |||
| 6 | + | ||||
| 7 | + | package purego | |||
| 8 | + | ||||
| 9 | + | // if CGO_ENABLED=1 import the Cgo runtime to ensure that it is set up properly. | |||
| 10 | + | // This is required since some frameworks need TLS setup the C way which Go doesn't do. | |||
| 11 | + | // We currently don't support ios in fakecgo mode so force Cgo or fail | |||
| 12 | + | // Even if CGO_ENABLED=1 the Cgo runtime is not imported unless `import "C"` is used. | |||
| 13 | + | // which will import this package automatically. Normally this isn't an issue since it | |||
| 14 | + | // usually isn't possible to call into C without using that import. However, with purego | |||
| 15 | + | // it is since we don't use `import "C"`! | |||
| 16 | + | import _ "runtime/cgo" | |||
M go_runtime.go +2 -6
| @@ -10,9 +10,5 @@ import ( | |||
| 10 | 10 | "unsafe" | |
| 11 | 11 | ) | |
| 12 | 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 uintptr, 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 | |
| 13 | + | //go:linkname runtime_cgocall runtime.cgocall | |
| 14 | + | func runtime_cgocall(fn uintptr, arg unsafe.Pointer) int32 // from runtime/sys_libc.go | |
| @@ -10,9 +10,5 @@ import ( | |||||
| 10 | "unsafe" | 10 | "unsafe" | ||
| 11 | ) | 11 | ) | ||
| 12 | 12 | ||||
| 13 | - | //go:linkname runtime_libcCall runtime.libcCall | 13 | + | //go:linkname runtime_cgocall runtime.cgocall |
| 14 | - | //go:linkname runtime_entersyscall runtime.entersyscall | 14 | + | func runtime_cgocall(fn uintptr, arg unsafe.Pointer) int32 // from runtime/sys_libc.go |
| 15 | - | //go:linkname runtime_exitsyscall runtime.exitsyscall | |||
| 16 | - | func runtime_libcCall(fn uintptr, 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 internal/fakecgo/callbacks.go +41 -0
| @@ -0,0 +1,41 @@ | |||
| 1 | + | // Copyright 2011 The Go Authors. All rights reserved. | |
| 2 | + | // Use of this source code is governed by a BSD-style | |
| 3 | + | // license that can be found in the LICENSE file. | |
| 4 | + | ||
| 5 | + | package fakecgo | |
| 6 | + | ||
| 7 | + | import _ "unsafe" | |
| 8 | + | ||
| 9 | + | // TODO: decide if we need _runtime_cgo_panic_internal | |
| 10 | + | ||
| 11 | + | //go:linkname x_cgo_init_trampoline x_cgo_init_trampoline | |
| 12 | + | //go:linkname _cgo_init _cgo_init | |
| 13 | + | var x_cgo_init_trampoline byte | |
| 14 | + | var _cgo_init = &x_cgo_init_trampoline | |
| 15 | + | ||
| 16 | + | // Creates a new system thread without updating any Go state. | |
| 17 | + | // | |
| 18 | + | // This method is invoked during shared library loading to create a new OS | |
| 19 | + | // thread to perform the runtime initialization. This method is similar to | |
| 20 | + | // _cgo_sys_thread_start except that it doesn't update any Go state. | |
| 21 | + | ||
| 22 | + | //go:linkname x_cgo_thread_start_trampoline x_cgo_thread_start_trampoline | |
| 23 | + | //go:linkname _cgo_thread_start _cgo_thread_start | |
| 24 | + | var x_cgo_thread_start_trampoline byte | |
| 25 | + | var _cgo_thread_start = &x_cgo_thread_start_trampoline | |
| 26 | + | ||
| 27 | + | // Notifies that the runtime has been initialized. | |
| 28 | + | // | |
| 29 | + | // We currently block at every CGO entry point (via _cgo_wait_runtime_init_done) | |
| 30 | + | // to ensure that the runtime has been initialized before the CGO call is | |
| 31 | + | // executed. This is necessary for shared libraries where we kickoff runtime | |
| 32 | + | // initialization in a separate thread and return without waiting for this | |
| 33 | + | // thread to complete the init. | |
| 34 | + | ||
| 35 | + | //go:linkname x_cgo_notify_runtime_init_done_trampoline x_cgo_notify_runtime_init_done_trampoline | |
| 36 | + | //go:linkname _cgo_notify_runtime_init_done _cgo_notify_runtime_init_done | |
| 37 | + | var x_cgo_notify_runtime_init_done_trampoline byte | |
| 38 | + | var _cgo_notify_runtime_init_done = &x_cgo_notify_runtime_init_done_trampoline | |
| 39 | + | ||
| 40 | + | // TODO: decide if we need x_cgo_set_context_function | |
| 41 | + | // TODO: decide if we need _cgo_yield | |
| @@ -0,0 +1,41 @@ | |||||
| 1 | + | // Copyright 2011 The Go Authors. All rights reserved. | |||
| 2 | + | // Use of this source code is governed by a BSD-style | |||
| 3 | + | // license that can be found in the LICENSE file. | |||
| 4 | + | ||||
| 5 | + | package fakecgo | |||
| 6 | + | ||||
| 7 | + | import _ "unsafe" | |||
| 8 | + | ||||
| 9 | + | // TODO: decide if we need _runtime_cgo_panic_internal | |||
| 10 | + | ||||
| 11 | + | //go:linkname x_cgo_init_trampoline x_cgo_init_trampoline | |||
| 12 | + | //go:linkname _cgo_init _cgo_init | |||
| 13 | + | var x_cgo_init_trampoline byte | |||
| 14 | + | var _cgo_init = &x_cgo_init_trampoline | |||
| 15 | + | ||||
| 16 | + | // Creates a new system thread without updating any Go state. | |||
| 17 | + | // | |||
| 18 | + | // This method is invoked during shared library loading to create a new OS | |||
| 19 | + | // thread to perform the runtime initialization. This method is similar to | |||
| 20 | + | // _cgo_sys_thread_start except that it doesn't update any Go state. | |||
| 21 | + | ||||
| 22 | + | //go:linkname x_cgo_thread_start_trampoline x_cgo_thread_start_trampoline | |||
| 23 | + | //go:linkname _cgo_thread_start _cgo_thread_start | |||
| 24 | + | var x_cgo_thread_start_trampoline byte | |||
| 25 | + | var _cgo_thread_start = &x_cgo_thread_start_trampoline | |||
| 26 | + | ||||
| 27 | + | // Notifies that the runtime has been initialized. | |||
| 28 | + | // | |||
| 29 | + | // We currently block at every CGO entry point (via _cgo_wait_runtime_init_done) | |||
| 30 | + | // to ensure that the runtime has been initialized before the CGO call is | |||
| 31 | + | // executed. This is necessary for shared libraries where we kickoff runtime | |||
| 32 | + | // initialization in a separate thread and return without waiting for this | |||
| 33 | + | // thread to complete the init. | |||
| 34 | + | ||||
| 35 | + | //go:linkname x_cgo_notify_runtime_init_done_trampoline x_cgo_notify_runtime_init_done_trampoline | |||
| 36 | + | //go:linkname _cgo_notify_runtime_init_done _cgo_notify_runtime_init_done | |||
| 37 | + | var x_cgo_notify_runtime_init_done_trampoline byte | |||
| 38 | + | var _cgo_notify_runtime_init_done = &x_cgo_notify_runtime_init_done_trampoline | |||
| 39 | + | ||||
| 40 | + | // TODO: decide if we need x_cgo_set_context_function | |||
| 41 | + | // TODO: decide if we need _cgo_yield | |||
A internal/fakecgo/doc.go +28 -0
| @@ -0,0 +1,28 @@ | |||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebitengine Authors | |
| 3 | + | ||
| 4 | + | // Package fakecgo implements the Cgo runtime (runtime/cgo) entirely in Go. | |
| 5 | + | // This allows code that calls into C to function properly when CGO_ENABLED=0. | |
| 6 | + | // | |
| 7 | + | // # Goals | |
| 8 | + | // | |
| 9 | + | // fakecgo attempts to replicate the same naming structure as in the runtime. | |
| 10 | + | // For example, functions that have the prefix "gcc_*" are named "go_*". | |
| 11 | + | // This makes it easier to port other GOOSs and GOARCHs as well as to keep | |
| 12 | + | // it in sync with runtime/cgo. | |
| 13 | + | // | |
| 14 | + | // # Support | |
| 15 | + | // | |
| 16 | + | // Currently, fakecgo only supports macOS on amd64 & arm64. It also cannot | |
| 17 | + | // be used with -buildmode=c-archive because that requires special initialization | |
| 18 | + | // that fakecgo does not implement at the moment. | |
| 19 | + | // | |
| 20 | + | // # Usage | |
| 21 | + | // | |
| 22 | + | // Using fakecgo is easy just import _ "github.com/ebitengine/purego" and then | |
| 23 | + | // set the environment variable CGO_ENABLED=0. | |
| 24 | + | // The recommended usage for fakecgo is to prefer using runtime/cgo if possible | |
| 25 | + | // but if cross-compiling or fast build times are important fakecgo is available. | |
| 26 | + | // Purego will pick which ever Cgo runtime is available and prefer the one that | |
| 27 | + | // comes with Go (runtime/cgo). | |
| 28 | + | package fakecgo | |
| @@ -0,0 +1,28 @@ | |||||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |||
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebitengine Authors | |||
| 3 | + | ||||
| 4 | + | // Package fakecgo implements the Cgo runtime (runtime/cgo) entirely in Go. | |||
| 5 | + | // This allows code that calls into C to function properly when CGO_ENABLED=0. | |||
| 6 | + | // | |||
| 7 | + | // # Goals | |||
| 8 | + | // | |||
| 9 | + | // fakecgo attempts to replicate the same naming structure as in the runtime. | |||
| 10 | + | // For example, functions that have the prefix "gcc_*" are named "go_*". | |||
| 11 | + | // This makes it easier to port other GOOSs and GOARCHs as well as to keep | |||
| 12 | + | // it in sync with runtime/cgo. | |||
| 13 | + | // | |||
| 14 | + | // # Support | |||
| 15 | + | // | |||
| 16 | + | // Currently, fakecgo only supports macOS on amd64 & arm64. It also cannot | |||
| 17 | + | // be used with -buildmode=c-archive because that requires special initialization | |||
| 18 | + | // that fakecgo does not implement at the moment. | |||
| 19 | + | // | |||
| 20 | + | // # Usage | |||
| 21 | + | // | |||
| 22 | + | // Using fakecgo is easy just import _ "github.com/ebitengine/purego" and then | |||
| 23 | + | // set the environment variable CGO_ENABLED=0. | |||
| 24 | + | // The recommended usage for fakecgo is to prefer using runtime/cgo if possible | |||
| 25 | + | // but if cross-compiling or fast build times are important fakecgo is available. | |||
| 26 | + | // Purego will pick which ever Cgo runtime is available and prefer the one that | |||
| 27 | + | // comes with Go (runtime/cgo). | |||
| 28 | + | package fakecgo | |||
A internal/fakecgo/go_darwin_amd64.go +69 -0
| @@ -0,0 +1,69 @@ | |||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebitengine Authors | |
| 3 | + | ||
| 4 | + | package fakecgo | |
| 5 | + | ||
| 6 | + | import "unsafe" | |
| 7 | + | ||
| 8 | + | //go:nosplit | |
| 9 | + | func _cgo_sys_thread_start(ts *ThreadStart) { | |
| 10 | + | var attr pthread_attr_t | |
| 11 | + | var ign, oset sigset_t | |
| 12 | + | var p pthread_t | |
| 13 | + | var size size_t | |
| 14 | + | var err int | |
| 15 | + | ||
| 16 | + | //fprintf(stderr, "runtime/cgo: _cgo_sys_thread_start: fn=%p, g=%p\n", ts->fn, ts->g); // debug | |
| 17 | + | sigfillset(&ign) | |
| 18 | + | pthread_sigmask(SIG_SETMASK, &ign, &oset) | |
| 19 | + | ||
| 20 | + | pthread_attr_init(&attr) | |
| 21 | + | pthread_attr_getstacksize(&attr, &size) | |
| 22 | + | // Leave stacklo=0 and set stackhi=size; mstart will do the rest. | |
| 23 | + | ts.g.stackhi = uintptr(size) | |
| 24 | + | ||
| 25 | + | err = _cgo_try_pthread_create(&p, &attr, unsafe.Pointer(threadentry_trampolineABI0), ts) | |
| 26 | + | ||
| 27 | + | pthread_sigmask(SIG_SETMASK, &oset, nil) | |
| 28 | + | ||
| 29 | + | if err != 0 { | |
| 30 | + | print("fakecgo: pthread_create failed: ") | |
| 31 | + | println(err) | |
| 32 | + | abort() | |
| 33 | + | } | |
| 34 | + | } | |
| 35 | + | ||
| 36 | + | // threadentry_trampolineABI0 maps the C ABI to Go ABI then calls the Go function | |
| 37 | + | // | |
| 38 | + | //go:linkname x_threadentry_trampoline threadentry_trampoline | |
| 39 | + | var x_threadentry_trampoline byte | |
| 40 | + | var threadentry_trampolineABI0 = &x_threadentry_trampoline | |
| 41 | + | ||
| 42 | + | //go:nosplit | |
| 43 | + | func threadentry(v unsafe.Pointer) unsafe.Pointer { | |
| 44 | + | ts := *(*ThreadStart)(v) | |
| 45 | + | free(v) | |
| 46 | + | ||
| 47 | + | setg_trampoline(setg_func, uintptr(unsafe.Pointer(ts.g))) | |
| 48 | + | ||
| 49 | + | // faking funcs in go is a bit a... involved - but the following works :) | |
| 50 | + | fn := uintptr(unsafe.Pointer(&ts.fn)) | |
| 51 | + | (*(*func())(unsafe.Pointer(&fn)))() | |
| 52 | + | ||
| 53 | + | return nil | |
| 54 | + | } | |
| 55 | + | ||
| 56 | + | // here we will store a pointer to the provided setg func | |
| 57 | + | var setg_func uintptr | |
| 58 | + | ||
| 59 | + | //go:nosplit | |
| 60 | + | func x_cgo_init(g *G, setg uintptr) { | |
| 61 | + | var size size_t | |
| 62 | + | var attr pthread_attr_t | |
| 63 | + | ||
| 64 | + | setg_func = setg | |
| 65 | + | pthread_attr_init(&attr) | |
| 66 | + | pthread_attr_getstacksize(&attr, &size) | |
| 67 | + | g.stacklo = uintptr(unsafe.Pointer(&size)) - uintptr(size) + 4096 | |
| 68 | + | pthread_attr_destroy(&attr) | |
| 69 | + | } | |
| @@ -0,0 +1,69 @@ | |||||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |||
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebitengine Authors | |||
| 3 | + | ||||
| 4 | + | package fakecgo | |||
| 5 | + | ||||
| 6 | + | import "unsafe" | |||
| 7 | + | ||||
| 8 | + | //go:nosplit | |||
| 9 | + | func _cgo_sys_thread_start(ts *ThreadStart) { | |||
| 10 | + | var attr pthread_attr_t | |||
| 11 | + | var ign, oset sigset_t | |||
| 12 | + | var p pthread_t | |||
| 13 | + | var size size_t | |||
| 14 | + | var err int | |||
| 15 | + | ||||
| 16 | + | //fprintf(stderr, "runtime/cgo: _cgo_sys_thread_start: fn=%p, g=%p\n", ts->fn, ts->g); // debug | |||
| 17 | + | sigfillset(&ign) | |||
| 18 | + | pthread_sigmask(SIG_SETMASK, &ign, &oset) | |||
| 19 | + | ||||
| 20 | + | pthread_attr_init(&attr) | |||
| 21 | + | pthread_attr_getstacksize(&attr, &size) | |||
| 22 | + | // Leave stacklo=0 and set stackhi=size; mstart will do the rest. | |||
| 23 | + | ts.g.stackhi = uintptr(size) | |||
| 24 | + | ||||
| 25 | + | err = _cgo_try_pthread_create(&p, &attr, unsafe.Pointer(threadentry_trampolineABI0), ts) | |||
| 26 | + | ||||
| 27 | + | pthread_sigmask(SIG_SETMASK, &oset, nil) | |||
| 28 | + | ||||
| 29 | + | if err != 0 { | |||
| 30 | + | print("fakecgo: pthread_create failed: ") | |||
| 31 | + | println(err) | |||
| 32 | + | abort() | |||
| 33 | + | } | |||
| 34 | + | } | |||
| 35 | + | ||||
| 36 | + | // threadentry_trampolineABI0 maps the C ABI to Go ABI then calls the Go function | |||
| 37 | + | // | |||
| 38 | + | //go:linkname x_threadentry_trampoline threadentry_trampoline | |||
| 39 | + | var x_threadentry_trampoline byte | |||
| 40 | + | var threadentry_trampolineABI0 = &x_threadentry_trampoline | |||
| 41 | + | ||||
| 42 | + | //go:nosplit | |||
| 43 | + | func threadentry(v unsafe.Pointer) unsafe.Pointer { | |||
| 44 | + | ts := *(*ThreadStart)(v) | |||
| 45 | + | free(v) | |||
| 46 | + | ||||
| 47 | + | setg_trampoline(setg_func, uintptr(unsafe.Pointer(ts.g))) | |||
| 48 | + | ||||
| 49 | + | // faking funcs in go is a bit a... involved - but the following works :) | |||
| 50 | + | fn := uintptr(unsafe.Pointer(&ts.fn)) | |||
| 51 | + | (*(*func())(unsafe.Pointer(&fn)))() | |||
| 52 | + | ||||
| 53 | + | return nil | |||
| 54 | + | } | |||
| 55 | + | ||||
| 56 | + | // here we will store a pointer to the provided setg func | |||
| 57 | + | var setg_func uintptr | |||
| 58 | + | ||||
| 59 | + | //go:nosplit | |||
| 60 | + | func x_cgo_init(g *G, setg uintptr) { | |||
| 61 | + | var size size_t | |||
| 62 | + | var attr pthread_attr_t | |||
| 63 | + | ||||
| 64 | + | setg_func = setg | |||
| 65 | + | pthread_attr_init(&attr) | |||
| 66 | + | pthread_attr_getstacksize(&attr, &size) | |||
| 67 | + | g.stacklo = uintptr(unsafe.Pointer(&size)) - uintptr(size) + 4096 | |||
| 68 | + | pthread_attr_destroy(&attr) | |||
| 69 | + | } | |||
A internal/fakecgo/go_darwin_arm64.go +85 -0
| @@ -0,0 +1,85 @@ | |||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebitengine Authors | |
| 3 | + | ||
| 4 | + | package fakecgo | |
| 5 | + | ||
| 6 | + | import "unsafe" | |
| 7 | + | ||
| 8 | + | //go:nosplit | |
| 9 | + | func _cgo_sys_thread_start(ts *ThreadStart) { | |
| 10 | + | var attr pthread_attr_t | |
| 11 | + | var ign, oset sigset_t | |
| 12 | + | var p pthread_t | |
| 13 | + | var size size_t | |
| 14 | + | var err int | |
| 15 | + | ||
| 16 | + | //fprintf(stderr, "runtime/cgo: _cgo_sys_thread_start: fn=%p, g=%p\n", ts->fn, ts->g); // debug | |
| 17 | + | sigfillset(&ign) | |
| 18 | + | pthread_sigmask(SIG_SETMASK, &ign, &oset) | |
| 19 | + | ||
| 20 | + | pthread_attr_init(&attr) | |
| 21 | + | pthread_attr_getstacksize(&attr, &size) | |
| 22 | + | // Leave stacklo=0 and set stackhi=size; mstart will do the rest. | |
| 23 | + | ts.g.stackhi = uintptr(size) | |
| 24 | + | ||
| 25 | + | err = _cgo_try_pthread_create(&p, &attr, unsafe.Pointer(threadentry_trampolineABI0), ts) | |
| 26 | + | ||
| 27 | + | pthread_sigmask(SIG_SETMASK, &oset, nil) | |
| 28 | + | ||
| 29 | + | if err != 0 { | |
| 30 | + | print("fakecgo: pthread_create failed: ") | |
| 31 | + | println(err) | |
| 32 | + | abort() | |
| 33 | + | } | |
| 34 | + | } | |
| 35 | + | ||
| 36 | + | // threadentry_trampolineABI0 maps the C ABI to Go ABI then calls the Go function | |
| 37 | + | // | |
| 38 | + | //go:linkname x_threadentry_trampoline threadentry_trampoline | |
| 39 | + | var x_threadentry_trampoline byte | |
| 40 | + | var threadentry_trampolineABI0 = &x_threadentry_trampoline | |
| 41 | + | ||
| 42 | + | //go:nosplit | |
| 43 | + | func threadentry(v unsafe.Pointer) unsafe.Pointer { | |
| 44 | + | ts := *(*ThreadStart)(v) | |
| 45 | + | free(v) | |
| 46 | + | ||
| 47 | + | // TODO: support ios | |
| 48 | + | //#if TARGET_OS_IPHONE | |
| 49 | + | // darwin_arm_init_thread_exception_port(); | |
| 50 | + | //#endif | |
| 51 | + | setg_trampoline(setg_func, uintptr(unsafe.Pointer(ts.g))) | |
| 52 | + | ||
| 53 | + | // faking funcs in go is a bit a... involved - but the following works :) | |
| 54 | + | fn := uintptr(unsafe.Pointer(&ts.fn)) | |
| 55 | + | (*(*func())(unsafe.Pointer(&fn)))() | |
| 56 | + | ||
| 57 | + | return nil | |
| 58 | + | } | |
| 59 | + | ||
| 60 | + | // here we will store a pointer to the provided setg func | |
| 61 | + | var setg_func uintptr | |
| 62 | + | ||
| 63 | + | // x_cgo_init(G *g, void (*setg)(void*)) (runtime/cgo/gcc_linux_amd64.c) | |
| 64 | + | // This get's called during startup, adjusts stacklo, and provides a pointer to setg_gcc for us | |
| 65 | + | // Additionally, if we set _cgo_init to non-null, go won't do it's own TLS setup | |
| 66 | + | // This function can't be go:systemstack since go is not in a state where the systemcheck would work. | |
| 67 | + | // | |
| 68 | + | //go:nosplit | |
| 69 | + | func x_cgo_init(g *G, setg uintptr) { | |
| 70 | + | var size size_t | |
| 71 | + | var attr pthread_attr_t | |
| 72 | + | ||
| 73 | + | setg_func = setg | |
| 74 | + | pthread_attr_init(&attr) | |
| 75 | + | pthread_attr_getstacksize(&attr, &size) | |
| 76 | + | g.stacklo = uintptr(unsafe.Pointer(&size)) - uintptr(size) + 4096 | |
| 77 | + | pthread_attr_destroy(&attr) | |
| 78 | + | ||
| 79 | + | //TODO: support ios | |
| 80 | + | //#if TARGET_OS_IPHONE | |
| 81 | + | // darwin_arm_init_mach_exception_handler(); | |
| 82 | + | // darwin_arm_init_thread_exception_port(); | |
| 83 | + | // init_working_dir(); | |
| 84 | + | //#endif | |
| 85 | + | } | |
| @@ -0,0 +1,85 @@ | |||||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |||
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebitengine Authors | |||
| 3 | + | ||||
| 4 | + | package fakecgo | |||
| 5 | + | ||||
| 6 | + | import "unsafe" | |||
| 7 | + | ||||
| 8 | + | //go:nosplit | |||
| 9 | + | func _cgo_sys_thread_start(ts *ThreadStart) { | |||
| 10 | + | var attr pthread_attr_t | |||
| 11 | + | var ign, oset sigset_t | |||
| 12 | + | var p pthread_t | |||
| 13 | + | var size size_t | |||
| 14 | + | var err int | |||
| 15 | + | ||||
| 16 | + | //fprintf(stderr, "runtime/cgo: _cgo_sys_thread_start: fn=%p, g=%p\n", ts->fn, ts->g); // debug | |||
| 17 | + | sigfillset(&ign) | |||
| 18 | + | pthread_sigmask(SIG_SETMASK, &ign, &oset) | |||
| 19 | + | ||||
| 20 | + | pthread_attr_init(&attr) | |||
| 21 | + | pthread_attr_getstacksize(&attr, &size) | |||
| 22 | + | // Leave stacklo=0 and set stackhi=size; mstart will do the rest. | |||
| 23 | + | ts.g.stackhi = uintptr(size) | |||
| 24 | + | ||||
| 25 | + | err = _cgo_try_pthread_create(&p, &attr, unsafe.Pointer(threadentry_trampolineABI0), ts) | |||
| 26 | + | ||||
| 27 | + | pthread_sigmask(SIG_SETMASK, &oset, nil) | |||
| 28 | + | ||||
| 29 | + | if err != 0 { | |||
| 30 | + | print("fakecgo: pthread_create failed: ") | |||
| 31 | + | println(err) | |||
| 32 | + | abort() | |||
| 33 | + | } | |||
| 34 | + | } | |||
| 35 | + | ||||
| 36 | + | // threadentry_trampolineABI0 maps the C ABI to Go ABI then calls the Go function | |||
| 37 | + | // | |||
| 38 | + | //go:linkname x_threadentry_trampoline threadentry_trampoline | |||
| 39 | + | var x_threadentry_trampoline byte | |||
| 40 | + | var threadentry_trampolineABI0 = &x_threadentry_trampoline | |||
| 41 | + | ||||
| 42 | + | //go:nosplit | |||
| 43 | + | func threadentry(v unsafe.Pointer) unsafe.Pointer { | |||
| 44 | + | ts := *(*ThreadStart)(v) | |||
| 45 | + | free(v) | |||
| 46 | + | ||||
| 47 | + | // TODO: support ios | |||
| 48 | + | //#if TARGET_OS_IPHONE | |||
| 49 | + | // darwin_arm_init_thread_exception_port(); | |||
| 50 | + | //#endif | |||
| 51 | + | setg_trampoline(setg_func, uintptr(unsafe.Pointer(ts.g))) | |||
| 52 | + | ||||
| 53 | + | // faking funcs in go is a bit a... involved - but the following works :) | |||
| 54 | + | fn := uintptr(unsafe.Pointer(&ts.fn)) | |||
| 55 | + | (*(*func())(unsafe.Pointer(&fn)))() | |||
| 56 | + | ||||
| 57 | + | return nil | |||
| 58 | + | } | |||
| 59 | + | ||||
| 60 | + | // here we will store a pointer to the provided setg func | |||
| 61 | + | var setg_func uintptr | |||
| 62 | + | ||||
| 63 | + | // x_cgo_init(G *g, void (*setg)(void*)) (runtime/cgo/gcc_linux_amd64.c) | |||
| 64 | + | // This get's called during startup, adjusts stacklo, and provides a pointer to setg_gcc for us | |||
| 65 | + | // Additionally, if we set _cgo_init to non-null, go won't do it's own TLS setup | |||
| 66 | + | // This function can't be go:systemstack since go is not in a state where the systemcheck would work. | |||
| 67 | + | // | |||
| 68 | + | //go:nosplit | |||
| 69 | + | func x_cgo_init(g *G, setg uintptr) { | |||
| 70 | + | var size size_t | |||
| 71 | + | var attr pthread_attr_t | |||
| 72 | + | ||||
| 73 | + | setg_func = setg | |||
| 74 | + | pthread_attr_init(&attr) | |||
| 75 | + | pthread_attr_getstacksize(&attr, &size) | |||
| 76 | + | g.stacklo = uintptr(unsafe.Pointer(&size)) - uintptr(size) + 4096 | |||
| 77 | + | pthread_attr_destroy(&attr) | |||
| 78 | + | ||||
| 79 | + | //TODO: support ios | |||
| 80 | + | //#if TARGET_OS_IPHONE | |||
| 81 | + | // darwin_arm_init_mach_exception_handler(); | |||
| 82 | + | // darwin_arm_init_thread_exception_port(); | |||
| 83 | + | // init_working_dir(); | |||
| 84 | + | //#endif | |||
| 85 | + | } | |||
A internal/fakecgo/go_libinit.go +39 -0
| @@ -0,0 +1,39 @@ | |||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebitengine Authors | |
| 3 | + | ||
| 4 | + | package fakecgo | |
| 5 | + | ||
| 6 | + | import ( | |
| 7 | + | "syscall" | |
| 8 | + | "unsafe" | |
| 9 | + | ) | |
| 10 | + | ||
| 11 | + | //go:nosplit | |
| 12 | + | func x_cgo_notify_runtime_init_done() { | |
| 13 | + | // we don't support being called as a library | |
| 14 | + | } | |
| 15 | + | ||
| 16 | + | // _cgo_try_pthread_create retries pthread_create if it fails with | |
| 17 | + | // EAGAIN. | |
| 18 | + | // | |
| 19 | + | //go:nosplit | |
| 20 | + | func _cgo_try_pthread_create(thread *pthread_t, attr *pthread_attr_t, pfn unsafe.Pointer, arg *ThreadStart) int { | |
| 21 | + | var tries int | |
| 22 | + | var err int | |
| 23 | + | var ts timespec | |
| 24 | + | ||
| 25 | + | for tries = 0; tries < 20; tries++ { | |
| 26 | + | err = int(pthread_create(thread, attr, pfn, unsafe.Pointer(arg))) | |
| 27 | + | if err == 0 { | |
| 28 | + | pthread_detach(*thread) | |
| 29 | + | return 0 | |
| 30 | + | } | |
| 31 | + | if err != int(syscall.EAGAIN) { | |
| 32 | + | return err | |
| 33 | + | } | |
| 34 | + | ts.tv_sec = 0 | |
| 35 | + | ts.tv_nsec = (tries + 1) * 1000 * 1000 // Milliseconds. | |
| 36 | + | nanosleep(&ts, nil) | |
| 37 | + | } | |
| 38 | + | return int(syscall.EAGAIN) | |
| 39 | + | } | |
| @@ -0,0 +1,39 @@ | |||||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |||
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebitengine Authors | |||
| 3 | + | ||||
| 4 | + | package fakecgo | |||
| 5 | + | ||||
| 6 | + | import ( | |||
| 7 | + | "syscall" | |||
| 8 | + | "unsafe" | |||
| 9 | + | ) | |||
| 10 | + | ||||
| 11 | + | //go:nosplit | |||
| 12 | + | func x_cgo_notify_runtime_init_done() { | |||
| 13 | + | // we don't support being called as a library | |||
| 14 | + | } | |||
| 15 | + | ||||
| 16 | + | // _cgo_try_pthread_create retries pthread_create if it fails with | |||
| 17 | + | // EAGAIN. | |||
| 18 | + | // | |||
| 19 | + | //go:nosplit | |||
| 20 | + | func _cgo_try_pthread_create(thread *pthread_t, attr *pthread_attr_t, pfn unsafe.Pointer, arg *ThreadStart) int { | |||
| 21 | + | var tries int | |||
| 22 | + | var err int | |||
| 23 | + | var ts timespec | |||
| 24 | + | ||||
| 25 | + | for tries = 0; tries < 20; tries++ { | |||
| 26 | + | err = int(pthread_create(thread, attr, pfn, unsafe.Pointer(arg))) | |||
| 27 | + | if err == 0 { | |||
| 28 | + | pthread_detach(*thread) | |||
| 29 | + | return 0 | |||
| 30 | + | } | |||
| 31 | + | if err != int(syscall.EAGAIN) { | |||
| 32 | + | return err | |||
| 33 | + | } | |||
| 34 | + | ts.tv_sec = 0 | |||
| 35 | + | ts.tv_nsec = (tries + 1) * 1000 * 1000 // Milliseconds. | |||
| 36 | + | nanosleep(&ts, nil) | |||
| 37 | + | } | |||
| 38 | + | return int(syscall.EAGAIN) | |||
| 39 | + | } | |||
A internal/fakecgo/go_setenv.go +14 -0
| @@ -0,0 +1,14 @@ | |||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebitengine Authors | |
| 3 | + | ||
| 4 | + | package fakecgo | |
| 5 | + | ||
| 6 | + | //go:nosplit | |
| 7 | + | func x_cgo_setenv(arg *[2]*byte) { | |
| 8 | + | setenv(arg[0], arg[1], 1) | |
| 9 | + | } | |
| 10 | + | ||
| 11 | + | //go:nosplit | |
| 12 | + | func x_cgo_unsetenv(arg *[1]*byte) { | |
| 13 | + | unsetenv(arg[0]) | |
| 14 | + | } | |
| @@ -0,0 +1,14 @@ | |||||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |||
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebitengine Authors | |||
| 3 | + | ||||
| 4 | + | package fakecgo | |||
| 5 | + | ||||
| 6 | + | //go:nosplit | |||
| 7 | + | func x_cgo_setenv(arg *[2]*byte) { | |||
| 8 | + | setenv(arg[0], arg[1], 1) | |||
| 9 | + | } | |||
| 10 | + | ||||
| 11 | + | //go:nosplit | |||
| 12 | + | func x_cgo_unsetenv(arg *[1]*byte) { | |||
| 13 | + | unsetenv(arg[0]) | |||
| 14 | + | } | |||
A internal/fakecgo/go_util.go +30 -0
| @@ -0,0 +1,30 @@ | |||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebitengine Authors | |
| 3 | + | ||
| 4 | + | package fakecgo | |
| 5 | + | ||
| 6 | + | import "unsafe" | |
| 7 | + | ||
| 8 | + | // _cgo_thread_start is split into three parts in cgo since only one part is system dependent (keep it here for easier handling) | |
| 9 | + | ||
| 10 | + | // _cgo_thread_start(ThreadStart *arg) (runtime/cgo/gcc_util.c) | |
| 11 | + | // This get's called instead of the go code for creating new threads | |
| 12 | + | // -> pthread_* stuff is used, so threads are setup correctly for C | |
| 13 | + | // If this is missing, TLS is only setup correctly on thread 1! | |
| 14 | + | // This function should be go:systemstack instead of go:nosplit (but that requires runtime) | |
| 15 | + | // | |
| 16 | + | //go:nosplit | |
| 17 | + | func x_cgo_thread_start(arg *ThreadStart) { | |
| 18 | + | var ts *ThreadStart | |
| 19 | + | // Make our own copy that can persist after we return. | |
| 20 | + | // _cgo_tsan_acquire(); | |
| 21 | + | ts = (*ThreadStart)(malloc(unsafe.Sizeof(*ts))) | |
| 22 | + | // _cgo_tsan_release(); | |
| 23 | + | if ts == nil { | |
| 24 | + | println("fakecgo: out of memory in thread_start") | |
| 25 | + | abort() | |
| 26 | + | } | |
| 27 | + | // *ts = *arg would cause a writebarrier so use memmove instead | |
| 28 | + | memmove(unsafe.Pointer(ts), unsafe.Pointer(arg), unsafe.Sizeof(*ts)) | |
| 29 | + | _cgo_sys_thread_start(ts) // OS-dependent half | |
| 30 | + | } | |
| @@ -0,0 +1,30 @@ | |||||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |||
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebitengine Authors | |||
| 3 | + | ||||
| 4 | + | package fakecgo | |||
| 5 | + | ||||
| 6 | + | import "unsafe" | |||
| 7 | + | ||||
| 8 | + | // _cgo_thread_start is split into three parts in cgo since only one part is system dependent (keep it here for easier handling) | |||
| 9 | + | ||||
| 10 | + | // _cgo_thread_start(ThreadStart *arg) (runtime/cgo/gcc_util.c) | |||
| 11 | + | // This get's called instead of the go code for creating new threads | |||
| 12 | + | // -> pthread_* stuff is used, so threads are setup correctly for C | |||
| 13 | + | // If this is missing, TLS is only setup correctly on thread 1! | |||
| 14 | + | // This function should be go:systemstack instead of go:nosplit (but that requires runtime) | |||
| 15 | + | // | |||
| 16 | + | //go:nosplit | |||
| 17 | + | func x_cgo_thread_start(arg *ThreadStart) { | |||
| 18 | + | var ts *ThreadStart | |||
| 19 | + | // Make our own copy that can persist after we return. | |||
| 20 | + | // _cgo_tsan_acquire(); | |||
| 21 | + | ts = (*ThreadStart)(malloc(unsafe.Sizeof(*ts))) | |||
| 22 | + | // _cgo_tsan_release(); | |||
| 23 | + | if ts == nil { | |||
| 24 | + | println("fakecgo: out of memory in thread_start") | |||
| 25 | + | abort() | |||
| 26 | + | } | |||
| 27 | + | // *ts = *arg would cause a writebarrier so use memmove instead | |||
| 28 | + | memmove(unsafe.Pointer(ts), unsafe.Pointer(arg), unsafe.Sizeof(*ts)) | |||
| 29 | + | _cgo_sys_thread_start(ts) // OS-dependent half | |||
| 30 | + | } | |||
A internal/fakecgo/iscgo.go +17 -0
| @@ -0,0 +1,17 @@ | |||
| 1 | + | // Copyright 2010 The Go Authors. All rights reserved. | |
| 2 | + | // Use of this source code is governed by a BSD-style | |
| 3 | + | // license that can be found in the LICENSE file. | |
| 4 | + | ||
| 5 | + | // The runtime package contains an uninitialized definition | |
| 6 | + | // for runtime·iscgo. Override it to tell the runtime we're here. | |
| 7 | + | // There are various function pointers that should be set too, | |
| 8 | + | // but those depend on dynamic linker magic to get initialized | |
| 9 | + | // correctly, and sometimes they break. This variable is a | |
| 10 | + | // backup: it depends only on old C style static linking rules. | |
| 11 | + | ||
| 12 | + | package fakecgo | |
| 13 | + | ||
| 14 | + | import _ "unsafe" // for go:linkname | |
| 15 | + | ||
| 16 | + | //go:linkname _iscgo runtime.iscgo | |
| 17 | + | var _iscgo bool = true | |
| @@ -0,0 +1,17 @@ | |||||
| 1 | + | // Copyright 2010 The Go Authors. All rights reserved. | |||
| 2 | + | // Use of this source code is governed by a BSD-style | |||
| 3 | + | // license that can be found in the LICENSE file. | |||
| 4 | + | ||||
| 5 | + | // The runtime package contains an uninitialized definition | |||
| 6 | + | // for runtime·iscgo. Override it to tell the runtime we're here. | |||
| 7 | + | // There are various function pointers that should be set too, | |||
| 8 | + | // but those depend on dynamic linker magic to get initialized | |||
| 9 | + | // correctly, and sometimes they break. This variable is a | |||
| 10 | + | // backup: it depends only on old C style static linking rules. | |||
| 11 | + | ||||
| 12 | + | package fakecgo | |||
| 13 | + | ||||
| 14 | + | import _ "unsafe" // for go:linkname | |||
| 15 | + | ||||
| 16 | + | //go:linkname _iscgo runtime.iscgo | |||
| 17 | + | var _iscgo bool = true | |||
A internal/fakecgo/libcgo.go +37 -0
| @@ -0,0 +1,37 @@ | |||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebitengine Authors | |
| 3 | + | ||
| 4 | + | package fakecgo | |
| 5 | + | ||
| 6 | + | type size_t uintptr | |
| 7 | + | ||
| 8 | + | type sigset_t [128]byte // TODO: figure out how big this should be | |
| 9 | + | type pthread_attr_t [56]byte // TODO: figure out how big this should be | |
| 10 | + | type pthread_t int | |
| 11 | + | ||
| 12 | + | // We could take timespec from syscall - but there it uses int32 and int64 for 32 bit and 64 bit arch, which complicates stuff for us | |
| 13 | + | type timespec struct { | |
| 14 | + | tv_sec int | |
| 15 | + | tv_nsec int | |
| 16 | + | } | |
| 17 | + | ||
| 18 | + | // for pthread_sigmask: | |
| 19 | + | ||
| 20 | + | type sighow int32 | |
| 21 | + | ||
| 22 | + | const ( | |
| 23 | + | SIG_BLOCK sighow = 0 | |
| 24 | + | SIG_UNBLOCK sighow = 1 | |
| 25 | + | SIG_SETMASK sighow = 2 | |
| 26 | + | ) | |
| 27 | + | ||
| 28 | + | type G struct { | |
| 29 | + | stacklo uintptr | |
| 30 | + | stackhi uintptr | |
| 31 | + | } | |
| 32 | + | ||
| 33 | + | type ThreadStart struct { | |
| 34 | + | g *G | |
| 35 | + | tls *uintptr | |
| 36 | + | fn uintptr | |
| 37 | + | } | |
| @@ -0,0 +1,37 @@ | |||||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |||
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebitengine Authors | |||
| 3 | + | ||||
| 4 | + | package fakecgo | |||
| 5 | + | ||||
| 6 | + | type size_t uintptr | |||
| 7 | + | ||||
| 8 | + | type sigset_t [128]byte // TODO: figure out how big this should be | |||
| 9 | + | type pthread_attr_t [56]byte // TODO: figure out how big this should be | |||
| 10 | + | type pthread_t int | |||
| 11 | + | ||||
| 12 | + | // We could take timespec from syscall - but there it uses int32 and int64 for 32 bit and 64 bit arch, which complicates stuff for us | |||
| 13 | + | type timespec struct { | |||
| 14 | + | tv_sec int | |||
| 15 | + | tv_nsec int | |||
| 16 | + | } | |||
| 17 | + | ||||
| 18 | + | // for pthread_sigmask: | |||
| 19 | + | ||||
| 20 | + | type sighow int32 | |||
| 21 | + | ||||
| 22 | + | const ( | |||
| 23 | + | SIG_BLOCK sighow = 0 | |||
| 24 | + | SIG_UNBLOCK sighow = 1 | |||
| 25 | + | SIG_SETMASK sighow = 2 | |||
| 26 | + | ) | |||
| 27 | + | ||||
| 28 | + | type G struct { | |||
| 29 | + | stacklo uintptr | |||
| 30 | + | stackhi uintptr | |||
| 31 | + | } | |||
| 32 | + | ||||
| 33 | + | type ThreadStart struct { | |||
| 34 | + | g *G | |||
| 35 | + | tls *uintptr | |||
| 36 | + | fn uintptr | |||
| 37 | + | } | |||
A internal/fakecgo/setenv.go +17 -0
| @@ -0,0 +1,17 @@ | |||
| 1 | + | // Copyright 2011 The Go Authors. All rights reserved. | |
| 2 | + | // Use of this source code is governed by a BSD-style | |
| 3 | + | // license that can be found in the LICENSE file. | |
| 4 | + | ||
| 5 | + | package fakecgo | |
| 6 | + | ||
| 7 | + | import _ "unsafe" // for go:linkname | |
| 8 | + | ||
| 9 | + | //go:linkname x_cgo_setenv_trampoline x_cgo_setenv_trampoline | |
| 10 | + | //go:linkname _cgo_setenv runtime._cgo_setenv | |
| 11 | + | var x_cgo_setenv_trampoline byte | |
| 12 | + | var _cgo_setenv = &x_cgo_setenv_trampoline | |
| 13 | + | ||
| 14 | + | //go:linkname x_cgo_unsetenv_trampoline x_cgo_unsetenv_trampoline | |
| 15 | + | //go:linkname _cgo_unsetenv runtime._cgo_unsetenv | |
| 16 | + | var x_cgo_unsetenv_trampoline byte | |
| 17 | + | var _cgo_unsetenv = &x_cgo_unsetenv_trampoline | |
| @@ -0,0 +1,17 @@ | |||||
| 1 | + | // Copyright 2011 The Go Authors. All rights reserved. | |||
| 2 | + | // Use of this source code is governed by a BSD-style | |||
| 3 | + | // license that can be found in the LICENSE file. | |||
| 4 | + | ||||
| 5 | + | package fakecgo | |||
| 6 | + | ||||
| 7 | + | import _ "unsafe" // for go:linkname | |||
| 8 | + | ||||
| 9 | + | //go:linkname x_cgo_setenv_trampoline x_cgo_setenv_trampoline | |||
| 10 | + | //go:linkname _cgo_setenv runtime._cgo_setenv | |||
| 11 | + | var x_cgo_setenv_trampoline byte | |||
| 12 | + | var _cgo_setenv = &x_cgo_setenv_trampoline | |||
| 13 | + | ||||
| 14 | + | //go:linkname x_cgo_unsetenv_trampoline x_cgo_unsetenv_trampoline | |||
| 15 | + | //go:linkname _cgo_unsetenv runtime._cgo_unsetenv | |||
| 16 | + | var x_cgo_unsetenv_trampoline byte | |||
| 17 | + | var _cgo_unsetenv = &x_cgo_unsetenv_trampoline | |||
A internal/fakecgo/symbols.go +48 -0
| @@ -0,0 +1,48 @@ | |||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebitengine Authors | |
| 3 | + | ||
| 4 | + | package fakecgo | |
| 5 | + | ||
| 6 | + | import "unsafe" | |
| 7 | + | ||
| 8 | + | func setg_trampoline(setg uintptr, G uintptr) | |
| 9 | + | ||
| 10 | + | //go:linkname memmove runtime.memmove | |
| 11 | + | func memmove(to, from unsafe.Pointer, n uintptr) | |
| 12 | + | ||
| 13 | + | func malloc(size uintptr) unsafe.Pointer | |
| 14 | + | ||
| 15 | + | func free(ptr unsafe.Pointer) | |
| 16 | + | ||
| 17 | + | func setenv(name *byte, value *byte, overwrite int32) int32 | |
| 18 | + | ||
| 19 | + | func unsetenv(name *byte) int32 | |
| 20 | + | ||
| 21 | + | // the no escape comments are needed inorder to ensure Go doesn't try | |
| 22 | + | // and create a new object in the runtime when the runtime isn't available. | |
| 23 | + | // After all, we are pretending to be C code. | |
| 24 | + | ||
| 25 | + | //go:noescape | |
| 26 | + | func pthread_attr_init(attr *pthread_attr_t) int32 | |
| 27 | + | ||
| 28 | + | //go:noescape | |
| 29 | + | func pthread_create(thread *pthread_t, attr *pthread_attr_t, start, arg unsafe.Pointer) int32 | |
| 30 | + | ||
| 31 | + | func pthread_detach(thread pthread_t) int32 | |
| 32 | + | ||
| 33 | + | //go:noescape | |
| 34 | + | func pthread_sigmask(how sighow, ign *sigset_t, oset *sigset_t) int32 | |
| 35 | + | ||
| 36 | + | //go:noescape | |
| 37 | + | func pthread_attr_getstacksize(attr *pthread_attr_t, stacksize *size_t) int32 | |
| 38 | + | ||
| 39 | + | //go:noescape | |
| 40 | + | func pthread_attr_destroy(attr *pthread_attr_t) int32 | |
| 41 | + | ||
| 42 | + | func abort() | |
| 43 | + | ||
| 44 | + | //go:noescape | |
| 45 | + | func sigfillset(set *sigset_t) int32 | |
| 46 | + | ||
| 47 | + | //go:noescape | |
| 48 | + | func nanosleep(ts *timespec, rem *timespec) int32 | |
| @@ -0,0 +1,48 @@ | |||||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |||
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebitengine Authors | |||
| 3 | + | ||||
| 4 | + | package fakecgo | |||
| 5 | + | ||||
| 6 | + | import "unsafe" | |||
| 7 | + | ||||
| 8 | + | func setg_trampoline(setg uintptr, G uintptr) | |||
| 9 | + | ||||
| 10 | + | //go:linkname memmove runtime.memmove | |||
| 11 | + | func memmove(to, from unsafe.Pointer, n uintptr) | |||
| 12 | + | ||||
| 13 | + | func malloc(size uintptr) unsafe.Pointer | |||
| 14 | + | ||||
| 15 | + | func free(ptr unsafe.Pointer) | |||
| 16 | + | ||||
| 17 | + | func setenv(name *byte, value *byte, overwrite int32) int32 | |||
| 18 | + | ||||
| 19 | + | func unsetenv(name *byte) int32 | |||
| 20 | + | ||||
| 21 | + | // the no escape comments are needed inorder to ensure Go doesn't try | |||
| 22 | + | // and create a new object in the runtime when the runtime isn't available. | |||
| 23 | + | // After all, we are pretending to be C code. | |||
| 24 | + | ||||
| 25 | + | //go:noescape | |||
| 26 | + | func pthread_attr_init(attr *pthread_attr_t) int32 | |||
| 27 | + | ||||
| 28 | + | //go:noescape | |||
| 29 | + | func pthread_create(thread *pthread_t, attr *pthread_attr_t, start, arg unsafe.Pointer) int32 | |||
| 30 | + | ||||
| 31 | + | func pthread_detach(thread pthread_t) int32 | |||
| 32 | + | ||||
| 33 | + | //go:noescape | |||
| 34 | + | func pthread_sigmask(how sighow, ign *sigset_t, oset *sigset_t) int32 | |||
| 35 | + | ||||
| 36 | + | //go:noescape | |||
| 37 | + | func pthread_attr_getstacksize(attr *pthread_attr_t, stacksize *size_t) int32 | |||
| 38 | + | ||||
| 39 | + | //go:noescape | |||
| 40 | + | func pthread_attr_destroy(attr *pthread_attr_t) int32 | |||
| 41 | + | ||||
| 42 | + | func abort() | |||
| 43 | + | ||||
| 44 | + | //go:noescape | |||
| 45 | + | func sigfillset(set *sigset_t) int32 | |||
| 46 | + | ||||
| 47 | + | //go:noescape | |||
| 48 | + | func nanosleep(ts *timespec, rem *timespec) int32 | |||
A internal/fakecgo/symbols_darwin.go +17 -0
| @@ -0,0 +1,17 @@ | |||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebitengine Authors | |
| 3 | + | ||
| 4 | + | package fakecgo | |
| 5 | + | ||
| 6 | + | //go:cgo_import_dynamic libc_malloc malloc "libSystem.dylib" | |
| 7 | + | //go:cgo_import_dynamic libc_free free "libSystem.dylib" | |
| 8 | + | //go:cgo_import_dynamic libc_setenv setenv "libSystem.dylib" | |
| 9 | + | //go:cgo_import_dynamic libc_unsetenv unsetenv "libSystem.dylib" | |
| 10 | + | //go:cgo_import_dynamic libc_pthread_attr_init pthread_attr_init "libSystem.dylib" | |
| 11 | + | //go:cgo_import_dynamic libc_pthread_create pthread_create "libSystem.dylib" | |
| 12 | + | //go:cgo_import_dynamic libc_pthread_detach pthread_detach "libSystem.dylib" | |
| 13 | + | //go:cgo_import_dynamic libc_pthread_attr_destroy pthread_attr_destroy "libSystem.dylib" | |
| 14 | + | //go:cgo_import_dynamic libc_pthread_attr_getstacksize pthread_attr_getstacksize "libSystem.dylib" | |
| 15 | + | //go:cgo_import_dynamic libc_abort abort "libSystem.dylib" | |
| 16 | + | //go:cgo_import_dynamic libc_sigfillset sigfillset "libSystem.dylib" | |
| 17 | + | //go:cgo_import_dynamic libc_nanosleep nanosleep "libSystem.dylib" | |
| @@ -0,0 +1,17 @@ | |||||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |||
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebitengine Authors | |||
| 3 | + | ||||
| 4 | + | package fakecgo | |||
| 5 | + | ||||
| 6 | + | //go:cgo_import_dynamic libc_malloc malloc "libSystem.dylib" | |||
| 7 | + | //go:cgo_import_dynamic libc_free free "libSystem.dylib" | |||
| 8 | + | //go:cgo_import_dynamic libc_setenv setenv "libSystem.dylib" | |||
| 9 | + | //go:cgo_import_dynamic libc_unsetenv unsetenv "libSystem.dylib" | |||
| 10 | + | //go:cgo_import_dynamic libc_pthread_attr_init pthread_attr_init "libSystem.dylib" | |||
| 11 | + | //go:cgo_import_dynamic libc_pthread_create pthread_create "libSystem.dylib" | |||
| 12 | + | //go:cgo_import_dynamic libc_pthread_detach pthread_detach "libSystem.dylib" | |||
| 13 | + | //go:cgo_import_dynamic libc_pthread_attr_destroy pthread_attr_destroy "libSystem.dylib" | |||
| 14 | + | //go:cgo_import_dynamic libc_pthread_attr_getstacksize pthread_attr_getstacksize "libSystem.dylib" | |||
| 15 | + | //go:cgo_import_dynamic libc_abort abort "libSystem.dylib" | |||
| 16 | + | //go:cgo_import_dynamic libc_sigfillset sigfillset "libSystem.dylib" | |||
| 17 | + | //go:cgo_import_dynamic libc_nanosleep nanosleep "libSystem.dylib" | |||
A internal/fakecgo/trampolines_amd64.s +150 -0
| @@ -0,0 +1,150 @@ | |||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebitengine Authors | |
| 3 | + | ||
| 4 | + | /* | |
| 5 | + | trampoline for emulating required C functions for cgo in go (see cgo.go) | |
| 6 | + | (we convert cdecl calling convention to go and vice-versa) | |
| 7 | + | ||
| 8 | + | Since we're called from go and call into C we can cheat a bit with the calling conventions: | |
| 9 | + | - in go all the registers are caller saved | |
| 10 | + | - in C we have a couple of callee saved registers | |
| 11 | + | ||
| 12 | + | => we can use BX, R12, R13, R14, R15 instead of the stack | |
| 13 | + | ||
| 14 | + | C Calling convention cdecl used here (we only need integer args): | |
| 15 | + | 1. arg: DI | |
| 16 | + | 2. arg: SI | |
| 17 | + | 3. arg: DX | |
| 18 | + | 4. arg: CX | |
| 19 | + | 5. arg: R8 | |
| 20 | + | 6. arg: R9 | |
| 21 | + | We don't need floats with these functions -> AX=0 | |
| 22 | + | return value will be in AX | |
| 23 | + | */ | |
| 24 | + | #include "textflag.h" | |
| 25 | + | #include "go_asm.h" | |
| 26 | + | ||
| 27 | + | // these trampolines map the gcc ABI to Go ABI and then calls into the Go equivalent functions. | |
| 28 | + | ||
| 29 | + | // TODO: put <> to make these private | |
| 30 | + | TEXT x_cgo_init_trampoline(SB), NOSPLIT, $16 | |
| 31 | + | MOVQ DI, 0(SP) | |
| 32 | + | MOVQ SI, 8(SP) | |
| 33 | + | CALL ·x_cgo_init(SB) | |
| 34 | + | RET | |
| 35 | + | ||
| 36 | + | TEXT x_cgo_thread_start_trampoline(SB), NOSPLIT, $8 | |
| 37 | + | MOVQ DI, 0(SP) | |
| 38 | + | CALL ·x_cgo_thread_start(SB) | |
| 39 | + | RET | |
| 40 | + | ||
| 41 | + | TEXT x_cgo_setenv_trampoline(SB), NOSPLIT, $8 | |
| 42 | + | MOVQ DI, 0(SP) | |
| 43 | + | CALL ·x_cgo_setenv(SB) | |
| 44 | + | RET | |
| 45 | + | ||
| 46 | + | TEXT x_cgo_unsetenv_trampoline(SB), NOSPLIT, $8 | |
| 47 | + | MOVQ DI, 0(SP) | |
| 48 | + | CALL ·x_cgo_unsetenv(SB) | |
| 49 | + | RET | |
| 50 | + | ||
| 51 | + | TEXT x_cgo_notify_runtime_init_done_trampoline(SB), NOSPLIT, $0 | |
| 52 | + | CALL ·x_cgo_notify_runtime_init_done(SB) | |
| 53 | + | RET | |
| 54 | + | ||
| 55 | + | // func setg_trampoline(setg uintptr, g uintptr) | |
| 56 | + | TEXT ·setg_trampoline(SB), NOSPLIT, $0-16 | |
| 57 | + | MOVQ G+8(FP), DI | |
| 58 | + | MOVQ setg+0(FP), AX | |
| 59 | + | CALL AX | |
| 60 | + | RET | |
| 61 | + | ||
| 62 | + | TEXT threadentry_trampoline(SB), NOSPLIT, $16 | |
| 63 | + | MOVQ DI, 0(SP) | |
| 64 | + | CALL ·threadentry(SB) | |
| 65 | + | MOVQ 8(SP), AX | |
| 66 | + | RET | |
| 67 | + | ||
| 68 | + | TEXT ·setenv(SB), NOSPLIT, $0-0 | |
| 69 | + | MOVQ name+0(FP), DI | |
| 70 | + | MOVQ value+8(FP), SI | |
| 71 | + | MOVL overwrite+16(FP), DX | |
| 72 | + | CALL libc_setenv(SB) | |
| 73 | + | MOVL AX, ret+24(FP) | |
| 74 | + | RET | |
| 75 | + | ||
| 76 | + | TEXT ·unsetenv(SB), NOSPLIT, $0-0 | |
| 77 | + | MOVQ name+0(FP), DI | |
| 78 | + | CALL libc_unsetenv(SB) | |
| 79 | + | MOVL AX, ret+8(FP) | |
| 80 | + | RET | |
| 81 | + | ||
| 82 | + | TEXT ·malloc(SB), NOSPLIT, $0-0 | |
| 83 | + | MOVQ size+0(FP), DI | |
| 84 | + | CALL libc_malloc(SB) | |
| 85 | + | MOVQ AX, ret+8(FP) | |
| 86 | + | RET | |
| 87 | + | ||
| 88 | + | TEXT ·free(SB), NOSPLIT, $0-0 | |
| 89 | + | MOVQ ptr+0(FP), DI | |
| 90 | + | CALL libc_free(SB) | |
| 91 | + | RET | |
| 92 | + | ||
| 93 | + | TEXT ·pthread_attr_init(SB), NOSPLIT, $0-12 | |
| 94 | + | MOVQ attr+0(FP), DI | |
| 95 | + | CALL libc_pthread_attr_init(SB) | |
| 96 | + | MOVL AX, ret+8(FP) | |
| 97 | + | RET | |
| 98 | + | ||
| 99 | + | TEXT ·pthread_detach(SB), NOSPLIT, $0-12 | |
| 100 | + | MOVQ thread+0(FP), DI | |
| 101 | + | CALL libc_pthread_detach(SB) | |
| 102 | + | MOVL AX, ret+8(FP) | |
| 103 | + | RET | |
| 104 | + | ||
| 105 | + | TEXT ·pthread_create(SB), NOSPLIT, $0-36 | |
| 106 | + | MOVQ thread+0(FP), DI | |
| 107 | + | MOVQ attr+8(FP), SI | |
| 108 | + | MOVQ start+16(FP), DX | |
| 109 | + | MOVQ arg+24(FP), CX | |
| 110 | + | CALL libc_pthread_create(SB) | |
| 111 | + | MOVL AX, ret+32(FP) | |
| 112 | + | RET | |
| 113 | + | ||
| 114 | + | TEXT ·pthread_attr_destroy(SB), NOSPLIT, $0-0 | |
| 115 | + | MOVQ attr+0(FP), DI | |
| 116 | + | CALL libc_pthread_attr_destroy(SB) | |
| 117 | + | MOVL AX, ret+8(FP) | |
| 118 | + | RET | |
| 119 | + | ||
| 120 | + | TEXT ·pthread_attr_getstacksize(SB), NOSPLIT, $0-0 | |
| 121 | + | MOVQ attr+0(FP), DI | |
| 122 | + | MOVQ stacksize+8(FP), SI | |
| 123 | + | CALL libc_pthread_attr_getstacksize(SB) | |
| 124 | + | MOVL AX, ret+16(FP) | |
| 125 | + | RET | |
| 126 | + | ||
| 127 | + | TEXT ·pthread_sigmask(SB), NOSPLIT, $0-0 | |
| 128 | + | MOVL how+0(FP), DI | |
| 129 | + | MOVQ ign+8(FP), SI | |
| 130 | + | MOVQ oset+16(FP), DX | |
| 131 | + | CALL libc_pthread_sigmask(SB) | |
| 132 | + | MOVL AX, ret+24(FP) | |
| 133 | + | RET | |
| 134 | + | ||
| 135 | + | TEXT ·abort(SB), NOSPLIT, $0-0 | |
| 136 | + | CALL libc_abort(SB) | |
| 137 | + | RET | |
| 138 | + | ||
| 139 | + | TEXT ·sigfillset(SB), NOSPLIT, $0-12 | |
| 140 | + | MOVQ set+0(FP), DI | |
| 141 | + | CALL libc_sigfillset(SB) | |
| 142 | + | MOVL AX, ret+8(FP) | |
| 143 | + | RET | |
| 144 | + | ||
| 145 | + | TEXT ·nanosleep(SB), NOSPLIT, $0-20 | |
| 146 | + | MOVQ ts+0(FP), DI | |
| 147 | + | MOVQ rem+8(FP), SI | |
| 148 | + | CALL libc_nanosleep(SB) | |
| 149 | + | MOVL AX, ret+16(FP) | |
| 150 | + | RET | |
| @@ -0,0 +1,150 @@ | |||||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |||
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebitengine Authors | |||
| 3 | + | ||||
| 4 | + | /* | |||
| 5 | + | trampoline for emulating required C functions for cgo in go (see cgo.go) | |||
| 6 | + | (we convert cdecl calling convention to go and vice-versa) | |||
| 7 | + | ||||
| 8 | + | Since we're called from go and call into C we can cheat a bit with the calling conventions: | |||
| 9 | + | - in go all the registers are caller saved | |||
| 10 | + | - in C we have a couple of callee saved registers | |||
| 11 | + | ||||
| 12 | + | => we can use BX, R12, R13, R14, R15 instead of the stack | |||
| 13 | + | ||||
| 14 | + | C Calling convention cdecl used here (we only need integer args): | |||
| 15 | + | 1. arg: DI | |||
| 16 | + | 2. arg: SI | |||
| 17 | + | 3. arg: DX | |||
| 18 | + | 4. arg: CX | |||
| 19 | + | 5. arg: R8 | |||
| 20 | + | 6. arg: R9 | |||
| 21 | + | We don't need floats with these functions -> AX=0 | |||
| 22 | + | return value will be in AX | |||
| 23 | + | */ | |||
| 24 | + | #include "textflag.h" | |||
| 25 | + | #include "go_asm.h" | |||
| 26 | + | ||||
| 27 | + | // these trampolines map the gcc ABI to Go ABI and then calls into the Go equivalent functions. | |||
| 28 | + | ||||
| 29 | + | // TODO: put <> to make these private | |||
| 30 | + | TEXT x_cgo_init_trampoline(SB), NOSPLIT, $16 | |||
| 31 | + | MOVQ DI, 0(SP) | |||
| 32 | + | MOVQ SI, 8(SP) | |||
| 33 | + | CALL ·x_cgo_init(SB) | |||
| 34 | + | RET | |||
| 35 | + | ||||
| 36 | + | TEXT x_cgo_thread_start_trampoline(SB), NOSPLIT, $8 | |||
| 37 | + | MOVQ DI, 0(SP) | |||
| 38 | + | CALL ·x_cgo_thread_start(SB) | |||
| 39 | + | RET | |||
| 40 | + | ||||
| 41 | + | TEXT x_cgo_setenv_trampoline(SB), NOSPLIT, $8 | |||
| 42 | + | MOVQ DI, 0(SP) | |||
| 43 | + | CALL ·x_cgo_setenv(SB) | |||
| 44 | + | RET | |||
| 45 | + | ||||
| 46 | + | TEXT x_cgo_unsetenv_trampoline(SB), NOSPLIT, $8 | |||
| 47 | + | MOVQ DI, 0(SP) | |||
| 48 | + | CALL ·x_cgo_unsetenv(SB) | |||
| 49 | + | RET | |||
| 50 | + | ||||
| 51 | + | TEXT x_cgo_notify_runtime_init_done_trampoline(SB), NOSPLIT, $0 | |||
| 52 | + | CALL ·x_cgo_notify_runtime_init_done(SB) | |||
| 53 | + | RET | |||
| 54 | + | ||||
| 55 | + | // func setg_trampoline(setg uintptr, g uintptr) | |||
| 56 | + | TEXT ·setg_trampoline(SB), NOSPLIT, $0-16 | |||
| 57 | + | MOVQ G+8(FP), DI | |||
| 58 | + | MOVQ setg+0(FP), AX | |||
| 59 | + | CALL AX | |||
| 60 | + | RET | |||
| 61 | + | ||||
| 62 | + | TEXT threadentry_trampoline(SB), NOSPLIT, $16 | |||
| 63 | + | MOVQ DI, 0(SP) | |||
| 64 | + | CALL ·threadentry(SB) | |||
| 65 | + | MOVQ 8(SP), AX | |||
| 66 | + | RET | |||
| 67 | + | ||||
| 68 | + | TEXT ·setenv(SB), NOSPLIT, $0-0 | |||
| 69 | + | MOVQ name+0(FP), DI | |||
| 70 | + | MOVQ value+8(FP), SI | |||
| 71 | + | MOVL overwrite+16(FP), DX | |||
| 72 | + | CALL libc_setenv(SB) | |||
| 73 | + | MOVL AX, ret+24(FP) | |||
| 74 | + | RET | |||
| 75 | + | ||||
| 76 | + | TEXT ·unsetenv(SB), NOSPLIT, $0-0 | |||
| 77 | + | MOVQ name+0(FP), DI | |||
| 78 | + | CALL libc_unsetenv(SB) | |||
| 79 | + | MOVL AX, ret+8(FP) | |||
| 80 | + | RET | |||
| 81 | + | ||||
| 82 | + | TEXT ·malloc(SB), NOSPLIT, $0-0 | |||
| 83 | + | MOVQ size+0(FP), DI | |||
| 84 | + | CALL libc_malloc(SB) | |||
| 85 | + | MOVQ AX, ret+8(FP) | |||
| 86 | + | RET | |||
| 87 | + | ||||
| 88 | + | TEXT ·free(SB), NOSPLIT, $0-0 | |||
| 89 | + | MOVQ ptr+0(FP), DI | |||
| 90 | + | CALL libc_free(SB) | |||
| 91 | + | RET | |||
| 92 | + | ||||
| 93 | + | TEXT ·pthread_attr_init(SB), NOSPLIT, $0-12 | |||
| 94 | + | MOVQ attr+0(FP), DI | |||
| 95 | + | CALL libc_pthread_attr_init(SB) | |||
| 96 | + | MOVL AX, ret+8(FP) | |||
| 97 | + | RET | |||
| 98 | + | ||||
| 99 | + | TEXT ·pthread_detach(SB), NOSPLIT, $0-12 | |||
| 100 | + | MOVQ thread+0(FP), DI | |||
| 101 | + | CALL libc_pthread_detach(SB) | |||
| 102 | + | MOVL AX, ret+8(FP) | |||
| 103 | + | RET | |||
| 104 | + | ||||
| 105 | + | TEXT ·pthread_create(SB), NOSPLIT, $0-36 | |||
| 106 | + | MOVQ thread+0(FP), DI | |||
| 107 | + | MOVQ attr+8(FP), SI | |||
| 108 | + | MOVQ start+16(FP), DX | |||
| 109 | + | MOVQ arg+24(FP), CX | |||
| 110 | + | CALL libc_pthread_create(SB) | |||
| 111 | + | MOVL AX, ret+32(FP) | |||
| 112 | + | RET | |||
| 113 | + | ||||
| 114 | + | TEXT ·pthread_attr_destroy(SB), NOSPLIT, $0-0 | |||
| 115 | + | MOVQ attr+0(FP), DI | |||
| 116 | + | CALL libc_pthread_attr_destroy(SB) | |||
| 117 | + | MOVL AX, ret+8(FP) | |||
| 118 | + | RET | |||
| 119 | + | ||||
| 120 | + | TEXT ·pthread_attr_getstacksize(SB), NOSPLIT, $0-0 | |||
| 121 | + | MOVQ attr+0(FP), DI | |||
| 122 | + | MOVQ stacksize+8(FP), SI | |||
| 123 | + | CALL libc_pthread_attr_getstacksize(SB) | |||
| 124 | + | MOVL AX, ret+16(FP) | |||
| 125 | + | RET | |||
| 126 | + | ||||
| 127 | + | TEXT ·pthread_sigmask(SB), NOSPLIT, $0-0 | |||
| 128 | + | MOVL how+0(FP), DI | |||
| 129 | + | MOVQ ign+8(FP), SI | |||
| 130 | + | MOVQ oset+16(FP), DX | |||
| 131 | + | CALL libc_pthread_sigmask(SB) | |||
| 132 | + | MOVL AX, ret+24(FP) | |||
| 133 | + | RET | |||
| 134 | + | ||||
| 135 | + | TEXT ·abort(SB), NOSPLIT, $0-0 | |||
| 136 | + | CALL libc_abort(SB) | |||
| 137 | + | RET | |||
| 138 | + | ||||
| 139 | + | TEXT ·sigfillset(SB), NOSPLIT, $0-12 | |||
| 140 | + | MOVQ set+0(FP), DI | |||
| 141 | + | CALL libc_sigfillset(SB) | |||
| 142 | + | MOVL AX, ret+8(FP) | |||
| 143 | + | RET | |||
| 144 | + | ||||
| 145 | + | TEXT ·nanosleep(SB), NOSPLIT, $0-20 | |||
| 146 | + | MOVQ ts+0(FP), DI | |||
| 147 | + | MOVQ rem+8(FP), SI | |||
| 148 | + | CALL libc_nanosleep(SB) | |||
| 149 | + | MOVL AX, ret+16(FP) | |||
| 150 | + | RET | |||
A internal/fakecgo/trampolines_arm64.s +130 -0
| @@ -0,0 +1,130 @@ | |||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebitengine Authors | |
| 3 | + | ||
| 4 | + | #include "textflag.h" | |
| 5 | + | #include "go_asm.h" | |
| 6 | + | ||
| 7 | + | // these trampolines map the gcc ABI to Go ABI and then calls into the Go equivalent functions. | |
| 8 | + | ||
| 9 | + | // TODO: put <> to make these private | |
| 10 | + | TEXT x_cgo_init_trampoline(SB), NOSPLIT, $0-0 | |
| 11 | + | MOVD R0, 8(RSP) | |
| 12 | + | MOVD R1, 16(RSP) | |
| 13 | + | CALL ·x_cgo_init(SB) | |
| 14 | + | RET | |
| 15 | + | ||
| 16 | + | TEXT x_cgo_thread_start_trampoline(SB), NOSPLIT, $0-0 | |
| 17 | + | MOVD R0, 8(RSP) | |
| 18 | + | CALL ·x_cgo_thread_start(SB) | |
| 19 | + | RET | |
| 20 | + | ||
| 21 | + | TEXT x_cgo_setenv_trampoline(SB), NOSPLIT, $0-0 | |
| 22 | + | MOVD R0, 8(RSP) | |
| 23 | + | CALL ·x_cgo_setenv(SB) | |
| 24 | + | RET | |
| 25 | + | ||
| 26 | + | TEXT x_cgo_unsetenv_trampoline(SB), NOSPLIT, $0-0 | |
| 27 | + | MOVD R0, 8(RSP) | |
| 28 | + | CALL ·x_cgo_unsetenv(SB) | |
| 29 | + | RET | |
| 30 | + | ||
| 31 | + | TEXT x_cgo_notify_runtime_init_done_trampoline(SB), NOSPLIT, $0-0 | |
| 32 | + | CALL ·x_cgo_notify_runtime_init_done(SB) | |
| 33 | + | RET | |
| 34 | + | ||
| 35 | + | // func setg_trampoline(setg uintptr, g uintptr) | |
| 36 | + | TEXT ·setg_trampoline(SB), NOSPLIT, $0-16 | |
| 37 | + | MOVD G+8(FP), R0 | |
| 38 | + | MOVD setg+0(FP), R1 | |
| 39 | + | CALL R1 | |
| 40 | + | RET | |
| 41 | + | ||
| 42 | + | TEXT threadentry_trampoline(SB), NOSPLIT, $0-0 | |
| 43 | + | MOVD R0, 8(RSP) | |
| 44 | + | CALL ·threadentry(SB) | |
| 45 | + | MOVD $0, R0 // TODO: get the return value from threadentry | |
| 46 | + | RET | |
| 47 | + | ||
| 48 | + | TEXT ·setenv(SB), NOSPLIT, $0-0 | |
| 49 | + | MOVD name+0(FP), R0 | |
| 50 | + | MOVD value+8(FP), R1 | |
| 51 | + | MOVD overwrite+16(FP), R2 | |
| 52 | + | CALL libc_setenv(SB) | |
| 53 | + | MOVD R0, ret+24(FP) | |
| 54 | + | RET | |
| 55 | + | ||
| 56 | + | TEXT ·unsetenv(SB), NOSPLIT, $0-0 | |
| 57 | + | MOVD name+0(FP), R0 | |
| 58 | + | CALL libc_unsetenv(SB) | |
| 59 | + | MOVD R0, ret+8(FP) | |
| 60 | + | RET | |
| 61 | + | ||
| 62 | + | TEXT ·malloc(SB), NOSPLIT, $0-0 | |
| 63 | + | MOVD size+0(FP), R0 | |
| 64 | + | CALL libc_malloc(SB) | |
| 65 | + | MOVD R0, ret+8(FP) | |
| 66 | + | RET | |
| 67 | + | ||
| 68 | + | TEXT ·free(SB), NOSPLIT, $0-0 | |
| 69 | + | MOVD ptr+0(FP), R0 | |
| 70 | + | CALL libc_free(SB) | |
| 71 | + | RET | |
| 72 | + | ||
| 73 | + | TEXT ·pthread_attr_init(SB), NOSPLIT, $0-12 | |
| 74 | + | MOVD attr+0(FP), R0 | |
| 75 | + | CALL libc_pthread_attr_init(SB) | |
| 76 | + | MOVD R0, ret+8(FP) | |
| 77 | + | RET | |
| 78 | + | ||
| 79 | + | TEXT ·pthread_detach(SB), NOSPLIT, $0-12 | |
| 80 | + | MOVD thread+0(FP), R0 | |
| 81 | + | CALL libc_pthread_detach(SB) | |
| 82 | + | MOVD R0, ret+8(FP) | |
| 83 | + | RET | |
| 84 | + | ||
| 85 | + | TEXT ·pthread_create(SB), NOSPLIT, $0-36 | |
| 86 | + | MOVD thread+0(FP), R0 | |
| 87 | + | MOVD attr+8(FP), R1 | |
| 88 | + | MOVD start+16(FP), R2 | |
| 89 | + | MOVD arg+24(FP), R3 | |
| 90 | + | CALL libc_pthread_create(SB) | |
| 91 | + | MOVD R0, ret+32(FP) | |
| 92 | + | RET | |
| 93 | + | ||
| 94 | + | TEXT ·pthread_attr_destroy(SB), NOSPLIT, $0-0 | |
| 95 | + | MOVD attr+0(FP), R0 | |
| 96 | + | CALL libc_pthread_attr_destroy(SB) | |
| 97 | + | MOVD R0, ret+8(FP) | |
| 98 | + | RET | |
| 99 | + | ||
| 100 | + | TEXT ·pthread_attr_getstacksize(SB), NOSPLIT, $0-0 | |
| 101 | + | MOVD attr+0(FP), R0 | |
| 102 | + | MOVD stacksize+8(FP), R1 | |
| 103 | + | CALL libc_pthread_attr_getstacksize(SB) | |
| 104 | + | MOVD R0, ret+16(FP) | |
| 105 | + | RET | |
| 106 | + | ||
| 107 | + | TEXT ·pthread_sigmask(SB), NOSPLIT, $0-0 | |
| 108 | + | MOVD how+0(FP), R0 | |
| 109 | + | MOVD ign+8(FP), R1 | |
| 110 | + | MOVD oset+16(FP), R2 | |
| 111 | + | CALL libc_pthread_sigmask(SB) | |
| 112 | + | MOVD R0, ret+24(FP) | |
| 113 | + | RET | |
| 114 | + | ||
| 115 | + | TEXT ·abort(SB), NOSPLIT, $0-0 | |
| 116 | + | CALL libc_abort(SB) | |
| 117 | + | RET | |
| 118 | + | ||
| 119 | + | TEXT ·sigfillset(SB), NOSPLIT, $0-12 | |
| 120 | + | MOVD set+0(FP), R0 | |
| 121 | + | CALL libc_sigfillset(SB) | |
| 122 | + | MOVD R0, ret+8(FP) | |
| 123 | + | RET | |
| 124 | + | ||
| 125 | + | TEXT ·nanosleep(SB), NOSPLIT, $0-20 | |
| 126 | + | MOVD ts+0(FP), R0 | |
| 127 | + | MOVD rem+8(FP), R1 | |
| 128 | + | CALL libc_nanosleep(SB) | |
| 129 | + | MOVD R0, ret+16(FP) | |
| 130 | + | RET | |
| @@ -0,0 +1,130 @@ | |||||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |||
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebitengine Authors | |||
| 3 | + | ||||
| 4 | + | #include "textflag.h" | |||
| 5 | + | #include "go_asm.h" | |||
| 6 | + | ||||
| 7 | + | // these trampolines map the gcc ABI to Go ABI and then calls into the Go equivalent functions. | |||
| 8 | + | ||||
| 9 | + | // TODO: put <> to make these private | |||
| 10 | + | TEXT x_cgo_init_trampoline(SB), NOSPLIT, $0-0 | |||
| 11 | + | MOVD R0, 8(RSP) | |||
| 12 | + | MOVD R1, 16(RSP) | |||
| 13 | + | CALL ·x_cgo_init(SB) | |||
| 14 | + | RET | |||
| 15 | + | ||||
| 16 | + | TEXT x_cgo_thread_start_trampoline(SB), NOSPLIT, $0-0 | |||
| 17 | + | MOVD R0, 8(RSP) | |||
| 18 | + | CALL ·x_cgo_thread_start(SB) | |||
| 19 | + | RET | |||
| 20 | + | ||||
| 21 | + | TEXT x_cgo_setenv_trampoline(SB), NOSPLIT, $0-0 | |||
| 22 | + | MOVD R0, 8(RSP) | |||
| 23 | + | CALL ·x_cgo_setenv(SB) | |||
| 24 | + | RET | |||
| 25 | + | ||||
| 26 | + | TEXT x_cgo_unsetenv_trampoline(SB), NOSPLIT, $0-0 | |||
| 27 | + | MOVD R0, 8(RSP) | |||
| 28 | + | CALL ·x_cgo_unsetenv(SB) | |||
| 29 | + | RET | |||
| 30 | + | ||||
| 31 | + | TEXT x_cgo_notify_runtime_init_done_trampoline(SB), NOSPLIT, $0-0 | |||
| 32 | + | CALL ·x_cgo_notify_runtime_init_done(SB) | |||
| 33 | + | RET | |||
| 34 | + | ||||
| 35 | + | // func setg_trampoline(setg uintptr, g uintptr) | |||
| 36 | + | TEXT ·setg_trampoline(SB), NOSPLIT, $0-16 | |||
| 37 | + | MOVD G+8(FP), R0 | |||
| 38 | + | MOVD setg+0(FP), R1 | |||
| 39 | + | CALL R1 | |||
| 40 | + | RET | |||
| 41 | + | ||||
| 42 | + | TEXT threadentry_trampoline(SB), NOSPLIT, $0-0 | |||
| 43 | + | MOVD R0, 8(RSP) | |||
| 44 | + | CALL ·threadentry(SB) | |||
| 45 | + | MOVD $0, R0 // TODO: get the return value from threadentry | |||
| 46 | + | RET | |||
| 47 | + | ||||
| 48 | + | TEXT ·setenv(SB), NOSPLIT, $0-0 | |||
| 49 | + | MOVD name+0(FP), R0 | |||
| 50 | + | MOVD value+8(FP), R1 | |||
| 51 | + | MOVD overwrite+16(FP), R2 | |||
| 52 | + | CALL libc_setenv(SB) | |||
| 53 | + | MOVD R0, ret+24(FP) | |||
| 54 | + | RET | |||
| 55 | + | ||||
| 56 | + | TEXT ·unsetenv(SB), NOSPLIT, $0-0 | |||
| 57 | + | MOVD name+0(FP), R0 | |||
| 58 | + | CALL libc_unsetenv(SB) | |||
| 59 | + | MOVD R0, ret+8(FP) | |||
| 60 | + | RET | |||
| 61 | + | ||||
| 62 | + | TEXT ·malloc(SB), NOSPLIT, $0-0 | |||
| 63 | + | MOVD size+0(FP), R0 | |||
| 64 | + | CALL libc_malloc(SB) | |||
| 65 | + | MOVD R0, ret+8(FP) | |||
| 66 | + | RET | |||
| 67 | + | ||||
| 68 | + | TEXT ·free(SB), NOSPLIT, $0-0 | |||
| 69 | + | MOVD ptr+0(FP), R0 | |||
| 70 | + | CALL libc_free(SB) | |||
| 71 | + | RET | |||
| 72 | + | ||||
| 73 | + | TEXT ·pthread_attr_init(SB), NOSPLIT, $0-12 | |||
| 74 | + | MOVD attr+0(FP), R0 | |||
| 75 | + | CALL libc_pthread_attr_init(SB) | |||
| 76 | + | MOVD R0, ret+8(FP) | |||
| 77 | + | RET | |||
| 78 | + | ||||
| 79 | + | TEXT ·pthread_detach(SB), NOSPLIT, $0-12 | |||
| 80 | + | MOVD thread+0(FP), R0 | |||
| 81 | + | CALL libc_pthread_detach(SB) | |||
| 82 | + | MOVD R0, ret+8(FP) | |||
| 83 | + | RET | |||
| 84 | + | ||||
| 85 | + | TEXT ·pthread_create(SB), NOSPLIT, $0-36 | |||
| 86 | + | MOVD thread+0(FP), R0 | |||
| 87 | + | MOVD attr+8(FP), R1 | |||
| 88 | + | MOVD start+16(FP), R2 | |||
| 89 | + | MOVD arg+24(FP), R3 | |||
| 90 | + | CALL libc_pthread_create(SB) | |||
| 91 | + | MOVD R0, ret+32(FP) | |||
| 92 | + | RET | |||
| 93 | + | ||||
| 94 | + | TEXT ·pthread_attr_destroy(SB), NOSPLIT, $0-0 | |||
| 95 | + | MOVD attr+0(FP), R0 | |||
| 96 | + | CALL libc_pthread_attr_destroy(SB) | |||
| 97 | + | MOVD R0, ret+8(FP) | |||
| 98 | + | RET | |||
| 99 | + | ||||
| 100 | + | TEXT ·pthread_attr_getstacksize(SB), NOSPLIT, $0-0 | |||
| 101 | + | MOVD attr+0(FP), R0 | |||
| 102 | + | MOVD stacksize+8(FP), R1 | |||
| 103 | + | CALL libc_pthread_attr_getstacksize(SB) | |||
| 104 | + | MOVD R0, ret+16(FP) | |||
| 105 | + | RET | |||
| 106 | + | ||||
| 107 | + | TEXT ·pthread_sigmask(SB), NOSPLIT, $0-0 | |||
| 108 | + | MOVD how+0(FP), R0 | |||
| 109 | + | MOVD ign+8(FP), R1 | |||
| 110 | + | MOVD oset+16(FP), R2 | |||
| 111 | + | CALL libc_pthread_sigmask(SB) | |||
| 112 | + | MOVD R0, ret+24(FP) | |||
| 113 | + | RET | |||
| 114 | + | ||||
| 115 | + | TEXT ·abort(SB), NOSPLIT, $0-0 | |||
| 116 | + | CALL libc_abort(SB) | |||
| 117 | + | RET | |||
| 118 | + | ||||
| 119 | + | TEXT ·sigfillset(SB), NOSPLIT, $0-12 | |||
| 120 | + | MOVD set+0(FP), R0 | |||
| 121 | + | CALL libc_sigfillset(SB) | |||
| 122 | + | MOVD R0, ret+8(FP) | |||
| 123 | + | RET | |||
| 124 | + | ||||
| 125 | + | TEXT ·nanosleep(SB), NOSPLIT, $0-20 | |||
| 126 | + | MOVD ts+0(FP), R0 | |||
| 127 | + | MOVD rem+8(FP), R1 | |||
| 128 | + | CALL libc_nanosleep(SB) | |||
| 129 | + | MOVD R0, ret+16(FP) | |||
| 130 | + | RET | |||
A isios_darwin.go +14 -0
| @@ -0,0 +1,14 @@ | |||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebitengine Authors | |
| 3 | + | ||
| 4 | + | //go:build !cgo && ios | |
| 5 | + | // +build !cgo,ios | |
| 6 | + | ||
| 7 | + | package purego | |
| 8 | + | ||
| 9 | + | // if you are getting this error it means that you have | |
| 10 | + | // CGO_ENABLED=0 while trying to build for ios. | |
| 11 | + | // purego does not support this mode yet. | |
| 12 | + | // the fix is to set CGO_ENABLED=1 which will require | |
| 13 | + | // a C compiler. | |
| 14 | + | var _ = _PUREGO_REQUIRES_CGO_ON_IOS | |
| @@ -0,0 +1,14 @@ | |||||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |||
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebitengine Authors | |||
| 3 | + | ||||
| 4 | + | //go:build !cgo && ios | |||
| 5 | + | // +build !cgo,ios | |||
| 6 | + | ||||
| 7 | + | package purego | |||
| 8 | + | ||||
| 9 | + | // if you are getting this error it means that you have | |||
| 10 | + | // CGO_ENABLED=0 while trying to build for ios. | |||
| 11 | + | // purego does not support this mode yet. | |||
| 12 | + | // the fix is to set CGO_ENABLED=1 which will require | |||
| 13 | + | // a C compiler. | |||
| 14 | + | var _ = _PUREGO_REQUIRES_CGO_ON_IOS | |||
A nocgo_darwin.go +26 -0
| @@ -0,0 +1,26 @@ | |||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebitengine Authors | |
| 3 | + | ||
| 4 | + | //go:build !cgo | |
| 5 | + | // +build !cgo | |
| 6 | + | ||
| 7 | + | package purego | |
| 8 | + | ||
| 9 | + | // if CGO_ENABLED=0 import fakecgo to setup the Cgo runtime correctly. | |
| 10 | + | // This is required since some frameworks need TLS setup the C way which Go doesn't do. | |
| 11 | + | // We currently don't support ios in fakecgo mode so force Cgo or fail | |
| 12 | + | // | |
| 13 | + | // The way that the Cgo runtime (runtime/cgo) works is by setting some variables found | |
| 14 | + | // in runtime with non-null GCC compiled functions. The variables that are replaced are | |
| 15 | + | // var ( | |
| 16 | + | // iscgo bool // in runtime/cgo.go | |
| 17 | + | // _cgo_init unsafe.Pointer // in runtime/cgo.go | |
| 18 | + | // _cgo_thread_start unsafe.Pointer // in runtime/cgo.go | |
| 19 | + | // _cgo_notify_runtime_init_done unsafe.Pointer // in runtime/cgo.go | |
| 20 | + | // _cgo_setenv unsafe.Pointer // in runtime/env_posix.go | |
| 21 | + | // _cgo_unsetenv unsafe.Pointer // in runtime/env_posix.go | |
| 22 | + | // ) | |
| 23 | + | // importing fakecgo will set these (using //go:linkname) with functions written | |
| 24 | + | // entirely in Go (except for some assembly trampolines to change GCC ABI to Go ABI). | |
| 25 | + | // Doing so makes it possible to build applications that call into C without CGO_ENABLED=1. | |
| 26 | + | import _ "github.com/ebitengine/purego/internal/fakecgo" | |
| @@ -0,0 +1,26 @@ | |||||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |||
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebitengine Authors | |||
| 3 | + | ||||
| 4 | + | //go:build !cgo | |||
| 5 | + | // +build !cgo | |||
| 6 | + | ||||
| 7 | + | package purego | |||
| 8 | + | ||||
| 9 | + | // if CGO_ENABLED=0 import fakecgo to setup the Cgo runtime correctly. | |||
| 10 | + | // This is required since some frameworks need TLS setup the C way which Go doesn't do. | |||
| 11 | + | // We currently don't support ios in fakecgo mode so force Cgo or fail | |||
| 12 | + | // | |||
| 13 | + | // The way that the Cgo runtime (runtime/cgo) works is by setting some variables found | |||
| 14 | + | // in runtime with non-null GCC compiled functions. The variables that are replaced are | |||
| 15 | + | // var ( | |||
| 16 | + | // iscgo bool // in runtime/cgo.go | |||
| 17 | + | // _cgo_init unsafe.Pointer // in runtime/cgo.go | |||
| 18 | + | // _cgo_thread_start unsafe.Pointer // in runtime/cgo.go | |||
| 19 | + | // _cgo_notify_runtime_init_done unsafe.Pointer // in runtime/cgo.go | |||
| 20 | + | // _cgo_setenv unsafe.Pointer // in runtime/env_posix.go | |||
| 21 | + | // _cgo_unsetenv unsafe.Pointer // in runtime/env_posix.go | |||
| 22 | + | // ) | |||
| 23 | + | // importing fakecgo will set these (using //go:linkname) with functions written | |||
| 24 | + | // entirely in Go (except for some assembly trampolines to change GCC ABI to Go ABI). | |||
| 25 | + | // Doing so makes it possible to build applications that call into C without CGO_ENABLED=1. | |||
| 26 | + | import _ "github.com/ebitengine/purego/internal/fakecgo" | |||
M objc/objc_runtime_darwin_test.go +0 -9
| @@ -5,21 +5,12 @@ package objc_test | |||
| 5 | 5 | ||
| 6 | 6 | import ( | |
| 7 | 7 | "fmt" | |
| 8 | - | "runtime" | |
| 9 | 8 | "unsafe" | |
| 10 | 9 | ||
| 11 | 10 | "github.com/ebitengine/purego" | |
| 12 | 11 | "github.com/ebitengine/purego/objc" | |
| 13 | 12 | ) | |
| 14 | 13 | ||
| 15 | - | func init() { | |
| 16 | - | // this is here so that the program doesn't crash with: | |
| 17 | - | // fatal error: schedule: in cgo | |
| 18 | - | // this can be removed after purego#12 is solved. | |
| 19 | - | // this is due to moving goroutines to different threads while in C code | |
| 20 | - | runtime.LockOSThread() | |
| 21 | - | } | |
| 22 | - | ||
| 23 | 14 | func ExampleAllocateClassPair() { | |
| 24 | 15 | var class = objc.AllocateClassPair(objc.GetClass("NSObject"), "FooObject", 0) | |
| 25 | 16 | class.AddMethod(objc.RegisterName("run"), objc.NewIMP(func(self objc.ID, _cmd objc.SEL) { | |
| @@ -5,21 +5,12 @@ package objc_test | |||||
| 5 | 5 | ||||
| 6 | import ( | 6 | import ( | ||
| 7 | "fmt" | 7 | "fmt" | ||
| 8 | - | "runtime" | |||
| 9 | "unsafe" | 8 | "unsafe" | ||
| 10 | 9 | ||||
| 11 | "github.com/ebitengine/purego" | 10 | "github.com/ebitengine/purego" | ||
| 12 | "github.com/ebitengine/purego/objc" | 11 | "github.com/ebitengine/purego/objc" | ||
| 13 | ) | 12 | ) | ||
| 14 | 13 | ||||
| 15 | - | func init() { | |||
| 16 | - | // this is here so that the program doesn't crash with: | |||
| 17 | - | // fatal error: schedule: in cgo | |||
| 18 | - | // this can be removed after purego#12 is solved. | |||
| 19 | - | // this is due to moving goroutines to different threads while in C code | |||
| 20 | - | runtime.LockOSThread() | |||
| 21 | - | } | |||
| 22 | - | ||||
| 23 | func ExampleAllocateClassPair() { | 14 | func ExampleAllocateClassPair() { | ||
| 24 | var class = objc.AllocateClassPair(objc.GetClass("NSObject"), "FooObject", 0) | 15 | var class = objc.AllocateClassPair(objc.GetClass("NSObject"), "FooObject", 0) | ||
| 25 | class.AddMethod(objc.RegisterName("run"), objc.NewIMP(func(self objc.ID, _cmd objc.SEL) { | 16 | class.AddMethod(objc.RegisterName("run"), objc.NewIMP(func(self objc.ID, _cmd objc.SEL) { | ||
M syscall_darwin.go +2 -8
| @@ -10,12 +10,6 @@ import ( | |||
| 10 | 10 | "unsafe" | |
| 11 | 11 | ) | |
| 12 | 12 | ||
| 13 | - | func callc(fn uintptr, args unsafe.Pointer) { | |
| 14 | - | runtime_entersyscall() | |
| 15 | - | runtime_libcCall(fn, args) | |
| 16 | - | runtime_exitsyscall() | |
| 17 | - | } | |
| 18 | - | ||
| 19 | 13 | var syscall9XABI0 uintptr | |
| 20 | 14 | ||
| 21 | 15 | type syscall9Args struct{ fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, r1, r2, err uintptr } | |
| @@ -23,11 +17,11 @@ type syscall9Args struct{ fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, r1, r2, err ui | |||
| 23 | 17 | //go:nosplit | |
| 24 | 18 | func syscall_syscall9X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr) { | |
| 25 | 19 | args := syscall9Args{fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, r1, r2, err} | |
| 26 | - | callc(syscall9XABI0, unsafe.Pointer(&args)) | |
| 20 | + | runtime_cgocall(syscall9XABI0, unsafe.Pointer(&args)) | |
| 27 | 21 | return args.r1, args.r2, args.err | |
| 28 | 22 | } | |
| 29 | 23 | ||
| 30 | - | // NewCallback converts a Go function to a function pointer conforming to the C calling convention | |
| 24 | + | // NewCallback converts a Go function to a function pointer conforming to the C calling convention. | |
| 31 | 25 | // This is useful when interoperating with C code requiring callbacks. The argument is expected to be a | |
| 32 | 26 | // function with zero or one uintptr-sized result. The function must not have arguments with size larger than the size | |
| 33 | 27 | // of uintptr. Only a limited number of callbacks may be created in a single Go process, and any memory allocated | |
| @@ -10,12 +10,6 @@ import ( | |||||
| 10 | "unsafe" | 10 | "unsafe" | ||
| 11 | ) | 11 | ) | ||
| 12 | 12 | ||||
| 13 | - | func callc(fn uintptr, args unsafe.Pointer) { | |||
| 14 | - | runtime_entersyscall() | |||
| 15 | - | runtime_libcCall(fn, args) | |||
| 16 | - | runtime_exitsyscall() | |||
| 17 | - | } | |||
| 18 | - | ||||
| 19 | var syscall9XABI0 uintptr | 13 | var syscall9XABI0 uintptr | ||
| 20 | 14 | ||||
| 21 | type syscall9Args struct{ fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, r1, r2, err uintptr } | 15 | type syscall9Args struct{ fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, r1, r2, err uintptr } | ||
| @@ -23,11 +17,11 @@ type syscall9Args struct{ fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, r1, r2, err ui | |||||
| 23 | //go:nosplit | 17 | //go:nosplit | ||
| 24 | func syscall_syscall9X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr) { | 18 | func syscall_syscall9X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr) { | ||
| 25 | args := syscall9Args{fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, r1, r2, err} | 19 | args := syscall9Args{fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, r1, r2, err} | ||
| 26 | - | callc(syscall9XABI0, unsafe.Pointer(&args)) | 20 | + | runtime_cgocall(syscall9XABI0, unsafe.Pointer(&args)) |
| 27 | return args.r1, args.r2, args.err | 21 | return args.r1, args.r2, args.err | ||
| 28 | } | 22 | } | ||
| 29 | 23 | ||||
| 30 | - | // NewCallback converts a Go function to a function pointer conforming to the C calling convention | 24 | + | // NewCallback converts a Go function to a function pointer conforming to the C calling convention. |
| 31 | // This is useful when interoperating with C code requiring callbacks. The argument is expected to be a | 25 | // This is useful when interoperating with C code requiring callbacks. The argument is expected to be a | ||
| 32 | // function with zero or one uintptr-sized result. The function must not have arguments with size larger than the size | 26 | // function with zero or one uintptr-sized result. The function must not have arguments with size larger than the size | ||
| 33 | // of uintptr. Only a limited number of callbacks may be created in a single Go process, and any memory allocated | 27 | // of uintptr. Only a limited number of callbacks may be created in a single Go process, and any memory allocated | ||