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