1 // Copyright 2009 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 #include <sys/types.h> 6 #include <dlfcn.h> 7 #include <errno.h> 8 #include <pthread.h> 9 #include <signal.h> 10 #include <string.h> 11 #include "libcgo.h" 12 #include "libcgo_unix.h" 13 14 static void* threadentry(void*); 15 static void (*setg_gcc)(void*); 16 17 // TCB_SIZE is sizeof(struct thread_control_block), as defined in 18 // /usr/src/lib/librthread/tcb.h on OpenBSD 5.9 and earlier. 19 #define TCB_SIZE (4 * sizeof(void *)) 20 21 // TIB_SIZE is sizeof(struct tib), as defined in 22 // /usr/include/tib.h on OpenBSD 6.0 and later. 23 #define TIB_SIZE (4 * sizeof(void *) + 6 * sizeof(int)) 24 25 // TLS_SIZE is the size of TLS needed for Go. 26 #define TLS_SIZE (2 * sizeof(void *)) 27 28 void *__get_tcb(void); 29 void __set_tcb(void *); 30 31 static int (*sys_pthread_create)(pthread_t *thread, const pthread_attr_t *attr, 32 void *(*start_routine)(void *), void *arg); 33 34 struct thread_args { 35 void *(*func)(void *); 36 void *arg; 37 }; 38 39 static int has_tib = 0; 40 41 static void 42 tcb_fixup(int mainthread) 43 { 44 void *tls, *newtcb, *oldtcb; 45 size_t tls_size, tcb_size; 46 47 // TODO(jsing): Remove once OpenBSD 6.1 is released and OpenBSD 5.9 is 48 // no longer supported. 49 50 // The OpenBSD ld.so(1) does not currently support PT_TLS. As a result, 51 // we need to allocate our own TLS space while preserving the existing 52 // TCB or TIB that has been setup via librthread. 53 54 tcb_size = has_tib ? TIB_SIZE : TCB_SIZE; 55 tls_size = TLS_SIZE + tcb_size; 56 tls = malloc(tls_size); 57 if(tls == NULL) 58 abort(); 59 60 // The signal trampoline expects the TLS slots to be zeroed. 61 bzero(tls, TLS_SIZE); 62 63 oldtcb = __get_tcb(); 64 newtcb = tls + TLS_SIZE; 65 bcopy(oldtcb, newtcb, tcb_size); 66 if(has_tib) { 67 // Fix up self pointer. 68 *(uintptr_t *)(newtcb) = (uintptr_t)newtcb; 69 } 70 __set_tcb(newtcb); 71 72 // NOTE(jsing, minux): we can't free oldtcb without causing double-free 73 // problem. so newtcb will be memory leaks. Get rid of this when OpenBSD 74 // has proper support for PT_TLS. 75 } 76 77 static void * 78 thread_start_wrapper(void *arg) 79 { 80 struct thread_args args = *(struct thread_args *)arg; 81 82 free(arg); 83 tcb_fixup(0); 84 85 return args.func(args.arg); 86 } 87 88 static void init_pthread_wrapper(void) { 89 void *handle; 90 91 // Locate symbol for the system pthread_create function. 92 handle = dlopen("libpthread.so", RTLD_LAZY); 93 if(handle == NULL) { 94 fprintf(stderr, "runtime/cgo: dlopen failed to load libpthread: %s\n", dlerror()); 95 abort(); 96 } 97 sys_pthread_create = dlsym(handle, "pthread_create"); 98 if(sys_pthread_create == NULL) { 99 fprintf(stderr, "runtime/cgo: dlsym failed to find pthread_create: %s\n", dlerror()); 100 abort(); 101 } 102 // _rthread_init is hidden in OpenBSD librthread that has TIB. 103 if(dlsym(handle, "_rthread_init") == NULL) { 104 has_tib = 1; 105 } 106 dlclose(handle); 107 } 108 109 static pthread_once_t init_pthread_wrapper_once = PTHREAD_ONCE_INIT; 110 111 int 112 pthread_create(pthread_t *thread, const pthread_attr_t *attr, 113 void *(*start_routine)(void *), void *arg) 114 { 115 struct thread_args *p; 116 117 // we must initialize our wrapper in pthread_create, because it is valid to call 118 // pthread_create in a static constructor, and in fact, our test for issue 9456 119 // does just that. 120 if(pthread_once(&init_pthread_wrapper_once, init_pthread_wrapper) != 0) { 121 fprintf(stderr, "runtime/cgo: failed to initialize pthread_create wrapper\n"); 122 abort(); 123 } 124 125 p = malloc(sizeof(*p)); 126 if(p == NULL) { 127 errno = ENOMEM; 128 return -1; 129 } 130 p->func = start_routine; 131 p->arg = arg; 132 133 return sys_pthread_create(thread, attr, thread_start_wrapper, p); 134 } 135 136 void 137 x_cgo_init(G *g, void (*setg)(void*)) 138 { 139 pthread_attr_t attr; 140 size_t size; 141 142 setg_gcc = setg; 143 pthread_attr_init(&attr); 144 pthread_attr_getstacksize(&attr, &size); 145 g->stacklo = (uintptr)&attr - size + 4096; 146 pthread_attr_destroy(&attr); 147 148 if(pthread_once(&init_pthread_wrapper_once, init_pthread_wrapper) != 0) { 149 fprintf(stderr, "runtime/cgo: failed to initialize pthread_create wrapper\n"); 150 abort(); 151 } 152 153 tcb_fixup(1); 154 } 155 156 157 void 158 _cgo_sys_thread_start(ThreadStart *ts) 159 { 160 pthread_attr_t attr; 161 sigset_t ign, oset; 162 pthread_t p; 163 size_t size; 164 int err; 165 166 sigfillset(&ign); 167 pthread_sigmask(SIG_SETMASK, &ign, &oset); 168 169 pthread_attr_init(&attr); 170 pthread_attr_getstacksize(&attr, &size); 171 172 // Leave stacklo=0 and set stackhi=size; mstack will do the rest. 173 ts->g->stackhi = size; 174 err = _cgo_openbsd_try_pthread_create(sys_pthread_create, &p, &attr, threadentry, ts); 175 176 pthread_sigmask(SIG_SETMASK, &oset, nil); 177 178 if (err != 0) { 179 fprintf(stderr, "runtime/cgo: pthread_create failed: %s\n", strerror(err)); 180 abort(); 181 } 182 } 183 184 static void* 185 threadentry(void *v) 186 { 187 ThreadStart ts; 188 189 tcb_fixup(0); 190 191 ts = *(ThreadStart*)v; 192 free(v); 193 194 /* 195 * Set specific keys. 196 */ 197 setg_gcc((void*)ts.g); 198 199 crosscall_386(ts.fn); 200 return nil; 201 } 202