Home | History | Annotate | Download | only in cgo
      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