github.com/notti/nocgo@v0.0.0-20190619201224-fc443047424c/internal/fakecgo/cgo.go (about) 1 /* 2 Package fakecgo fakes necessary functionality to support calling C-code (TLS initialization for main thread and subsequent threads). 3 4 Usage 5 6 Just import this library with 7 import _ "github.com/notti/nocgo/fakecgo" 8 and enjoy the side effects (e.g., you can't use cgo any longer) :) 9 */ 10 package fakecgo 11 12 import ( 13 "syscall" 14 "unsafe" 15 ) 16 17 // WARNING: please read this before changing/improving anything 18 // This here might look like (ugly) go - but it is actually somehow C-code written in go (basically stuff in runtime/cgo/) 19 // Yeah this somehow works, but needs the trampolines from trampoline_*.s to fix calling conventions cdecl <-> go 20 // 21 // Beware that strings must end with a 0 byte to not confuse C-code we call 22 // 23 // Write barriers (we will be called while go is in a state where this is not possible) and stack split (we will be on systemstack anyway) are NOT allowed in here 24 // -> e.g. use memmove for copying to pointers 25 // go:nowritebarrierrec is only allowed inside runtime - so this has to be checked manually :( 26 27 // pthread_create will call this with ts as argument in a new thread -> this fixes up arguments to go (in assembly) and calls threadentry 28 func threadentry_trampoline() 29 30 // here we will store a pointer to the provided setg func 31 var setg_func uintptr 32 33 // x_cgo_init(G *g, void (*setg)(void*)) (runtime/cgo/gcc_linux_amd64.c) 34 // This get's called during startup, adjusts stacklo, and provides a pointer to setg_gcc for us 35 // Additionally, if we set _cgo_init to non-null, go won't do it's own TLS setup 36 // This function can't be go:systemstack since go is not in a state where the systemcheck would work. 37 // 38 //go:nosplit 39 func x_cgo_init(g *g, setg uintptr) { 40 var size size_t 41 var attr pthread_attr 42 43 // we need an extra variable here - otherwise go generates "safe" code, which is not allowed here 44 stackSp := uintptr(unsafe.Pointer(&size)) 45 46 setg_func = setg 47 48 pthread_attr_init(&attr) 49 pthread_attr_getstacksize(&attr, &size) 50 g.stack.lo = stackSp - uintptr(size) + 4096 51 pthread_attr_destroy(&attr) 52 } 53 54 // _cgo_thread_start is split into three parts in cgo since only one part is system dependent (keep it here for easier handling) 55 56 // _cgo_thread_start(ThreadStart *arg) (runtime/cgo/gcc_util.c) 57 // This get's called instead of the go code for creating new threads 58 // -> pthread_* stuff is used, so threads are setup correctly for C 59 // If this is missing, TLS is only setup correctly on thread 1! 60 // This function should be go:systemstack instead of go:nosplit (but that requires runtime) 61 // 62 //go:nosplit 63 func x_cgo_thread_start(arg *threadstart) { 64 ts := malloc(unsafe.Sizeof(threadstart{})) 65 66 if ts == 0 { 67 dprintf(2, "couldn't allocate memory for threadstart\n\000") 68 abort() 69 } 70 71 // *ts = *arg would cause a write barrier, which is not allowed 72 memmove(unsafe.Pointer(ts), unsafe.Pointer(arg), unsafe.Sizeof(threadstart{})) 73 74 var attr pthread_attr 75 var ign, oset sigset_t 76 var p pthread_t 77 var size size_t 78 79 sigfillset(&ign) 80 pthread_sigmask(SIG_SETMASK, &ign, &oset) 81 82 pthread_attr_init(&attr) 83 pthread_attr_getstacksize(&attr, &size) 84 (*g)((*threadstart)(unsafe.Pointer(ts)).g).stack.hi = uintptr(size) 85 86 var err int32 87 88 for tries := 0; tries < 20; tries++ { 89 err = pthread_create(&p, &attr, unsafe.Pointer(funcPC(threadentry_trampoline)), unsafe.Pointer(ts)) 90 if err == 0 { 91 pthread_detach(p) 92 break 93 } 94 if err != int32(syscall.EAGAIN) { 95 break 96 } 97 ts := timespec{tv_sec: 0, tv_nsec: (tries + 1) * 1000 * 1000} 98 nanosleep(&ts, nil) 99 } 100 101 pthread_sigmask(SIG_SETMASK, &oset, nil) 102 103 if err != 0 { 104 dprintf(2, "pthread_create failed: %s\n\000", strerror(err)) 105 abort() 106 } 107 } 108 109 func setg_trampoline(uintptr, unsafe.Pointer) 110 111 //go:nosplit 112 func threadentry(v unsafe.Pointer) uintptr { 113 ts := *(*threadstart)(v) 114 115 free(v) 116 117 setg_trampoline(setg_func, ts.g) 118 119 // faking funcs in go is a bit a... involved - but the following works :) 120 fn := uintptr(unsafe.Pointer(&ts.fn)) 121 (*(*func())(unsafe.Pointer(&fn)))() 122 123 return 0 124 } 125 126 // The following functions are required by the runtime - otherwise it complains via panic that they are missing 127 128 // do nothing - we don't support being a library for now 129 // _cgo_notify_runtime_init_done (runtime/cgo/gcc_libinit.c) 130 //go:nosplit 131 func x_cgo_notify_runtime_init_done() {} 132 133 // _cgo_setenv(char **arg) (runtime/cgo/gcc_setenv.c) 134 //go:nosplit 135 func x_cgo_setenv(arg [2]*byte) { 136 setenv(arg[0], arg[1], 1) 137 } 138 139 // _cgo_unsetenv(char *arg) (runtime/cgo/gcc_setenv.c) 140 //go:nosplit 141 func x_cgo_unsetenv(arg *byte) { 142 unsetenv(arg) 143 }