github.com/tidwall/go@v0.0.0-20170415222209-6694a6888b7d/src/runtime/cgo/gcc_darwin_arm64.c (about) 1 // Copyright 2014 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 <limits.h> 6 #include <pthread.h> 7 #include <signal.h> 8 #include <string.h> /* for strerror */ 9 #include <sys/param.h> 10 #include <unistd.h> 11 #include <stdlib.h> 12 13 #include "libcgo.h" 14 #include "libcgo_unix.h" 15 16 #include <CoreFoundation/CFBundle.h> 17 #include <CoreFoundation/CFString.h> 18 19 #define magic (0xc476c475c47957UL) 20 21 // inittls allocates a thread-local storage slot for g. 22 // 23 // It finds the first available slot using pthread_key_create and uses 24 // it as the offset value for runtime.tlsg. 25 static void 26 inittls(void **tlsg, void **tlsbase) 27 { 28 pthread_key_t k; 29 int i, err; 30 31 err = pthread_key_create(&k, nil); 32 if(err != 0) { 33 fprintf(stderr, "runtime/cgo: pthread_key_create failed: %d\n", err); 34 abort(); 35 } 36 //fprintf(stderr, "runtime/cgo: k = %d, tlsbase = %p\n", (int)k, tlsbase); // debug 37 pthread_setspecific(k, (void*)magic); 38 // The first key should be at 257. 39 for (i=0; i<PTHREAD_KEYS_MAX; i++) { 40 if (*(tlsbase+i) == (void*)magic) { 41 *tlsg = (void*)(i*sizeof(void *)); 42 pthread_setspecific(k, 0); 43 return; 44 } 45 } 46 fprintf(stderr, "runtime/cgo: could not find pthread key.\n"); 47 abort(); 48 } 49 50 static void *threadentry(void*); 51 void (*setg_gcc)(void*); 52 53 void 54 _cgo_sys_thread_start(ThreadStart *ts) 55 { 56 pthread_attr_t attr; 57 sigset_t ign, oset; 58 pthread_t p; 59 size_t size; 60 int err; 61 62 //fprintf(stderr, "runtime/cgo: _cgo_sys_thread_start: fn=%p, g=%p\n", ts->fn, ts->g); // debug 63 sigfillset(&ign); 64 pthread_sigmask(SIG_SETMASK, &ign, &oset); 65 66 pthread_attr_init(&attr); 67 size = 0; 68 pthread_attr_getstacksize(&attr, &size); 69 // Leave stacklo=0 and set stackhi=size; mstack will do the rest. 70 ts->g->stackhi = size; 71 err = _cgo_try_pthread_create(&p, &attr, threadentry, ts); 72 73 pthread_sigmask(SIG_SETMASK, &oset, nil); 74 75 if (err != 0) { 76 fprintf(stderr, "runtime/cgo: pthread_create failed: %s\n", strerror(err)); 77 abort(); 78 } 79 } 80 81 extern void crosscall1(void (*fn)(void), void (*setg_gcc)(void*), void *g); 82 static void* 83 threadentry(void *v) 84 { 85 ThreadStart ts; 86 87 ts = *(ThreadStart*)v; 88 free(v); 89 90 darwin_arm_init_thread_exception_port(); 91 92 crosscall1(ts.fn, setg_gcc, (void*)ts.g); 93 return nil; 94 } 95 96 // init_working_dir sets the current working directory to the app root. 97 // By default darwin/arm processes start in "/". 98 static void 99 init_working_dir() 100 { 101 CFBundleRef bundle = CFBundleGetMainBundle(); 102 if (bundle == NULL) { 103 fprintf(stderr, "runtime/cgo: no main bundle\n"); 104 return; 105 } 106 CFURLRef url_ref = CFBundleCopyResourceURL(bundle, CFSTR("Info"), CFSTR("plist"), NULL); 107 if (url_ref == NULL) { 108 fprintf(stderr, "runtime/cgo: no Info.plist URL\n"); 109 return; 110 } 111 CFStringRef url_str_ref = CFURLGetString(url_ref); 112 char buf[MAXPATHLEN]; 113 Boolean res = CFStringGetCString(url_str_ref, buf, sizeof(buf), kCFStringEncodingUTF8); 114 CFRelease(url_ref); 115 if (!res) { 116 fprintf(stderr, "runtime/cgo: cannot get URL string\n"); 117 return; 118 } 119 120 // url is of the form "file:///path/to/Info.plist". 121 // strip it down to the working directory "/path/to". 122 int url_len = strlen(buf); 123 if (url_len < sizeof("file://")+sizeof("/Info.plist")) { 124 fprintf(stderr, "runtime/cgo: bad URL: %s\n", buf); 125 return; 126 } 127 buf[url_len-sizeof("/Info.plist")+1] = 0; 128 char *dir = &buf[0] + sizeof("file://")-1; 129 130 if (chdir(dir) != 0) { 131 fprintf(stderr, "runtime/cgo: chdir(%s) failed\n", dir); 132 } 133 134 // The test harness in go_darwin_arm_exec passes the relative working directory 135 // in the GoExecWrapperWorkingDirectory property of the app bundle. 136 CFStringRef wd_ref = CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("GoExecWrapperWorkingDirectory")); 137 if (wd_ref != NULL) { 138 if (!CFStringGetCString(wd_ref, buf, sizeof(buf), kCFStringEncodingUTF8)) { 139 fprintf(stderr, "runtime/cgo: cannot get GoExecWrapperWorkingDirectory string\n"); 140 return; 141 } 142 if (chdir(buf) != 0) { 143 fprintf(stderr, "runtime/cgo: chdir(%s) failed\n", buf); 144 } 145 // Notify the test harness that we're correctly set up 146 raise(SIGINT); 147 } 148 } 149 150 void 151 x_cgo_init(G *g, void (*setg)(void*), void **tlsg, void **tlsbase) 152 { 153 pthread_attr_t attr; 154 size_t size; 155 156 //fprintf(stderr, "x_cgo_init = %p\n", &x_cgo_init); // aid debugging in presence of ASLR 157 setg_gcc = setg; 158 pthread_attr_init(&attr); 159 pthread_attr_getstacksize(&attr, &size); 160 g->stacklo = (uintptr)&attr - size + 4096; 161 pthread_attr_destroy(&attr); 162 163 // yes, tlsbase from mrs might not be correctly aligned. 164 inittls(tlsg, (void**)((uintptr)tlsbase & ~7)); 165 166 darwin_arm_init_mach_exception_handler(); 167 darwin_arm_init_thread_exception_port(); 168 169 init_working_dir(); 170 }