summaryrefslogtreecommitdiff
path: root/src/libcgo
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2009-10-03 10:37:12 -0700
committerRuss Cox <rsc@golang.org>2009-10-03 10:37:12 -0700
commit1bb8f19f5e9016a9eb118b8aba2b5689b4505812 (patch)
tree7b69b0974f4999bac76d11abed2071a2dc9e97aa /src/libcgo
parent6b7539d607f26a1b1cd3eda71fc123ad4d52a133 (diff)
downloadgolang-1bb8f19f5e9016a9eb118b8aba2b5689b4505812.tar.gz
8c, 8l dynamic loading support.
better mach binaries. cgo working on darwin+linux amd64+386. eliminated context switches - pi is 30x faster. add libcgo to build. on snow leopard: - non-cgo binaries work; all tests pass. - cgo binaries work on amd64 but not 386. R=r DELTA=2031 (1316 added, 626 deleted, 89 changed) OCL=35264 CL=35304
Diffstat (limited to 'src/libcgo')
-rw-r--r--src/libcgo/386.S37
-rw-r--r--src/libcgo/Makefile26
-rw-r--r--src/libcgo/amd64.S45
-rw-r--r--src/libcgo/cgocall.c308
-rw-r--r--src/libcgo/darwin_386.c144
-rw-r--r--src/libcgo/darwin_amd64.c46
-rw-r--r--src/libcgo/libcgo.h60
-rw-r--r--src/libcgo/linux_386.c57
-rw-r--r--src/libcgo/linux_amd64.c46
-rw-r--r--src/libcgo/linux_arm.c1
-rw-r--r--src/libcgo/nacl_386.c1
-rw-r--r--src/libcgo/util.c35
12 files changed, 489 insertions, 317 deletions
diff --git a/src/libcgo/386.S b/src/libcgo/386.S
new file mode 100644
index 000000000..3d7786d14
--- /dev/null
+++ b/src/libcgo/386.S
@@ -0,0 +1,37 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+ * Apple still insists on underscore prefixes for C function names.
+ */
+#ifdef __APPLE__
+#define EXT(s) _##s
+#else
+#define EXT(s) s
+#endif
+
+/*
+ * void crosscall_386(void (*fn)(void))
+ *
+ * Calling into the 8c tool chain, where all registers are caller save.
+ * Called from standard x86 ABI, where %ebp, %ebx, %esi,
+ * and %edi are callee-save, so they must be saved explicitly.
+ */
+.globl EXT(crosscall_386)
+EXT(crosscall_386):
+ pushl %ebp
+ movl %esp, %ebp
+ pushl %ebx
+ pushl %esi
+ pushl %edi
+
+ movl 8(%ebp), %eax /* fn */
+ call *%eax
+
+ popl %edi
+ popl %esi
+ popl %ebx
+ popl %ebp
+ ret
+
diff --git a/src/libcgo/Makefile b/src/libcgo/Makefile
index ea4ccc7ef..a32382350 100644
--- a/src/libcgo/Makefile
+++ b/src/libcgo/Makefile
@@ -2,21 +2,29 @@
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
-# not linked into build for now
+all: libcgo.so
-CFLAGS_386=-m32
+install: $(GOROOT)/pkg/$(GOOS)_$(GOARCH)/libcgo.so
-TARG=libcgo.so
+OFILES=\
+ $(GOOS)_$(GOARCH).o\
+ $(GOARCH).o\
+ util.o\
-all: libcgo.so
+CFLAGS_386=-m32
+CFLAGS_amd64=-m64
-cgocall.o: cgocall.c
- gcc $(CFLAGS_$(GOARCH)) -O2 -fPIC -o cgocall.o -c cgocall.c
+LDFLAGS_linux=-shared -lpthread -lm
+LDFLAGS_darwin=-dynamiclib -Wl,-undefined,dynamic_lookup /usr/lib/libpthread.dylib
-libcgo.so: cgocall.o
- gcc $(CFLAGS_$(GOARCH)) -shared -o libcgo.so cgocall.o -lpthread -lm
+%.o: %.c
+ gcc $(CFLAGS_$(GOARCH)) -O2 -fPIC -o $@ -c $*.c
-install: $(GOROOT)/pkg/$(GOOS)_$(GOARCH)/libcgo.so
+%.o: %.S
+ gcc $(CFLAGS_$(GOARCH)) -O2 -fPIC -o $@ -c $*.S
+
+libcgo.so: $(OFILES)
+ gcc $(CFLAGS_$(GOARCH)) $(LDFLAGS_$(GOOS)) -o libcgo.so $(OFILES)
$(GOROOT)/pkg/$(GOOS)_$(GOARCH)/libcgo.so: libcgo.so
cp libcgo.so $@
diff --git a/src/libcgo/amd64.S b/src/libcgo/amd64.S
new file mode 100644
index 000000000..eaa346a14
--- /dev/null
+++ b/src/libcgo/amd64.S
@@ -0,0 +1,45 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+ * Apple still insists on underscore prefixes for C function names.
+ */
+#ifdef __APPLE__
+#define EXT(s) _##s
+#else
+#define EXT(s) s
+#endif
+
+/*
+ * void crosscall_amd64(M *m, G *g, void (*fn)(void))
+ *
+ * Calling into the 6c tool chain, where all registers are caller save.
+ * Called from standard x86-64 ABI, where %rbx, %rbp, %r12-%r15
+ * are callee-save so they must be saved explicitly.
+ * The standard x86-64 ABI passes the three arguments m, g, fn
+ * in %rdi, %rsi, %rdx.
+ *
+ * Also need to set %r15 to g and %r14 to m (see ../pkg/runtime/mkasmh.sh)
+ * during the call.
+ */
+.globl EXT(crosscall_amd64)
+EXT(crosscall_amd64):
+ pushq %rbx
+ pushq %rbp
+ pushq %r12
+ pushq %r13
+ pushq %r14
+ pushq %r15
+
+ movq %rdi, %r14 /* m */
+ movq %rsi, %r15 /* g */
+ call *%rdx /* fn */
+
+ popq %r15
+ popq %r14
+ popq %r13
+ popq %r12
+ popq %rbp
+ popq %rbx
+ ret
diff --git a/src/libcgo/cgocall.c b/src/libcgo/cgocall.c
deleted file mode 100644
index 13843d400..000000000
--- a/src/libcgo/cgocall.c
+++ /dev/null
@@ -1,308 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-#define _GNU_SOURCE
-#include <stdio.h>
-#include <errno.h>
-#include <linux/futex.h>
-#include <sys/syscall.h>
-#include <sys/time.h>
-#include <pthread.h>
-#include <stdint.h>
-#include <string.h>
-#include <stdlib.h>
-
-#define nil ((void*)0)
-
-/*
- * gcc implementation of src/pkg/runtime/linux/thread.c
- */
-typedef struct Lock Lock;
-typedef struct Note Note;
-typedef uint32_t uint32;
-
-struct Lock
-{
- uint32 key;
- uint32 sema; // ignored
-};
-
-struct Note
-{
- Lock lock;
- uint32 pad;
-};
-
-static struct timespec longtime =
-{
- 1<<30, // 34 years
- 0
-};
-
-static int
-cas(uint32 *val, uint32 old, uint32 new)
-{
- int ret;
-
- __asm__ __volatile__(
- "lock; cmpxchgl %2, 0(%3)\n"
- "setz %%al\n"
- : "=a" (ret)
- : "a" (old),
- "r" (new),
- "r" (val)
- : "memory", "cc"
- );
-
- return ret & 1;
-}
-
-static void
-futexsleep(uint32 *addr, uint32 val)
-{
- int ret;
-
- ret = syscall(SYS_futex, (int*)addr, FUTEX_WAIT, val, &longtime, nil, 0);
- if(ret >= 0 || errno == EAGAIN || errno == EINTR)
- return;
- fprintf(stderr, "futexsleep: %s\n", strerror(errno));
- *(int*)0 = 0;
-}
-
-static void
-futexwakeup(uint32 *addr)
-{
- int ret;
-
- ret = syscall(SYS_futex, (int*)addr, FUTEX_WAKE, 1, nil, nil, 0);
- if(ret >= 0)
- return;
- fprintf(stderr, "futexwakeup: %s\n", strerror(errno));
- *(int*)0 = 0;
-}
-
-static void
-futexlock(Lock *l)
-{
- uint32 v;
-
-again:
- v = l->key;
- if((v&1) == 0){
- if(cas(&l->key, v, v|1)){
- // Lock wasn't held; we grabbed it.
- return;
- }
- goto again;
- }
-
- if(!cas(&l->key, v, v+2))
- goto again;
-
- futexsleep(&l->key, v+2);
- for(;;){
- v = l->key;
- if((int)v < 2) {
- fprintf(stderr, "futexsleep: invalid key %d\n", (int)v);
- *(int*)0 = 0;
- }
- if(cas(&l->key, v, v-2))
- break;
- }
- goto again;
-}
-
-static void
-futexunlock(Lock *l)
-{
- uint32 v;
-
-again:
- v = l->key;
- if((v&1) == 0)
- *(int*)0 = 0;
- if(!cas(&l->key, v, v&~1))
- goto again;
-
- // If there were waiters, wake one.
- if(v & ~1)
- futexwakeup(&l->key);
-}
-
-static void
-lock(Lock *l)
-{
- futexlock(l);
-}
-
-static void
-unlock(Lock *l)
-{
- futexunlock(l);
-}
-
-void
-noteclear(Note *n)
-{
- n->lock.key = 0;
- futexlock(&n->lock);
-}
-
-static void
-notewakeup(Note *n)
-{
- futexunlock(&n->lock);
-}
-
-static void
-notesleep(Note *n)
-{
- futexlock(&n->lock);
- futexunlock(&n->lock);
-}
-
-/*
- * runtime Cgo server.
- * gcc half of src/pkg/runtime/cgocall.c
- */
-
-typedef struct CgoWork CgoWork;
-typedef struct CgoServer CgoServer;
-typedef struct Cgo Cgo;
-
-struct Cgo
-{
- Lock lock;
- CgoServer *idle;
- CgoWork *whead;
- CgoWork *wtail;
-};
-
-struct CgoServer
-{
- CgoServer *next;
- Note note;
- CgoWork *work;
-};
-
-struct CgoWork
-{
- CgoWork *next;
- Note note;
- void (*fn)(void*);
- void *arg;
-};
-
-Cgo cgo;
-
-static void newserver(void);
-
-void
-initcgo(void)
-{
- newserver();
-}
-
-static void* go_pthread(void*);
-
-/*
- * allocate servers to handle any work that has piled up
- * and one more server to sit idle and wait for new work.
- */
-static void
-newserver(void)
-{
- CgoServer *f;
- CgoWork *w, *next;
- pthread_t p;
-
- lock(&cgo.lock);
- // kick off new servers with work to do
- for(w=cgo.whead; w; w=next) {
- next = w;
- w->next = nil;
- f = malloc(sizeof *f);
- memset(f, 0, sizeof *f);
- f->work = w;
- noteclear(&f->note);
- notewakeup(&f->note);
- if(pthread_create(&p, nil, go_pthread, f) < 0) {
- fprintf(stderr, "pthread_create: %s\n", strerror(errno));
- *(int*)0 = 0;
- }
- }
- cgo.whead = nil;
- cgo.wtail = nil;
-
- // kick off one more server to sit idle
- if(cgo.idle == nil) {
- f = malloc(sizeof *f);
- memset(f, 0, sizeof *f);
- f->next = cgo.idle;
- noteclear(&f->note);
- cgo.idle = f;
- if(pthread_create(&p, nil, go_pthread, f) < 0) {
- fprintf(stderr, "pthread_create: %s\n", strerror(errno));
- *(int*)0 = 0;
- }
- }
- unlock(&cgo.lock);
-}
-
-static void*
-go_pthread(void *v)
-{
- CgoServer *f;
- CgoWork *w;
-
- // newserver queued us; wait for work
- f = v;
- goto wait;
-
- for(;;) {
- // kick off new server to handle requests while we work
- newserver();
-
- // do work
- w = f->work;
- w->fn(w->arg);
- notewakeup(&w->note);
- f->work = nil;
-
- // take some work if available
- lock(&cgo.lock);
- if((w = cgo.whead) != nil) {
- cgo.whead = w->next;
- if(cgo.whead == nil)
- cgo.wtail = nil;
- unlock(&cgo.lock);
- f->work = w;
- continue;
- }
-
- // otherwise queue
- f->work = nil;
- noteclear(&f->note);
- f->next = cgo.idle;
- cgo.idle = f;
- unlock(&cgo.lock);
-
-wait:
- // wait for work
- notesleep(&f->note);
- }
-}
-
-// Helper.
-
-void
-_cgo_malloc(void *p)
-{
- struct a {
- long long n;
- void *ret;
- } *a = p;
-
- a->ret = malloc(a->n);
-}
diff --git a/src/libcgo/darwin_386.c b/src/libcgo/darwin_386.c
new file mode 100644
index 000000000..28a428309
--- /dev/null
+++ b/src/libcgo/darwin_386.c
@@ -0,0 +1,144 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include <pthread.h>
+#include "libcgo.h"
+
+static void* threadentry(void*);
+static pthread_key_t k1, k2;
+
+/* gccism: arrange for inittls to be called at dynamic load time */
+static void inittls(void) __attribute__((constructor));
+
+static void
+inittls(void)
+{
+ uint32 x, y;
+ pthread_key_t tofree[16], k;
+ int i, ntofree;
+ int havek1, havek2;
+
+ /*
+ * Allocate thread-local storage slots for m, g.
+ * The key numbers start at 0x100, and we expect to be
+ * one of the early calls to pthread_key_create, so we
+ * should be able to get pretty low numbers.
+ *
+ * In Darwin/386 pthreads, %gs points at the thread
+ * structure, and each key is an index into the thread-local
+ * storage array that begins at offset 0x48 within in that structure.
+ * It may happen that we are not quite the first function to try
+ * to allocate thread-local storage keys, so instead of depending
+ * on getting 0x100 and 0x101, we try for 0x108 and 0x109,
+ * allocating keys until we get the ones we want and then freeing
+ * the ones we didn't want.
+ *
+ * Thus the final offsets to use in %gs references are
+ * 0x48+4*0x108 = 0x468 and 0x48+4*0x109 = 0x46c.
+ *
+ * The linker and runtime hard-code these constant offsets
+ * from %gs where we expect to find m and g. The code
+ * below verifies that the constants are correct once it has
+ * obtained the keys. Known to ../cmd/8l/obj.c:/468
+ * and to ../pkg/runtime/darwin/386/sys.s:/468
+ *
+ * This is truly disgusting and a bit fragile, but taking care
+ * of it here protects the rest of the system from damage.
+ * The alternative would be to use a global variable that
+ * held the offset and refer to that variable each time we
+ * need a %gs variable (m or g). That approach would
+ * require an extra instruction and memory reference in
+ * every stack growth prolog and would also require
+ * rewriting the code that 8c generates for extern registers.
+ */
+ havek1 = 0;
+ havek2 = 0;
+ ntofree = 0;
+ while(!havek1 || !havek2) {
+ if(pthread_key_create(&k, nil) < 0) {
+ fprintf(stderr, "libcgo: pthread_key_create failed\n");
+ abort();
+ }
+ if(k == 0x108) {
+ havek1 = 1;
+ k1 = k;
+ continue;
+ }
+ if(k == 0x109) {
+ havek2 = 1;
+ k2 = k;
+ continue;
+ }
+ if(ntofree >= nelem(tofree)) {
+ fprintf(stderr, "libcgo: could not obtain pthread_keys\n");
+ fprintf(stderr, "\twanted 0x108 and 0x109\n");
+ fprintf(stderr, "\tgot");
+ for(i=0; i<ntofree; i++)
+ fprintf(stderr, " %#x", tofree[i]);
+ fprintf(stderr, "\n");
+ abort();
+ }
+ tofree[ntofree++] = k;
+ }
+
+ for(i=0; i<ntofree; i++)
+ pthread_key_delete(tofree[i]);
+
+ /*
+ * We got the keys we wanted. Make sure that we observe
+ * updates to k1 at 0x468, to verify that the TLS array
+ * offset from %gs hasn't changed.
+ */
+ pthread_setspecific(k1, (void*)0x12345678);
+ asm volatile("movl %%gs:0x468, %0" : "=r"(x));
+
+ pthread_setspecific(k1, (void*)0x87654321);
+ asm volatile("movl %%gs:0x468, %0" : "=r"(y));
+
+ if(x != 0x12345678 || y != 0x87654321) {
+ printf("libcgo: thread-local storage %#x not at %%gs:0x468 - x=%#x y=%#x\n", k1, x, y);
+ abort();
+ }
+}
+
+void
+initcgo(void)
+{
+}
+
+void
+libcgo_sys_thread_start(ThreadStart *ts)
+{
+ pthread_attr_t attr;
+ pthread_t p;
+ size_t size;
+
+ pthread_attr_init(&attr);
+ pthread_attr_getstacksize(&attr, &size);
+ ts->g->stackguard = size;
+ pthread_create(&p, &attr, threadentry, ts);
+}
+
+static void*
+threadentry(void *v)
+{
+ ThreadStart ts;
+
+ ts = *(ThreadStart*)v;
+ free(v);
+
+ ts.g->stackbase = (uintptr)&ts;
+
+ /*
+ * libcgo_sys_thread_start set stackguard to stack size;
+ * change to actual guard pointer.
+ */
+ ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096;
+
+ pthread_setspecific(k1, (void*)ts.g);
+ pthread_setspecific(k2, (void*)ts.m);
+
+ crosscall_386(ts.fn);
+ return nil;
+}
diff --git a/src/libcgo/darwin_amd64.c b/src/libcgo/darwin_amd64.c
new file mode 100644
index 000000000..14a409f5e
--- /dev/null
+++ b/src/libcgo/darwin_amd64.c
@@ -0,0 +1,46 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include <pthread.h>
+#include "libcgo.h"
+
+static void* threadentry(void*);
+
+void
+initcgo(void)
+{
+}
+
+void
+libcgo_sys_thread_start(ThreadStart *ts)
+{
+ pthread_attr_t attr;
+ pthread_t p;
+ size_t size;
+
+ pthread_attr_init(&attr);
+ pthread_attr_getstacksize(&attr, &size);
+ ts->g->stackguard = size;
+ pthread_create(&p, &attr, threadentry, ts);
+}
+
+static void*
+threadentry(void *v)
+{
+ ThreadStart ts;
+
+ ts = *(ThreadStart*)v;
+ free(v);
+
+ ts.g->stackbase = (uintptr)&ts;
+
+ /*
+ * libcgo_sys_thread_start set stackguard to stack size;
+ * change to actual guard pointer.
+ */
+ ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096;
+
+ crosscall_amd64(ts.m, ts.g, ts.fn);
+ return nil;
+}
diff --git a/src/libcgo/libcgo.h b/src/libcgo/libcgo.h
new file mode 100644
index 000000000..b4b25accb
--- /dev/null
+++ b/src/libcgo/libcgo.h
@@ -0,0 +1,60 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define nil ((void*)0)
+#define nelem(x) (sizeof(x)/sizeof((x)[0]))
+
+typedef uint32_t uint32;
+typedef uintptr_t uintptr;
+
+/*
+ * The beginning of the per-goroutine structure,
+ * as defined in ../pkg/runtime/runtime.h.
+ * Just enough to edit these two fields.
+ */
+typedef struct G G;
+struct G
+{
+ uintptr stackguard;
+ uintptr stackbase;
+};
+
+/*
+ * Arguments to the libcgo_thread_start call.
+ * Also known to ../pkg/runtime/runtime.h.
+ */
+typedef struct ThreadStart ThreadStart;
+struct ThreadStart
+{
+ uintptr m;
+ G *g;
+ void (*fn)(void);
+};
+
+/*
+ * Called by 5c/6c/8c world.
+ * Makes a local copy of the ThreadStart and
+ * calls libcgo_sys_thread_start(ts).
+ */
+void libcgo_thread_start(ThreadStart *ts);
+
+/*
+ * Creates the new operating system thread (OS, arch dependent).
+ */
+void libcgo_sys_thread_start(ThreadStart *ts);
+
+/*
+ * Call fn in the 6c world, with m and g
+ * set to the given parameters.
+ */
+void crosscall_amd64(uintptr m, G *g, void (*fn)(void));
+
+/*
+ * Call fn in the 8c world.
+ */
+void crosscall_386(void (*fn)(void));
diff --git a/src/libcgo/linux_386.c b/src/libcgo/linux_386.c
new file mode 100644
index 000000000..9d02455cc
--- /dev/null
+++ b/src/libcgo/linux_386.c
@@ -0,0 +1,57 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include <pthread.h>
+#include "libcgo.h"
+
+static void *threadentry(void*);
+
+void
+initcgo(void)
+{
+}
+
+void
+libcgo_sys_thread_start(ThreadStart *ts)
+{
+ pthread_attr_t attr;
+ pthread_t p;
+ size_t size;
+
+ pthread_attr_init(&attr);
+ pthread_attr_getstacksize(&attr, &size);
+ ts->g->stackguard = size;
+ pthread_create(&p, &attr, threadentry, ts);
+}
+
+static void*
+threadentry(void *v)
+{
+ ThreadStart ts;
+
+ ts = *(ThreadStart*)v;
+ free(v);
+
+ ts.g->stackbase = (uintptr)&ts;
+
+ /*
+ * libcgo_sys_thread_start set stackguard to stack size;
+ * change to actual guard pointer.
+ */
+ ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096;
+
+ /*
+ * Set specific keys. On Linux/ELF, the thread local storage
+ * is just before %gs:0. Our dynamic 8.out's reserve 8 bytes
+ * for the two words g and m at %gs:-8 and %gs:-4.
+ */
+ asm volatile (
+ "movl %0, %%gs:-8\n" // MOVL g, -8(GS)
+ "movl %1, %%gs:-4\n" // MOVL m, -4(GS)
+ :: "r"(ts.g), "r"(ts.m)
+ );
+
+ crosscall_386(ts.fn);
+ return nil;
+}
diff --git a/src/libcgo/linux_amd64.c b/src/libcgo/linux_amd64.c
new file mode 100644
index 000000000..14a409f5e
--- /dev/null
+++ b/src/libcgo/linux_amd64.c
@@ -0,0 +1,46 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include <pthread.h>
+#include "libcgo.h"
+
+static void* threadentry(void*);
+
+void
+initcgo(void)
+{
+}
+
+void
+libcgo_sys_thread_start(ThreadStart *ts)
+{
+ pthread_attr_t attr;
+ pthread_t p;
+ size_t size;
+
+ pthread_attr_init(&attr);
+ pthread_attr_getstacksize(&attr, &size);
+ ts->g->stackguard = size;
+ pthread_create(&p, &attr, threadentry, ts);
+}
+
+static void*
+threadentry(void *v)
+{
+ ThreadStart ts;
+
+ ts = *(ThreadStart*)v;
+ free(v);
+
+ ts.g->stackbase = (uintptr)&ts;
+
+ /*
+ * libcgo_sys_thread_start set stackguard to stack size;
+ * change to actual guard pointer.
+ */
+ ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096;
+
+ crosscall_amd64(ts.m, ts.g, ts.fn);
+ return nil;
+}
diff --git a/src/libcgo/linux_arm.c b/src/libcgo/linux_arm.c
new file mode 100644
index 000000000..32d862984
--- /dev/null
+++ b/src/libcgo/linux_arm.c
@@ -0,0 +1 @@
+/* unimplemented */
diff --git a/src/libcgo/nacl_386.c b/src/libcgo/nacl_386.c
new file mode 100644
index 000000000..32d862984
--- /dev/null
+++ b/src/libcgo/nacl_386.c
@@ -0,0 +1 @@
+/* unimplemented */
diff --git a/src/libcgo/util.c b/src/libcgo/util.c
new file mode 100644
index 000000000..a814e018b
--- /dev/null
+++ b/src/libcgo/util.c
@@ -0,0 +1,35 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "libcgo.h"
+
+/* Stub for calling malloc from the other world */
+void
+_cgo_malloc(void *p)
+{
+ struct a {
+ long long n;
+ void *ret;
+ } *a = p;
+
+ a->ret = malloc(a->n);
+}
+
+/* Stub for creating a new thread */
+void
+libcgo_thread_start(ThreadStart *arg)
+{
+ ThreadStart *ts;
+
+ /* Make our own copy that can persist after we return. */
+ ts = malloc(sizeof *ts);
+ if(ts == nil) {
+ fprintf(stderr, "libcgo: out of memory in thread_start\n");
+ abort();
+ }
+ *ts = *arg;
+
+ libcgo_sys_thread_start(ts); /* OS-dependent half */
+}
+