detached fcdb024

internal/fakecgo: circumvent Go 1.20's race code (#73)

Go 1.20 rewrote the race detector on darwin to be entirely Go. This means
that fakecgo had race functions placed inside of it which made it break.
This PR circumvents the race functions by adding //go:norace and using
closures to call the ABIInternal ABIWrapper which does not have the race
instrumentation.

Closes #71
commit
fcdb0240d06c471170813a06ef8f9c00df24e108
author
TotallyGamerJet · · 2022-12-02 03:15 UTC
committer
Hajime Hoshi · · 2022-12-02 15:58 UTC
parent
7ec80ca
signature
unsigned
9 files · +73 -19
M example/main.go +11 -1
@@ -6,9 +6,19 @@
66 package main
77
88 import (
9+ "os"
10+
911 _ "github.com/ebitengine/purego"
1012 )
1113
1214 func main() {
13- // TODO: Add an implementation. This example exists only for testing so far.
15+ // set and unset an environment variable since this calls into fakecgo.
16+ err := os.Setenv("TESTING", "SOMETHING")
17+ if err != nil {
18+ panic(err)
19+ }
20+ err = os.Unsetenv("TESTING")
21+ if err != nil {
22+ panic(err)
23+ }
1424 }
@@ -6,9 +6,19 @@
6 package main6 package main
7 7
8 import (8 import (
9+ "os"
10+
9 _ "github.com/ebitengine/purego"11 _ "github.com/ebitengine/purego"
10 )12 )
11 13
12 func main() {14 func main() {
13- // TODO: Add an implementation. This example exists only for testing so far.15+ // set and unset an environment variable since this calls into fakecgo.
16+ err := os.Setenv("TESTING", "SOMETHING")
17+ if err != nil {
18+ panic(err)
19+ }
20+ err = os.Unsetenv("TESTING")
21+ if err != nil {
22+ panic(err)
23+ }
14 }24 }
M internal/fakecgo/callbacks.go +15 -0
@@ -41,3 +41,18 @@ var _cgo_notify_runtime_init_done = &x_cgo_notify_runtime_init_done_trampoline
4141
4242 // TODO: decide if we need x_cgo_set_context_function
4343 // TODO: decide if we need _cgo_yield
44+
45+var (
46+ // In Go 1.20 the race detector was rewritten to pure Go
47+ // on darwin. This means that when CGO_ENABLED=0 is set
48+ // fakecgo is built with race detector code. This is not
49+ // good since this code is pretending to be C. The
50+ // go:norace comments are not enough since the ABI wrappers
51+ // still have race code. These variables will circumvent
52+ // that issue by calling the function directly.
53+ threadentry_call = threadentry
54+ x_cgo_init_call = x_cgo_init
55+ x_cgo_setenv_call = x_cgo_setenv
56+ x_cgo_unsetenv_call = x_cgo_unsetenv
57+ x_cgo_thread_start_call = x_cgo_thread_start
58+)
@@ -41,3 +41,18 @@ var _cgo_notify_runtime_init_done = &x_cgo_notify_runtime_init_done_trampoline
41 41
42 // TODO: decide if we need x_cgo_set_context_function42 // TODO: decide if we need x_cgo_set_context_function
43 // TODO: decide if we need _cgo_yield43 // TODO: decide if we need _cgo_yield
44+
45+var (
46+ // In Go 1.20 the race detector was rewritten to pure Go
47+ // on darwin. This means that when CGO_ENABLED=0 is set
48+ // fakecgo is built with race detector code. This is not
49+ // good since this code is pretending to be C. The
50+ // go:norace comments are not enough since the ABI wrappers
51+ // still have race code. These variables will circumvent
52+ // that issue by calling the function directly.
53+ threadentry_call = threadentry
54+ x_cgo_init_call = x_cgo_init
55+ x_cgo_setenv_call = x_cgo_setenv
56+ x_cgo_unsetenv_call = x_cgo_unsetenv
57+ x_cgo_thread_start_call = x_cgo_thread_start
58+)
M internal/fakecgo/go_darwin_amd64.go +3 -0
@@ -7,6 +7,7 @@ package fakecgo
77 import "unsafe"
88
99 //go:nosplit
10+//go:norace
1011 func _cgo_sys_thread_start(ts *ThreadStart) {
1112 var attr pthread_attr_t
1213 var ign, oset sigset_t
@@ -41,6 +42,7 @@ var x_threadentry_trampoline byte
4142 var threadentry_trampolineABI0 = &x_threadentry_trampoline
4243
4344 //go:nosplit
45+//go:norace
4446 func threadentry(v unsafe.Pointer) unsafe.Pointer {
4547 ts := *(*ThreadStart)(v)
4648 free(v)
@@ -58,6 +60,7 @@ func threadentry(v unsafe.Pointer) unsafe.Pointer {
5860 var setg_func uintptr
5961
6062 //go:nosplit
63+//go:norace
6164 func x_cgo_init(g *G, setg uintptr) {
6265 var size size_t
6366 var attr pthread_attr_t
@@ -7,6 +7,7 @@ package fakecgo
7 import "unsafe"7 import "unsafe"
8 8
9 //go:nosplit9 //go:nosplit
10+//go:norace
10 func _cgo_sys_thread_start(ts *ThreadStart) {11 func _cgo_sys_thread_start(ts *ThreadStart) {
11 var attr pthread_attr_t12 var attr pthread_attr_t
12 var ign, oset sigset_t13 var ign, oset sigset_t
@@ -41,6 +42,7 @@ var x_threadentry_trampoline byte
41 var threadentry_trampolineABI0 = &x_threadentry_trampoline42 var threadentry_trampolineABI0 = &x_threadentry_trampoline
42 43
43 //go:nosplit44 //go:nosplit
45+//go:norace
44 func threadentry(v unsafe.Pointer) unsafe.Pointer {46 func threadentry(v unsafe.Pointer) unsafe.Pointer {
45 ts := *(*ThreadStart)(v)47 ts := *(*ThreadStart)(v)
46 free(v)48 free(v)
@@ -58,6 +60,7 @@ func threadentry(v unsafe.Pointer) unsafe.Pointer {
58 var setg_func uintptr60 var setg_func uintptr
59 61
60 //go:nosplit62 //go:nosplit
63+//go:norace
61 func x_cgo_init(g *G, setg uintptr) {64 func x_cgo_init(g *G, setg uintptr) {
62 var size size_t65 var size size_t
63 var attr pthread_attr_t66 var attr pthread_attr_t
M internal/fakecgo/go_darwin_arm64.go +3 -0
@@ -7,6 +7,7 @@ package fakecgo
77 import "unsafe"
88
99 //go:nosplit
10+//go:norace
1011 func _cgo_sys_thread_start(ts *ThreadStart) {
1112 var attr pthread_attr_t
1213 var ign, oset sigset_t
@@ -41,6 +42,7 @@ var x_threadentry_trampoline byte
4142 var threadentry_trampolineABI0 = &x_threadentry_trampoline
4243
4344 //go:nosplit
45+//go:norace
4446 func threadentry(v unsafe.Pointer) unsafe.Pointer {
4547 ts := *(*ThreadStart)(v)
4648 free(v)
@@ -67,6 +69,7 @@ var setg_func uintptr
6769 // This function can't be go:systemstack since go is not in a state where the systemcheck would work.
6870 //
6971 //go:nosplit
72+//go:norace
7073 func x_cgo_init(g *G, setg uintptr) {
7174 var size size_t
7275 var attr pthread_attr_t
@@ -7,6 +7,7 @@ package fakecgo
7 import "unsafe"7 import "unsafe"
8 8
9 //go:nosplit9 //go:nosplit
10+//go:norace
10 func _cgo_sys_thread_start(ts *ThreadStart) {11 func _cgo_sys_thread_start(ts *ThreadStart) {
11 var attr pthread_attr_t12 var attr pthread_attr_t
12 var ign, oset sigset_t13 var ign, oset sigset_t
@@ -41,6 +42,7 @@ var x_threadentry_trampoline byte
41 var threadentry_trampolineABI0 = &x_threadentry_trampoline42 var threadentry_trampolineABI0 = &x_threadentry_trampoline
42 43
43 //go:nosplit44 //go:nosplit
45+//go:norace
44 func threadentry(v unsafe.Pointer) unsafe.Pointer {46 func threadentry(v unsafe.Pointer) unsafe.Pointer {
45 ts := *(*ThreadStart)(v)47 ts := *(*ThreadStart)(v)
46 free(v)48 free(v)
@@ -67,6 +69,7 @@ var setg_func uintptr
67 // This function can't be go:systemstack since go is not in a state where the systemcheck would work.69 // This function can't be go:systemstack since go is not in a state where the systemcheck would work.
68 //70 //
69 //go:nosplit71 //go:nosplit
72+//go:norace
70 func x_cgo_init(g *G, setg uintptr) {73 func x_cgo_init(g *G, setg uintptr) {
71 var size size_t74 var size size_t
72 var attr pthread_attr_t75 var attr pthread_attr_t
M internal/fakecgo/go_libinit.go +1 -0
@@ -19,6 +19,7 @@ func x_cgo_notify_runtime_init_done() {
1919 // EAGAIN.
2020 //
2121 //go:nosplit
22+//go:norace
2223 func _cgo_try_pthread_create(thread *pthread_t, attr *pthread_attr_t, pfn unsafe.Pointer, arg *ThreadStart) int {
2324 var ts syscall.Timespec
2425 // tries needs to be the same type as syscall.Timespec.Nsec
@@ -19,6 +19,7 @@ func x_cgo_notify_runtime_init_done() {
19 // EAGAIN.19 // EAGAIN.
20 //20 //
21 //go:nosplit21 //go:nosplit
22+//go:norace
22 func _cgo_try_pthread_create(thread *pthread_t, attr *pthread_attr_t, pfn unsafe.Pointer, arg *ThreadStart) int {23 func _cgo_try_pthread_create(thread *pthread_t, attr *pthread_attr_t, pfn unsafe.Pointer, arg *ThreadStart) int {
23 var ts syscall.Timespec24 var ts syscall.Timespec
24 // tries needs to be the same type as syscall.Timespec.Nsec25 // tries needs to be the same type as syscall.Timespec.Nsec
M internal/fakecgo/go_setenv.go +2 -0
@@ -6,11 +6,13 @@
66 package fakecgo
77
88 //go:nosplit
9+//go:norace
910 func x_cgo_setenv(arg *[2]*byte) {
1011 setenv(arg[0], arg[1], 1)
1112 }
1213
1314 //go:nosplit
15+//go:norace
1416 func x_cgo_unsetenv(arg *[1]*byte) {
1517 unsetenv(arg[0])
1618 }
@@ -6,11 +6,13 @@
6 package fakecgo6 package fakecgo
7 7
8 //go:nosplit8 //go:nosplit
9+//go:norace
9 func x_cgo_setenv(arg *[2]*byte) {10 func x_cgo_setenv(arg *[2]*byte) {
10 setenv(arg[0], arg[1], 1)11 setenv(arg[0], arg[1], 1)
11 }12 }
12 13
13 //go:nosplit14 //go:nosplit
15+//go:norace
14 func x_cgo_unsetenv(arg *[1]*byte) {16 func x_cgo_unsetenv(arg *[1]*byte) {
15 unsetenv(arg[0])17 unsetenv(arg[0])
16 }18 }
M internal/fakecgo/go_util.go +1 -0
@@ -16,6 +16,7 @@ import "unsafe"
1616 // This function should be go:systemstack instead of go:nosplit (but that requires runtime)
1717 //
1818 //go:nosplit
19+//go:norace
1920 func x_cgo_thread_start(arg *ThreadStart) {
2021 var ts *ThreadStart
2122 // Make our own copy that can persist after we return.
@@ -16,6 +16,7 @@ import "unsafe"
16 // This function should be go:systemstack instead of go:nosplit (but that requires runtime)16 // This function should be go:systemstack instead of go:nosplit (but that requires runtime)
17 //17 //
18 //go:nosplit18 //go:nosplit
19+//go:norace
19 func x_cgo_thread_start(arg *ThreadStart) {20 func x_cgo_thread_start(arg *ThreadStart) {
20 var ts *ThreadStart21 var ts *ThreadStart
21 // Make our own copy that can persist after we return.22 // Make our own copy that can persist after we return.
M internal/fakecgo/trampolines_amd64.s +21 -12
@@ -29,24 +29,32 @@ return value will be in AX
2929 // these trampolines map the gcc ABI to Go ABI and then calls into the Go equivalent functions.
3030
3131 TEXT x_cgo_init_trampoline(SB), NOSPLIT, $16
32- MOVQ DI, 0(SP)
33- MOVQ SI, 8(SP)
34- CALL ·x_cgo_init(SB)
32+ MOVQ DI, AX
33+ MOVQ SI, BX
34+ MOVQ ·x_cgo_init_call(SB), DX
35+ MOVQ (DX), CX
36+ CALL CX
3537 RET
3638
3739 TEXT x_cgo_thread_start_trampoline(SB), NOSPLIT, $8
38- MOVQ DI, 0(SP)
39- CALL ·x_cgo_thread_start(SB)
40+ MOVQ DI, AX
41+ MOVQ ·x_cgo_thread_start_call(SB), DX
42+ MOVQ (DX), CX
43+ CALL CX
4044 RET
4145
4246 TEXT x_cgo_setenv_trampoline(SB), NOSPLIT, $8
43- MOVQ DI, 0(SP)
44- CALL ·x_cgo_setenv(SB)
47+ MOVQ DI, AX
48+ MOVQ ·x_cgo_setenv_call(SB), DX
49+ MOVQ (DX), CX
50+ CALL CX
4551 RET
4652
4753 TEXT x_cgo_unsetenv_trampoline(SB), NOSPLIT, $8
48- MOVQ DI, 0(SP)
49- CALL ·x_cgo_unsetenv(SB)
54+ MOVQ DI, AX
55+ MOVQ ·x_cgo_unsetenv_call(SB), DX
56+ MOVQ (DX), CX
57+ CALL CX
5058 RET
5159
5260 TEXT x_cgo_notify_runtime_init_done_trampoline(SB), NOSPLIT, $0
@@ -61,9 +69,10 @@ TEXT ·setg_trampoline(SB), NOSPLIT, $0-16
6169 RET
6270
6371 TEXT threadentry_trampoline(SB), NOSPLIT, $16
64- MOVQ DI, 0(SP)
65- CALL ·threadentry(SB)
66- MOVQ 8(SP), AX
72+ MOVQ DI, AX
73+ MOVQ ·threadentry_call(SB), DX
74+ MOVQ (DX), CX
75+ CALL CX
6776 RET
6877
6978 TEXT ·call5(SB), NOSPLIT, $0-0
@@ -29,24 +29,32 @@ return value will be in AX
29 // these trampolines map the gcc ABI to Go ABI and then calls into the Go equivalent functions.29 // these trampolines map the gcc ABI to Go ABI and then calls into the Go equivalent functions.
30 30
31 TEXT x_cgo_init_trampoline(SB), NOSPLIT, $1631 TEXT x_cgo_init_trampoline(SB), NOSPLIT, $16
32- MOVQ DI, 0(SP)32+ MOVQ DI, AX
33- MOVQ SI, 8(SP)33+ MOVQ SI, BX
34- CALL ·x_cgo_init(SB)34+ MOVQ ·x_cgo_init_call(SB), DX
35+ MOVQ (DX), CX
36+ CALL CX
35 RET37 RET
36 38
37 TEXT x_cgo_thread_start_trampoline(SB), NOSPLIT, $839 TEXT x_cgo_thread_start_trampoline(SB), NOSPLIT, $8
38- MOVQ DI, 0(SP)40+ MOVQ DI, AX
39- CALL ·x_cgo_thread_start(SB)41+ MOVQ ·x_cgo_thread_start_call(SB), DX
42+ MOVQ (DX), CX
43+ CALL CX
40 RET44 RET
41 45
42 TEXT x_cgo_setenv_trampoline(SB), NOSPLIT, $846 TEXT x_cgo_setenv_trampoline(SB), NOSPLIT, $8
43- MOVQ DI, 0(SP)47+ MOVQ DI, AX
44- CALL ·x_cgo_setenv(SB)48+ MOVQ ·x_cgo_setenv_call(SB), DX
49+ MOVQ (DX), CX
50+ CALL CX
45 RET51 RET
46 52
47 TEXT x_cgo_unsetenv_trampoline(SB), NOSPLIT, $853 TEXT x_cgo_unsetenv_trampoline(SB), NOSPLIT, $8
48- MOVQ DI, 0(SP)54+ MOVQ DI, AX
49- CALL ·x_cgo_unsetenv(SB)55+ MOVQ ·x_cgo_unsetenv_call(SB), DX
56+ MOVQ (DX), CX
57+ CALL CX
50 RET58 RET
51 59
52 TEXT x_cgo_notify_runtime_init_done_trampoline(SB), NOSPLIT, $060 TEXT x_cgo_notify_runtime_init_done_trampoline(SB), NOSPLIT, $0
@@ -61,9 +69,10 @@ TEXT ·setg_trampoline(SB), NOSPLIT, $0-16
61 RET69 RET
62 70
63 TEXT threadentry_trampoline(SB), NOSPLIT, $1671 TEXT threadentry_trampoline(SB), NOSPLIT, $16
64- MOVQ DI, 0(SP)72+ MOVQ DI, AX
65- CALL ·threadentry(SB)73+ MOVQ ·threadentry_call(SB), DX
66- MOVQ 8(SP), AX74+ MOVQ (DX), CX
75+ CALL CX
67 RET76 RET
68 77
69 TEXT ·call5(SB), NOSPLIT, $0-078 TEXT ·call5(SB), NOSPLIT, $0-0
M internal/fakecgo/trampolines_arm64.s +16 -6
@@ -11,22 +11,30 @@
1111 TEXT x_cgo_init_trampoline(SB), NOSPLIT, $0-0
1212 MOVD R0, 8(RSP)
1313 MOVD R1, 16(RSP)
14- CALL ·x_cgo_init(SB)
14+ MOVD ·x_cgo_init_call(SB), R26
15+ MOVD (R26), R2
16+ CALL (R2)
1517 RET
1618
1719 TEXT x_cgo_thread_start_trampoline(SB), NOSPLIT, $0-0
1820 MOVD R0, 8(RSP)
19- CALL ·x_cgo_thread_start(SB)
21+ MOVD ·x_cgo_thread_start_call(SB), R26
22+ MOVD (R26), R2
23+ CALL (R2)
2024 RET
2125
2226 TEXT x_cgo_setenv_trampoline(SB), NOSPLIT, $0-0
2327 MOVD R0, 8(RSP)
24- CALL ·x_cgo_setenv(SB)
28+ MOVD ·x_cgo_setenv_call(SB), R26
29+ MOVD (R26), R2
30+ CALL (R2)
2531 RET
2632
2733 TEXT x_cgo_unsetenv_trampoline(SB), NOSPLIT, $0-0
2834 MOVD R0, 8(RSP)
29- CALL ·x_cgo_unsetenv(SB)
35+ MOVD ·x_cgo_unsetenv_call(SB), R26
36+ MOVD (R26), R2
37+ CALL (R2)
3038 RET
3139
3240 TEXT x_cgo_notify_runtime_init_done_trampoline(SB), NOSPLIT, $0-0
@@ -42,8 +50,10 @@ TEXT ·setg_trampoline(SB), NOSPLIT, $0-16
4250
4351 TEXT threadentry_trampoline(SB), NOSPLIT, $0-0
4452 MOVD R0, 8(RSP)
45- CALL ·threadentry(SB)
46- MOVD $0, R0 // TODO: get the return value from threadentry
53+ MOVD ·threadentry_call(SB), R26
54+ MOVD (R26), R2
55+ CALL (R2)
56+ MOVD $0, R0 // TODO: get the return value from threadentry
4757 RET
4858
4959 TEXT ·call5(SB), NOSPLIT, $0-0
@@ -11,22 +11,30 @@
11 TEXT x_cgo_init_trampoline(SB), NOSPLIT, $0-011 TEXT x_cgo_init_trampoline(SB), NOSPLIT, $0-0
12 MOVD R0, 8(RSP)12 MOVD R0, 8(RSP)
13 MOVD R1, 16(RSP)13 MOVD R1, 16(RSP)
14- CALL ·x_cgo_init(SB)14+ MOVD ·x_cgo_init_call(SB), R26
15+ MOVD (R26), R2
16+ CALL (R2)
15 RET17 RET
16 18
17 TEXT x_cgo_thread_start_trampoline(SB), NOSPLIT, $0-019 TEXT x_cgo_thread_start_trampoline(SB), NOSPLIT, $0-0
18 MOVD R0, 8(RSP)20 MOVD R0, 8(RSP)
19- CALL ·x_cgo_thread_start(SB)21+ MOVD ·x_cgo_thread_start_call(SB), R26
22+ MOVD (R26), R2
23+ CALL (R2)
20 RET24 RET
21 25
22 TEXT x_cgo_setenv_trampoline(SB), NOSPLIT, $0-026 TEXT x_cgo_setenv_trampoline(SB), NOSPLIT, $0-0
23 MOVD R0, 8(RSP)27 MOVD R0, 8(RSP)
24- CALL ·x_cgo_setenv(SB)28+ MOVD ·x_cgo_setenv_call(SB), R26
29+ MOVD (R26), R2
30+ CALL (R2)
25 RET31 RET
26 32
27 TEXT x_cgo_unsetenv_trampoline(SB), NOSPLIT, $0-033 TEXT x_cgo_unsetenv_trampoline(SB), NOSPLIT, $0-0
28 MOVD R0, 8(RSP)34 MOVD R0, 8(RSP)
29- CALL ·x_cgo_unsetenv(SB)35+ MOVD ·x_cgo_unsetenv_call(SB), R26
36+ MOVD (R26), R2
37+ CALL (R2)
30 RET38 RET
31 39
32 TEXT x_cgo_notify_runtime_init_done_trampoline(SB), NOSPLIT, $0-040 TEXT x_cgo_notify_runtime_init_done_trampoline(SB), NOSPLIT, $0-0
@@ -42,8 +50,10 @@ TEXT ·setg_trampoline(SB), NOSPLIT, $0-16
42 50
43 TEXT threadentry_trampoline(SB), NOSPLIT, $0-051 TEXT threadentry_trampoline(SB), NOSPLIT, $0-0
44 MOVD R0, 8(RSP)52 MOVD R0, 8(RSP)
45- CALL ·threadentry(SB)53+ MOVD ·threadentry_call(SB), R26
46- MOVD $0, R0 // TODO: get the return value from threadentry54+ MOVD (R26), R2
55+ CALL (R2)
56+ MOVD $0, R0 // TODO: get the return value from threadentry
47 RET57 RET
48 58
49 TEXT ·call5(SB), NOSPLIT, $0-059 TEXT ·call5(SB), NOSPLIT, $0-0

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