detached c1f3f96

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 Unverified4AEE18F83AFDEB23 The 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
1010 This is a list of the copied files:
1111
1212 * `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 (
1010 "unsafe"
1111 )
1212
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.libcCall13+//go:linkname runtime_cgocall runtime.cgocall
14-//go:linkname runtime_entersyscall runtime.entersyscall14+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
55
66 import (
77 "fmt"
8- "runtime"
98 "unsafe"
109
1110 "github.com/ebitengine/purego"
1211 "github.com/ebitengine/purego/objc"
1312 )
1413
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-
2314 func ExampleAllocateClassPair() {
2415 var class = objc.AllocateClassPair(objc.GetClass("NSObject"), "FooObject", 0)
2516 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 (
1010 "unsafe"
1111 )
1212
13-func callc(fn uintptr, args unsafe.Pointer) {
14- runtime_entersyscall()
15- runtime_libcCall(fn, args)
16- runtime_exitsyscall()
17-}
18-
1913 var syscall9XABI0 uintptr
2014
2115 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
2317 //go:nosplit
2418 func syscall_syscall9X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr) {
2519 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))
2721 return args.r1, args.r2, args.err
2822 }
2923
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.
3125 // This is useful when interoperating with C code requiring callbacks. The argument is expected to be a
3226 // function with zero or one uintptr-sized result. The function must not have arguments with size larger than the size
3327 // 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 uintptr13 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:nosplit17 //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.err21 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 convention24+// 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 a25 // 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 size26 // 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 allocated27 // of uintptr. Only a limited number of callbacks may be created in a single Go process, and any memory allocated

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 102

↑↓ moveenter openesc close