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 <string.h> /* for strerror */ 6 #include <pthread.h> 7 #include <signal.h> 8 #include "libcgo.h" 9 #include "libcgo_unix.h" 10 11 static void* threadentry(void*); 12 static pthread_key_t k1; 13 14 #define magic1 (0x23581321U) 15 16 static void 17 inittls(void) 18 { 19 uint32 x; 20 pthread_key_t tofree[128], k; 21 int i, ntofree; 22 23 /* 24 * Allocate thread-local storage slot for g. 25 * The key numbers start at 0x100, and we expect to be 26 * one of the early calls to pthread_key_create, so we 27 * should be able to get a pretty low number. 28 * 29 * In Darwin/386 pthreads, %gs points at the thread 30 * structure, and each key is an index into the thread-local 31 * storage array that begins at offset 0x48 within in that structure. 32 * It may happen that we are not quite the first function to try 33 * to allocate thread-local storage keys, so instead of depending 34 * on getting 0x100, we try for 0x108, allocating keys until 35 * we get the one we want and then freeing the ones we didn't want. 36 * 37 * Thus the final offset to use in %gs references is 38 * 0x48+4*0x108 = 0x468. 39 * 40 * The linker and runtime hard-code this constant offset 41 * from %gs where we expect to find g. 42 * Known to ../../../liblink/sym.c:/468 43 * and to ../sys_darwin_386.s:/468 44 * 45 * This is truly disgusting and a bit fragile, but taking care 46 * of it here protects the rest of the system from damage. 47 * The alternative would be to use a global variable that 48 * held the offset and refer to that variable each time we 49 * need a %gs variable (g). That approach would 50 * require an extra instruction and memory reference in 51 * every stack growth prolog and would also require 52 * rewriting the code that 8c generates for extern registers. 53 * 54 * Things get more disgusting on OS X 10.7 Lion. 55 * The 0x48 base mentioned above is the offset of the tsd 56 * array within the per-thread structure on Leopard and Snow Leopard. 57 * On Lion, the base moved a little, so while the math above 58 * still applies, the base is different. Thus, we cannot 59 * look for specific key values if we want to build binaries 60 * that run on both systems. Instead, forget about the 61 * specific key values and just allocate and initialize per-thread 62 * storage until we find a key that writes to the memory location 63 * we want. Then keep that key. 64 */ 65 ntofree = 0; 66 for(;;) { 67 if(pthread_key_create(&k, nil) < 0) { 68 fprintf(stderr, "runtime/cgo: pthread_key_create failed\n"); 69 abort(); 70 } 71 pthread_setspecific(k, (void*)magic1); 72 asm volatile("movl %%gs:0x468, %0" : "=r"(x)); 73 pthread_setspecific(k, 0); 74 if(x == magic1) { 75 k1 = k; 76 break; 77 } 78 if(ntofree >= nelem(tofree)) { 79 fprintf(stderr, "runtime/cgo: could not obtain pthread_keys\n"); 80 fprintf(stderr, "\ttried"); 81 for(i=0; i<ntofree; i++) 82 fprintf(stderr, " %#x", (unsigned)tofree[i]); 83 fprintf(stderr, "\n"); 84 abort(); 85 } 86 tofree[ntofree++] = k; 87 } 88 89 /* 90 * We got the key we wanted. Free the others. 91 */ 92 for(i=0; i<ntofree; i++) 93 pthread_key_delete(tofree[i]); 94 } 95 96 void 97 x_cgo_init(G *g) 98 { 99 pthread_attr_t attr; 100 size_t size; 101 102 pthread_attr_init(&attr); 103 pthread_attr_getstacksize(&attr, &size); 104 g->stacklo = (uintptr)&attr - size + 4096; 105 pthread_attr_destroy(&attr); 106 107 inittls(); 108 } 109 110 111 void 112 _cgo_sys_thread_start(ThreadStart *ts) 113 { 114 pthread_attr_t attr; 115 sigset_t ign, oset; 116 pthread_t p; 117 size_t size; 118 int err; 119 120 sigfillset(&ign); 121 pthread_sigmask(SIG_SETMASK, &ign, &oset); 122 123 pthread_attr_init(&attr); 124 pthread_attr_getstacksize(&attr, &size); 125 // Leave stacklo=0 and set stackhi=size; mstack will do the rest. 126 ts->g->stackhi = size; 127 err = _cgo_try_pthread_create(&p, &attr, threadentry, ts); 128 129 pthread_sigmask(SIG_SETMASK, &oset, nil); 130 131 if (err != 0) { 132 fprintf(stderr, "runtime/cgo: pthread_create failed: %s\n", strerror(err)); 133 abort(); 134 } 135 } 136 137 static void* 138 threadentry(void *v) 139 { 140 ThreadStart ts; 141 142 ts = *(ThreadStart*)v; 143 free(v); 144 145 pthread_setspecific(k1, (void*)ts.g); 146 147 crosscall_386(ts.fn); 148 return nil; 149 } 150