runtime/cgo: initialize our pthread_create wrapper earlier on openbsd

This is a genuine bug exposed by our test for issue 9456: our wrapper
for pthread_create is not initialized until we initialize cgo itself,
but it is possible that a static constructor could call pthread_create,
and in that case, it will be calling a nil function pointer.

Fix that by also initializing the sys_pthread_create function pointer
inside our pthread_create wrapper function, and use a pthread_once to
make sure it is only initialized once.

Fix build for openbsd.

Change-Id: Ica4da2c21fcaec186fdd3379128ef46f0e767ed7
Reviewed-on: https://go-review.googlesource.com/2232
Reviewed-by: David Crawshaw <crawshaw@golang.org>
diff --git a/src/runtime/cgo/gcc_openbsd_amd64.c b/src/runtime/cgo/gcc_openbsd_amd64.c
index 35b359b..8522cd4 100644
--- a/src/runtime/cgo/gcc_openbsd_amd64.c
+++ b/src/runtime/cgo/gcc_openbsd_amd64.c
@@ -65,12 +65,39 @@
 	return args.func(args.arg);
 }
 
+static void init_pthread_wrapper(void) {
+	void *handle;
+
+	// Locate symbol for the system pthread_create function.
+	handle = dlopen("libpthread.so", RTLD_LAZY);
+	if(handle == NULL) {
+		fprintf(stderr, "runtime/cgo: dlopen failed to load libpthread: %s\n", dlerror());
+		abort();
+	}
+	sys_pthread_create = dlsym(handle, "pthread_create");
+	if(sys_pthread_create == NULL) {
+		fprintf(stderr, "runtime/cgo: dlsym failed to find pthread_create: %s\n", dlerror());
+		abort();
+	}
+	dlclose(handle);
+}
+
+static pthread_once_t init_pthread_wrapper_once = PTHREAD_ONCE_INIT;
+
 int
 pthread_create(pthread_t *thread, const pthread_attr_t *attr,
 	void *(*start_routine)(void *), void *arg)
 {
 	struct thread_args *p;
 
+	// we must initialize our wrapper in pthread_create, because it is valid to call
+	// pthread_create in a static constructor, and in fact, our test for issue 9456
+	// does just that.
+	if(pthread_once(&init_pthread_wrapper_once, init_pthread_wrapper) != 0) {
+		fprintf(stderr, "runtime/cgo: failed to initialize pthread_create wrapper\n");
+		abort();
+	}
+
 	p = malloc(sizeof(*p));
 	if(p == NULL) {
 		errno = ENOMEM;
@@ -87,7 +114,6 @@
 {
 	pthread_attr_t attr;
 	size_t size;
-	void *handle;
 
 	setg_gcc = setg;
 	pthread_attr_init(&attr);
@@ -95,18 +121,10 @@
 	g->stacklo = (uintptr)&attr - size + 4096;
 	pthread_attr_destroy(&attr);
 
-	// Locate symbol for the system pthread_create function.
-	handle = dlopen("libpthread.so", RTLD_LAZY);
-	if(handle == NULL) {
-		fprintf(stderr, "dlopen: failed to load libpthread: %s\n", dlerror());
+	if(pthread_once(&init_pthread_wrapper_once, init_pthread_wrapper) != 0) {
+		fprintf(stderr, "runtime/cgo: failed to initialize pthread_create wrapper\n");
 		abort();
 	}
-	sys_pthread_create = dlsym(handle, "pthread_create");
-	if(sys_pthread_create == NULL) {
-		fprintf(stderr, "dlsym: failed to find pthread_create: %s\n", dlerror());
-		abort();
-	}
-	dlclose(handle);
 
 	tcb_fixup(1);
 }