summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--misc/cgo/gmp/Makefile21
-rw-r--r--misc/cgo/gmp/fib.go43
-rw-r--r--misc/cgo/gmp/pi.go (renamed from misc/cgo/gmp/pidigits.go)0
-rw-r--r--misc/cgo/stdio/Makefile17
-rw-r--r--misc/cgo/stdio/fib.go47
-rw-r--r--misc/cgo/stdio/file.go43
-rw-r--r--misc/cgo/stdio/hello.go12
-rw-r--r--src/Make.pkg9
-rw-r--r--src/cmd/6l/asm.c101
-rw-r--r--src/cmd/6l/l.h4
-rw-r--r--src/cmd/6l/obj.c6
-rw-r--r--src/cmd/6l/pass.c10
-rw-r--r--src/cmd/6l/span.c11
-rw-r--r--src/cmd/8l/asm.c109
-rw-r--r--src/cmd/8l/l.h4
-rw-r--r--src/cmd/8l/obj.c21
-rw-r--r--src/cmd/8l/pass.c12
-rw-r--r--src/cmd/8l/span.c11
-rw-r--r--src/cmd/cgo/ast.go1
-rw-r--r--src/cmd/cgo/gcc.go16
-rw-r--r--src/cmd/cgo/main.go38
-rw-r--r--src/cmd/cgo/out.go2
-rw-r--r--src/cmd/ld/elf.h2
-rw-r--r--src/cmd/ld/macho.c321
-rw-r--r--src/cmd/ld/macho.h9
-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
-rw-r--r--src/libmach/executable.c12
-rwxr-xr-xsrc/make.bash2
-rw-r--r--src/pkg/runtime/386/asm.s40
-rw-r--r--src/pkg/runtime/Makefile4
-rw-r--r--src/pkg/runtime/amd64/asm.s20
-rw-r--r--src/pkg/runtime/cgocall.c43
-rw-r--r--src/pkg/runtime/cgocall.h31
-rw-r--r--src/pkg/runtime/darwin/386/sys.s39
-rw-r--r--src/pkg/runtime/darwin/thread.c9
-rwxr-xr-xsrc/pkg/runtime/linux/386/rt0.s6
-rwxr-xr-xsrc/pkg/runtime/linux/386/sys.s25
-rw-r--r--src/pkg/runtime/linux/thread.c2
-rwxr-xr-xsrc/pkg/runtime/mkasmh.sh8
-rw-r--r--src/pkg/runtime/proc.c45
-rw-r--r--src/pkg/runtime/runtime.h10
52 files changed, 1333 insertions, 639 deletions
diff --git a/misc/cgo/gmp/Makefile b/misc/cgo/gmp/Makefile
index b261ff235..c92458c90 100644
--- a/misc/cgo/gmp/Makefile
+++ b/misc/cgo/gmp/Makefile
@@ -5,6 +5,9 @@
include $(GOROOT)/src/Make.$(GOARCH)
TARG=gmp
+
+# Can have plain GOFILES too, but this example doesn't.
+
CGOFILES=\
gmp.go
@@ -15,15 +18,21 @@ CGO_LDFLAGS=-lgmp
# alternate installation of the library:
# CGO_CFLAGS=-I/home/rsc/gmp32/include
# CGO_LDFLAGS+=-L/home/rsc/gmp32/lib
+# Note the += on the second line.
-# Can have plain GOFILES too, but this example doesn't.
+CLEANFILES+=pi fib
include $(GOROOT)/src/Make.pkg
-# Simple test program
+# Simple test programs
+
+# Computes 1000 digits of pi; single-threaded.
+pi: install pi.go
+ $(GC) pi.go
+ $(LD) -o $@ pi.$O
-pidigits.$O: install pidigits.go
- $(GC) pidigits.go
+# Computes 200 Fibonacci numbers; multi-threaded.
+fib: install fib.go
+ $(GC) fib.go
+ $(LD) -o $@ fib.$O
-pidigits: pidigits.$O
- $(LD) -o $@ pidigits.$O
diff --git a/misc/cgo/gmp/fib.go b/misc/cgo/gmp/fib.go
new file mode 100644
index 000000000..02b98b108
--- /dev/null
+++ b/misc/cgo/gmp/fib.go
@@ -0,0 +1,43 @@
+// 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.
+
+// Compute Fibonacci numbers with two goroutines
+// that pass integers back and forth. No actual
+// concurrency, just threads and synchronization
+// and foreign code on multiple pthreads.
+
+package main
+
+import (
+ big "gmp";
+ "runtime";
+)
+
+func fibber(c chan *big.Int, out chan string, n int64) {
+ // Keep the fibbers in dedicated operating system
+ // threads, so that this program tests coordination
+ // between pthreads and not just goroutines.
+ runtime.LockOSThread();
+
+ i := big.NewInt(n);
+ if n == 0 {
+ c <- i;
+ }
+ for {
+ j := <-c;
+ out <- j.String();
+ i.Add(i, j);
+ c <- i;
+ }
+}
+
+func main() {
+ c := make(chan *big.Int);
+ out := make(chan string);
+ go fibber(c, out, 0);
+ go fibber(c, out, 1);
+ for i := 0; i < 200; i++ {
+ println(<-out);
+ }
+}
diff --git a/misc/cgo/gmp/pidigits.go b/misc/cgo/gmp/pi.go
index d22bbc653..d22bbc653 100644
--- a/misc/cgo/gmp/pidigits.go
+++ b/misc/cgo/gmp/pi.go
diff --git a/misc/cgo/stdio/Makefile b/misc/cgo/stdio/Makefile
new file mode 100644
index 000000000..010e17974
--- /dev/null
+++ b/misc/cgo/stdio/Makefile
@@ -0,0 +1,17 @@
+# 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 $(GOROOT)/src/Make.$(GOARCH)
+
+TARG=stdio
+CGOFILES=\
+ file.go
+
+CLEANFILES+=hello fib chain
+
+include $(GOROOT)/src/Make.pkg
+
+%: install %.go
+ $(GC) $*.go
+ $(LD) -o $@ $*.$O
diff --git a/misc/cgo/stdio/fib.go b/misc/cgo/stdio/fib.go
new file mode 100644
index 000000000..972057e11
--- /dev/null
+++ b/misc/cgo/stdio/fib.go
@@ -0,0 +1,47 @@
+// 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.
+
+// Compute Fibonacci numbers with two goroutines
+// that pass integers back and forth. No actual
+// concurrency, just threads and synchronization
+// and foreign code on multiple pthreads.
+
+package main
+
+import (
+ "runtime";
+ "stdio";
+ "strconv";
+)
+
+func fibber(c, out chan int64, i int64) {
+ // Keep the fibbers in dedicated operating system
+ // threads, so that this program tests coordination
+ // between pthreads and not just goroutines.
+ runtime.LockOSThread();
+
+ if i == 0 {
+ c <- i;
+ }
+ for {
+ j := <-c;
+ stdio.Puts(strconv.Itoa64(j));
+ out <- j;
+ <-out;
+ i += j;
+ c <- i;
+ }
+}
+
+func main() {
+ c := make(chan int64);
+ out := make(chan int64);
+ go fibber(c, out, 0);
+ go fibber(c, out, 1);
+ <-out;
+ for i := 0; i < 90; i++ {
+ out <- 1;
+ <-out;
+ }
+}
diff --git a/misc/cgo/stdio/file.go b/misc/cgo/stdio/file.go
new file mode 100644
index 000000000..7935f8f4d
--- /dev/null
+++ b/misc/cgo/stdio/file.go
@@ -0,0 +1,43 @@
+// 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.
+
+/*
+A trivial example of wrapping a C library in Go.
+For a more complex example and explanation,
+see ../gmp/gmp.go.
+*/
+
+package stdio
+
+// TODO(rsc): Remove fflushstdout when C.fflush(C.stdout) works in cgo.
+
+/*
+#include <stdio.h>
+#include <stdlib.h>
+
+void fflushstdout(void) { fflush(stdout); }
+*/
+import "C"
+import "unsafe"
+
+/*
+type File C.FILE
+
+var Stdout = (*File)(C.stdout)
+var Stderr = (*File)(C.stderr)
+
+func (f *File) WriteString(s string) {
+ p := C.CString(s);
+ C.fputs(p, (*C.FILE)(f));
+ C.free(p);
+}
+*/
+
+func Puts(s string) {
+ p := C.CString(s);
+ C.puts(p);
+ C.free(unsafe.Pointer(p));
+ C.fflushstdout();
+}
+
diff --git a/misc/cgo/stdio/hello.go b/misc/cgo/stdio/hello.go
new file mode 100644
index 000000000..8809c9a9c
--- /dev/null
+++ b/misc/cgo/stdio/hello.go
@@ -0,0 +1,12 @@
+// 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.
+
+package main
+
+import "stdio"
+
+func main() {
+// stdio.Stdout.WriteString("hello, world\n");
+ stdio.Puts("hello, world");
+}
diff --git a/src/Make.pkg b/src/Make.pkg
index bc00eeaef..24cd45437 100644
--- a/src/Make.pkg
+++ b/src/Make.pkg
@@ -24,7 +24,7 @@ coverage:
6cov -g $(shell pwd) | grep -v '_test\.go:'
clean:
- rm -rf *.[$(OS)o] *.a [$(OS)].out *.cgo[12].go *.cgo[34].c *.so _obj _test _testmain.go
+ rm -rf *.[$(OS)o] *.a [$(OS)].out *.cgo[12].go *.cgo[34].c *.so _obj _test _testmain.go $(CLEANFILES)
test:
gotest
@@ -81,7 +81,7 @@ dir:
# x.cgo4.c - C implementations compiled with gcc to create dynamic library
#
%.cgo1.go %.cgo2.go %.cgo3.c %.cgo4.c: %.go
- cgo $*.go
+ cgo $(CGO_CFLAGS) $*.go
# The rules above added x.cgo1.go and x.cgo2.go to $(GOFILES),
# added x.cgo3.$O to $OFILES, and added the installed copy of
@@ -96,13 +96,16 @@ RUNTIME_CFLAGS=-I$(GOROOT)/src/pkg/runtime $(RUNTIME_CFLAGS_$(GOARCH))
# Have to run gcc with the right size argument on hybrid 32/64 machines.
_CGO_CFLAGS_386=-m32
_CGO_CFLAGS_amd64=-m64
+_CGO_LDFLAGS_linux=-shared -lpthread -lm
+_CGO_LDFLAGS_darwin=-dynamiclib -Wl,-undefined,dynamic_lookup
+
# Compile x.cgo4.c with gcc to make package_x.so.
%.cgo4.o: %.cgo4.c
gcc $(_CGO_CFLAGS_$(GOARCH)) -fPIC -O2 -o $@ -c $(CGO_CFLAGS) $*.cgo4.c
$(elem)_%.so: %.cgo4.o
- gcc $(_CGO_CFLAGS_$(GOARCH)) -shared -o $@ $*.cgo4.o $(CGO_LDFLAGS)
+ gcc $(_CGO_CFLAGS_$(GOARCH)) $(_CGO_LDFLAGS_$(GOOS)) -o $@ $*.cgo4.o $(CGO_LDFLAGS)
$(pkgdir)/$(dir)/$(elem)_%.so: $(elem)_%.so
@test -d $(GOROOT)/pkg && mkdir -p $(pkgdir)/$(dir)
diff --git a/src/cmd/6l/asm.c b/src/cmd/6l/asm.c
index 5562ee4e3..a0f8524e7 100644
--- a/src/cmd/6l/asm.c
+++ b/src/cmd/6l/asm.c
@@ -425,16 +425,11 @@ asmb(void)
int32 v, magic;
int a, dynsym;
uchar *op1;
- vlong vl, va, startva, fo, w, symo;
+ vlong vl, va, startva, fo, w, symo, machlink;
vlong symdatva = 0x99LL<<32;
ElfEhdr *eh;
ElfPhdr *ph, *pph;
ElfShdr *sh;
- MachoHdr *mh;
- MachoSect *msect;
- MachoSeg *ms;
- MachoDebug *md;
- MachoLoad *ml;
if(debug['v'])
Bprint(&bso, "%5.2f asmb\n", cputime());
@@ -523,6 +518,10 @@ asmb(void)
datblk(v, datsize-v);
}
+ machlink = 0;
+ if(HEADTYPE == 6)
+ machlink = domacholink();
+
symsize = 0;
spsize = 0;
lcsize = 0;
@@ -539,7 +538,7 @@ asmb(void)
symo = HEADR+textsize+datsize;
break;
case 6:
- symo = rnd(HEADR+textsize, INITRND)+rnd(datsize, INITRND);
+ symo = rnd(HEADR+textsize, INITRND)+rnd(datsize, INITRND)+machlink;
break;
case 7:
symo = rnd(HEADR+textsize, INITRND)+datsize;
@@ -607,92 +606,8 @@ asmb(void)
lputb(lcsize); /* line offsets */
break;
case 6:
- /* apple MACH */
- va = HEADR;
- mh = getMachoHdr();
- mh->cpu = MACHO_CPU_AMD64;
- mh->subcpu = MACHO_SUBCPU_X86;
-
- /* segment for zero page */
- ms = newMachoSeg("__PAGEZERO", 0);
- ms->vsize = va;
-
- /* text */
- v = rnd(HEADR+textsize, INITRND);
- ms = newMachoSeg("__TEXT", 1);
- ms->vaddr = va;
- ms->vsize = v;
- ms->filesize = v;
- ms->prot1 = 7;
- ms->prot2 = 5;
-
- msect = newMachoSect(ms, "__text");
- msect->addr = va+HEADR;
- msect->size = v - HEADR;
- msect->off = HEADR;
- msect->flag = 0x400; /* flag - some instructions */
-
- /* data */
- w = datsize+bsssize;
- ms = newMachoSeg("__DATA", 2);
- ms->vaddr = va+v;
- ms->vsize = w;
- ms->fileoffset = v;
- ms->filesize = datsize;
- ms->prot1 = 7;
- ms->prot2 = 3;
-
- msect = newMachoSect(ms, "__data");
- msect->addr = va+v;
- msect->size = datsize;
- msect->off = v;
-
- msect = newMachoSect(ms, "__bss");
- msect->addr = va+v+datsize;
- msect->size = bsssize;
- msect->flag = 1; /* flag - zero fill */
-
- ml = newMachoLoad(5, 42+2); /* unix thread */
- ml->data[0] = 4; /* thread type */
- ml->data[1] = 42; /* word count */
- ml->data[2+32] = entryvalue(); /* start pc */
- ml->data[2+32+1] = entryvalue()>>32;
-
- if(!debug['d']) {
- ml = newMachoLoad(2, 4); /* LC_SYMTAB */
- USED(ml);
-
- ml = newMachoLoad(11, 18); /* LC_DYSYMTAB */
- USED(ml);
-
- ml = newMachoLoad(14, 6); /* LC_LOAD_DYLINKER */
- ml->data[0] = 12; /* offset to string */
- strcpy((char*)&ml->data[1], "/usr/lib/dyld");
- }
-
- if(!debug['s']) {
- ms = newMachoSeg("__SYMDAT", 1);
- ms->vaddr = symdatva;
- ms->vsize = 8+symsize+lcsize;
- ms->fileoffset = symo;
- ms->filesize = 8+symsize+lcsize;
- ms->prot1 = 7;
- ms->prot2 = 5;
-
- md = newMachoDebug();
- md->fileoffset = symo+8;
- md->filesize = symsize;
-
- md = newMachoDebug();
- md->fileoffset = symo+8+symsize;
- md->filesize = lcsize;
- }
-
- a = machowrite();
- if(a > MACHORESERVE)
- diag("MACHORESERVE too small: %d > %d", a, MACHORESERVE);
+ asmbmacho(symdatva, symo);
break;
-
case 7:
/* elf amd-64 */
@@ -965,6 +880,8 @@ datblk(int32 s, int32 n)
curp = p;
if(!p->from.sym->reachable)
diag("unreachable symbol in datblk - %s", p->from.sym->name);
+ if(p->from.sym->type == SMACHO)
+ continue;
l = p->from.sym->value + p->from.offset - s;
c = p->from.scale;
i = 0;
diff --git a/src/cmd/6l/l.h b/src/cmd/6l/l.h
index a1c2ec527..28c37a82c 100644
--- a/src/cmd/6l/l.h
+++ b/src/cmd/6l/l.h
@@ -160,6 +160,8 @@ enum
SIMPORT,
SEXPORT,
+ SMACHO,
+
NHASH = 10007,
NHUNK = 100000,
MINSIZ = 8,
@@ -362,6 +364,7 @@ EXTERN Prog undefp;
EXTERN vlong textstksiz;
EXTERN vlong textarg;
extern char thechar;
+EXTERN int dynptrsize;
#define UP (&undefp)
@@ -403,6 +406,7 @@ void dobss(void);
void dodata(void);
void doelf(void);
void doinit(void);
+void domacho(void);
void doprof1(void);
void doprof2(void);
void dostkoff(void);
diff --git a/src/cmd/6l/obj.c b/src/cmd/6l/obj.c
index ba2dec3b2..4b40cce61 100644
--- a/src/cmd/6l/obj.c
+++ b/src/cmd/6l/obj.c
@@ -191,12 +191,12 @@ main(int argc, char *argv[])
case 6: /* apple MACH */
machoinit();
HEADR = MACHORESERVE;
+ if(INITRND == -1)
+ INITRND = 4096;
if(INITTEXT == -1)
INITTEXT = 4096+HEADR;
if(INITDAT == -1)
INITDAT = 0;
- if(INITRND == -1)
- INITRND = 4096;
break;
case 7: /* elf64 executable */
elfinit();
@@ -393,6 +393,8 @@ main(int argc, char *argv[])
patch();
follow();
doelf();
+ if(HEADTYPE == 6)
+ domacho();
dodata();
dobss();
dostkoff();
diff --git a/src/cmd/6l/pass.c b/src/cmd/6l/pass.c
index c2f560500..2da88bac1 100644
--- a/src/cmd/6l/pass.c
+++ b/src/cmd/6l/pass.c
@@ -142,6 +142,11 @@ dobss(void)
Sym *s;
int32 t;
+ if(dynptrsize > 0) {
+ /* dynamic pointer section between data and bss */
+ datsize = rnd(datsize, 8);
+ }
+
/* now the bss */
bsssize = 0;
for(i=0; i<NHASH; i++)
@@ -154,12 +159,13 @@ dobss(void)
s->size = t;
if(t >= 8)
bsssize = rnd(bsssize, 8);
- s->value = bsssize + datsize;
+ s->value = bsssize + dynptrsize + datsize;
bsssize += t;
}
+
xdefine("data", SBSS, 0);
xdefine("edata", SBSS, datsize);
- xdefine("end", SBSS, bsssize + datsize);
+ xdefine("end", SBSS, dynptrsize + bsssize + datsize);
}
Prog*
diff --git a/src/cmd/6l/span.c b/src/cmd/6l/span.c
index f1eafff00..18bf8cc0c 100644
--- a/src/cmd/6l/span.c
+++ b/src/cmd/6l/span.c
@@ -237,6 +237,12 @@ asmsym(void)
putsymb(s->name, 'D', s->value+INITDAT, s->version, s->gotype);
continue;
+ case SMACHO:
+ if(!s->reachable)
+ continue;
+ putsymb(s->name, 'D', s->value+INITDAT+datsize+bsssize, s->version, s->gotype);
+ continue;
+
case SBSS:
if(!s->reachable)
continue;
@@ -715,6 +721,11 @@ vaddr(Adr *a)
v += INITTEXT; /* TO DO */
v += s->value;
break;
+ case SMACHO:
+ if(!s->reachable)
+ sysfatal("unreachable symbol in vaddr - %s", s->name);
+ v += INITDAT + datsize + s->value;
+ break;
default:
if(!s->reachable)
diag("unreachable symbol in vaddr - %s", s->name);
diff --git a/src/cmd/8l/asm.c b/src/cmd/8l/asm.c
index 449467a5c..c70af7072 100644
--- a/src/cmd/8l/asm.c
+++ b/src/cmd/8l/asm.c
@@ -257,7 +257,7 @@ needlib(char *name)
Sym *s;
/* reuse hash code in symbol table */
- p = smprint(".elfload.%s", name);
+ p = smprint(".dynlib.%s", name);
s = lookup(p, 0);
if(s->type == 0) {
s->type = 100; // avoid SDATA, etc.
@@ -414,8 +414,8 @@ asmb(void)
{
Prog *p;
int32 v, magic;
- int a, dynsym;
- uint32 va, fo, w, symo, startva;
+ int a, i, dynsym;
+ uint32 va, fo, w, symo, startva, machlink;
uchar *op1;
ulong expectpc;
ElfEhdr *eh;
@@ -547,6 +547,10 @@ asmb(void)
datblk(v, datsize-v);
}
+ machlink = 0;
+ if(HEADTYPE == 6)
+ machlink = domacholink();
+
symsize = 0;
spsize = 0;
lcsize = 0;
@@ -572,7 +576,7 @@ asmb(void)
symo = HEADR+textsize+datsize;
break;
case 6:
- symo = rnd(HEADR+textsize, INITRND)+rnd(datsize, INITRND);
+ symo = rnd(HEADR+textsize, INITRND)+rnd(datsize, INITRND)+machlink;
break;
case 7:
case 8:
@@ -740,89 +744,7 @@ asmb(void)
break;
case 6:
- /* apple MACH */
- va = HEADR;
- mh = getMachoHdr();
- mh->cpu = MACHO_CPU_386;
- mh->subcpu = MACHO_SUBCPU_X86;
-
- /* segment for zero page */
- ms = newMachoSeg("__PAGEZERO", 0);
- ms->vsize = va;
-
- /* text */
- v = rnd(HEADR+textsize, INITRND);
- ms = newMachoSeg("__TEXT", 1);
- ms->vaddr = va;
- ms->vsize = v;
- ms->filesize = v;
- ms->prot1 = 7;
- ms->prot2 = 5;
-
- msect = newMachoSect(ms, "__text");
- msect->addr = va+HEADR;
- msect->size = v - HEADR;
- msect->off = HEADR;
- msect->flag = 0x400; /* flag - some instructions */
-
- /* data */
- w = datsize+bsssize;
- ms = newMachoSeg("__DATA", 2);
- ms->vaddr = va+v;
- ms->vsize = w;
- ms->fileoffset = v;
- ms->filesize = datsize;
- ms->prot1 = 7;
- ms->prot2 = 3;
-
- msect = newMachoSect(ms, "__data");
- msect->addr = va+v;
- msect->size = datsize;
- msect->off = v;
-
- msect = newMachoSect(ms, "__bss");
- msect->addr = va+v+datsize;
- msect->size = bsssize;
- msect->flag = 1; /* flag - zero fill */
-
- ml = newMachoLoad(5, 16+2); /* unix thread */
- ml->data[0] = 1; /* thread type */
- ml->data[1] = 16; /* word count */
- ml->data[2+10] = entryvalue(); /* start pc */
-
- if(!debug['d']) {
- ml = newMachoLoad(2, 4); /* LC_SYMTAB */
- USED(ml);
-
- ml = newMachoLoad(11, 18); /* LC_DYSYMTAB */
- USED(ml);
-
- ml = newMachoLoad(14, 6); /* LC_LOAD_DYLINKER */
- ml->data[0] = 12; /* offset to string */
- strcpy((char*)&ml->data[1], "/usr/lib/dyld");
- }
-
- if(!debug['s']) {
- ms = newMachoSeg("__SYMDAT", 1);
- ms->vaddr = symdatva;
- ms->vsize = 8+symsize+lcsize;
- ms->fileoffset = symo;
- ms->filesize = 8+symsize+lcsize;
- ms->prot1 = 7;
- ms->prot2 = 5;
-
- md = newMachoDebug();
- md->fileoffset = symo+8;
- md->filesize = symsize;
-
- md = newMachoDebug();
- md->fileoffset = symo+8+symsize;
- md->filesize = lcsize;
- }
-
- a = machowrite();
- if(a > MACHORESERVE)
- diag("MACHORESERVE too small: %d > %d", a, MACHORESERVE);
+ asmbmacho(symdatva, symo);
break;
case 7:
@@ -963,6 +885,17 @@ asmb(void)
ph->type = PT_DYNAMIC;
ph->flags = PF_R + PF_W;
phsh(ph, sh);
+
+ /*
+ * Thread-local storage segment (really just size).
+ */
+ if(tlsoffset != 0) {
+ ph = newElfPhdr();
+ ph->type = PT_TLS;
+ ph->flags = PF_R;
+ ph->memsz = -tlsoffset;
+ ph->align = 4;
+ }
}
ph = newElfPhdr();
@@ -1105,6 +1038,8 @@ datblk(int32 s, int32 n)
curp = p;
if(!p->from.sym->reachable)
diag("unreachable symbol in datblk - %s", p->from.sym->name);
+ if(p->from.sym->type == SMACHO)
+ continue;
l = p->from.sym->value + p->from.offset - s;
c = p->from.scale;
i = 0;
diff --git a/src/cmd/8l/l.h b/src/cmd/8l/l.h
index cc5901fcb..1959b2c74 100644
--- a/src/cmd/8l/l.h
+++ b/src/cmd/8l/l.h
@@ -151,6 +151,8 @@ enum
SIMPORT,
SEXPORT,
+ SMACHO, /* pointer to mach-o imported symbol */
+
NHASH = 10007,
NHUNK = 100000,
MINSIZ = 4,
@@ -272,6 +274,7 @@ EXTERN Prog* curtext;
EXTERN Prog* datap;
EXTERN Prog* edatap;
EXTERN int32 datsize;
+EXTERN int32 dynptrsize;
EXTERN char debug[128];
EXTERN char literal[32];
EXTERN Prog* etextp;
@@ -311,6 +314,7 @@ EXTERN int version;
EXTERN Prog zprg;
EXTERN int dtype;
EXTERN char thechar;
+EXTERN int tlsoffset;
EXTERN Adr* reloca;
EXTERN int doexp, dlm;
diff --git a/src/cmd/8l/obj.c b/src/cmd/8l/obj.c
index 4b6532568..aa197be53 100644
--- a/src/cmd/8l/obj.c
+++ b/src/cmd/8l/obj.c
@@ -213,6 +213,11 @@ main(int argc, char *argv[])
Bprint(&bso, "HEADR = 0x%ld\n", HEADR);
break;
case 6: /* apple MACH */
+ /*
+ * OS X system constant - offset from %gs to our TLS.
+ * Explained in ../../libcgo/darwin_386.c.
+ */
+ tlsoffset = 0x468;
machoinit();
HEADR = MACHORESERVE;
if(INITTEXT == -1)
@@ -223,6 +228,13 @@ main(int argc, char *argv[])
INITRND = 4096;
break;
case 7: /* elf32 executable */
+ /*
+ * Linux ELF uses TLS offsets negative from %gs.
+ * Translate 0(GS) and 4(GS) into -8(GS) and -4(GS).
+ * Also known to ../../pkg/runtime/linux/386/sys.s
+ * and ../../libcgo/linux_386.c.
+ */
+ tlsoffset = -8;
elfinit();
HEADR = ELFRESERVE;
if(INITTEXT == -1)
@@ -373,6 +385,8 @@ main(int argc, char *argv[])
patch();
follow();
doelf();
+ if(HEADTYPE == 6)
+ domacho();
dodata();
dostkoff();
if(debug['p'])
@@ -592,11 +606,14 @@ zaddr(Biobuf *f, Adr *a, Sym *h[])
a->type = Bgetc(f);
if(t & T_GOTYPE)
a->gotype = h[Bgetc(f)];
+
+ t = a->type;
+ if(t == D_INDIR+D_GS)
+ a->offset += tlsoffset;
+
s = a->sym;
if(s == S)
return;
-
- t = a->type;
if(t != D_AUTO && t != D_PARAM) {
if(a->gotype)
s->gotype = a->gotype;
diff --git a/src/cmd/8l/pass.c b/src/cmd/8l/pass.c
index 7ce419e8f..c624f750a 100644
--- a/src/cmd/8l/pass.c
+++ b/src/cmd/8l/pass.c
@@ -125,6 +125,11 @@ dodata(void)
datsize += u;
}
+ if(dynptrsize > 0) {
+ /* dynamic pointer section between data and bss */
+ datsize = rnd(datsize, 4);
+ }
+
/* now the bss */
bsssize = 0;
for(i=0; i<NHASH; i++)
@@ -135,12 +140,13 @@ dodata(void)
continue;
t = s->value;
s->size = t;
- s->value = bsssize + datsize;
+ s->value = bsssize + dynptrsize + datsize;
bsssize += t;
}
+
xdefine("data", SBSS, 0);
xdefine("edata", SBSS, datsize);
- xdefine("end", SBSS, bsssize + datsize);
+ xdefine("end", SBSS, dynptrsize + bsssize + datsize);
}
Prog*
@@ -570,7 +576,7 @@ dostkoff(void)
p = appendp(p); // load g into CX
p->as = AMOVL;
p->from.type = D_INDIR+D_GS;
- p->from.offset = 0;
+ p->from.offset = tlsoffset + 0;
p->to.type = D_CX;
if(debug['K']) {
diff --git a/src/cmd/8l/span.c b/src/cmd/8l/span.c
index 71607fcf2..6f62f9b4d 100644
--- a/src/cmd/8l/span.c
+++ b/src/cmd/8l/span.c
@@ -219,6 +219,12 @@ asmsym(void)
putsymb(s->name, 'D', s->value+INITDAT, s->version, s->gotype);
continue;
+ case SMACHO:
+ if(!s->reachable)
+ continue;
+ putsymb(s->name, 'D', s->value+INITDAT+datsize+bsssize, s->version, s->gotype);
+ continue;
+
case SBSS:
if(!s->reachable)
continue;
@@ -611,6 +617,11 @@ vaddr(Adr *a)
sysfatal("unreachable symbol in vaddr - %s", s->name);
v += s->value;
break;
+ case SMACHO:
+ if(!s->reachable)
+ sysfatal("unreachable symbol in vaddr - %s", s->name);
+ v += INITDAT + datsize + s->value;
+ break;
default:
if(!s->reachable)
sysfatal("unreachable symbol in vaddr - %s", s->name);
diff --git a/src/cmd/cgo/ast.go b/src/cmd/cgo/ast.go
index 9b122676c..57500680b 100644
--- a/src/cmd/cgo/ast.go
+++ b/src/cmd/cgo/ast.go
@@ -36,6 +36,7 @@ type Prog struct {
Vardef map[string]*Type;
Funcdef map[string]*FuncType;
PtrSize int64;
+ GccOptions []string;
}
// A Type collects information about a type in both the C and Go worlds.
diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go
index e3f526845..f573b98cb 100644
--- a/src/cmd/cgo/gcc.go
+++ b/src/cmd/cgo/gcc.go
@@ -172,6 +172,17 @@ func (p *Prog) loadDebugInfo() {
p.Typedef = conv.typedef;
}
+func concat(a, b []string) []string {
+ c := make([]string, len(a)+len(b));
+ for i, s := range a {
+ c[i] = s;
+ }
+ for i, s := range b {
+ c[i+len(a)] = s;
+ }
+ return c;
+}
+
// gccDebug runs gcc -gdwarf-2 over the C program stdin and
// returns the corresponding DWARF data and any messages
// printed to standard error.
@@ -182,7 +193,7 @@ func (p *Prog) gccDebug(stdin []byte) (*dwarf.Data, string) {
}
tmp := "_cgo_.o";
- _, stderr, ok := run(stdin, []string{
+ base := []string{
"gcc",
machine,
"-Wall", // many warnings
@@ -192,7 +203,8 @@ func (p *Prog) gccDebug(stdin []byte) (*dwarf.Data, string) {
"-c", // do not link
"-xc", // input language is C
"-", // read input from standard input
- });
+ };
+ _, stderr, ok := run(stdin, concat(base, p.GccOptions));
if !ok {
return nil, string(stderr);
}
diff --git a/src/cmd/cgo/main.go b/src/cmd/cgo/main.go
index b629f0a22..eb04fa77d 100644
--- a/src/cmd/cgo/main.go
+++ b/src/cmd/cgo/main.go
@@ -11,15 +11,13 @@
package main
import (
- "flag";
"fmt";
"go/ast";
"os";
)
func usage() {
- fmt.Fprint(os.Stderr, "usage: cgo file.cgo\n");
- flag.PrintDefaults();
+ fmt.Fprint(os.Stderr, "usage: cgo [compiler options] file.go\n");
}
var ptrSizeMap = map[string]int64 {
@@ -28,9 +26,24 @@ var ptrSizeMap = map[string]int64 {
"arm": 4
}
+var expandName = map[string]string {
+ "schar": "signed char",
+ "uchar": "unsigned char",
+ "ushort": "unsigned short",
+ "uint": "unsigned int",
+ "ulong": "unsigned long",
+ "longlong": "long long",
+ "ulonglong": "unsigned long long",
+}
+
func main() {
- flag.Usage = usage;
- flag.Parse();
+ args := os.Args;
+ if len(args) < 2 {
+ usage();
+ os.Exit(2);
+ }
+ gccOptions := args[1:len(args)-1];
+ input := args[len(args)-1];
arch := os.Getenv("GOARCH");
if arch == "" {
@@ -41,14 +54,17 @@ func main() {
fatal("unknown architecture %s", arch);
}
- args := flag.Args();
- if len(args) != 1 {
- usage();
- os.Exit(2);
+ p := openProg(input);
+ for _, cref := range p.Crefs {
+ // Convert C.ulong to C.unsigned long, etc.
+ if expand, ok := expandName[cref.Name]; ok {
+ cref.Name = expand;
+ }
}
- p := openProg(args[0]);
+
p.PtrSize = ptrSize;
p.Preamble = p.Preamble + "\n" + builtinProlog;
+ p.GccOptions = gccOptions;
p.loadDebugInfo();
p.Vardef = make(map[string]*Type);
p.Funcdef = make(map[string]*FuncType);
@@ -83,5 +99,5 @@ func main() {
}
p.PackagePath = p.Package;
- p.writeOutput(args[0]);
+ p.writeOutput(input);
}
diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go
index 91473abeb..d2eedc331 100644
--- a/src/cmd/cgo/out.go
+++ b/src/cmd/cgo/out.go
@@ -198,7 +198,7 @@ const cProlog = `
#include "cgocall.h"
#pragma dynld initcgo initcgo "%s/libcgo.so"
-#pragma dynld cgo cgo "%s/libcgo.so"
+#pragma dynld libcgo_thread_start libcgo_thread_start "%s/libcgo.so"
#pragma dynld _cgo_malloc _cgo_malloc "%s/libcgo.so"
#pragma dynld _cgo_free free "%s/libcgo.so"
diff --git a/src/cmd/ld/elf.h b/src/cmd/ld/elf.h
index cb4857248..e0c2bd1b6 100644
--- a/src/cmd/ld/elf.h
+++ b/src/cmd/ld/elf.h
@@ -243,7 +243,7 @@ typedef struct {
#define PT_NOTE 4 /* Auxiliary information. */
#define PT_SHLIB 5 /* Reserved (not used). */
#define PT_PHDR 6 /* Location of program header itself. */
-#define PT_TLS 7 /* Thread local storage segment */
+#define PT_TLS 7 /* Thread local storage segment */
#define PT_LOOS 0x60000000 /* First OS-specific. */
#define PT_HIOS 0x6fffffff /* Last OS-specific. */
#define PT_LOPROC 0x70000000 /* First processor-specific type. */
diff --git a/src/cmd/ld/macho.c b/src/cmd/ld/macho.c
index 159aceb9e..e4fe963ac 100644
--- a/src/cmd/ld/macho.c
+++ b/src/cmd/ld/macho.c
@@ -92,6 +92,22 @@ newMachoDebug(void)
return &xdebug[ndebug++];
}
+
+// Generic linking code.
+
+static uchar *linkdata;
+static uint32 nlinkdata;
+static uint32 mlinkdata;
+
+static uchar *strtab;
+static uint32 nstrtab;
+static uint32 mstrtab;
+
+static char **dylib;
+static int ndylib;
+
+static vlong linkoff;
+
int
machowrite(void)
{
@@ -205,3 +221,308 @@ machowrite(void)
return Boffset(&bso) - o1;
}
+
+static void*
+grow(uchar **dat, uint32 *ndat, uint32 *mdat, uint32 n)
+{
+ uchar *p;
+ uint32 old;
+
+ if(*ndat+n > *mdat) {
+ old = *mdat;
+ *mdat = (*ndat+n)*2 + 128;
+ *dat = realloc(*dat, *mdat);
+ if(*dat == 0) {
+ diag("out of memory");
+ errorexit();
+ }
+ memset(*dat+old, 0, *mdat-old);
+ }
+ p = *dat + *ndat;
+ *ndat += n;
+ return p;
+}
+
+static int
+needlib(char *name)
+{
+ char *p;
+ Sym *s;
+
+ /* reuse hash code in symbol table */
+ p = smprint(".machoload.%s", name);
+ s = lookup(p, 0);
+ if(s->type == 0) {
+ s->type = 100; // avoid SDATA, etc.
+ return 1;
+ }
+ return 0;
+}
+
+void
+domacho(void)
+{
+ int h, nsym, ptrsize;
+ char *p;
+ uchar *dat;
+ uint32 x;
+ Sym *s;
+
+ ptrsize = 4;
+ if(macho64)
+ ptrsize = 8;
+
+ // empirically, string table must begin with " \x00".
+ if(!debug['d'])
+ *(char*)grow(&strtab, &nstrtab, &mstrtab, 2) = ' ';
+
+ nsym = 0;
+ for(h=0; h<NHASH; h++) {
+ for(s=hash[h]; s!=S; s=s->link) {
+ if(!s->reachable || (s->type != SDATA && s->type != SBSS) || s->dynldname == nil)
+ continue;
+ if(debug['d']) {
+ diag("cannot use dynamic loading and -d");
+ errorexit();
+ }
+ s->type = SMACHO;
+ s->value = nsym*ptrsize;
+
+ /* symbol table entry - darwin still puts _ prefixes on all C symbols */
+ x = nstrtab;
+ p = grow(&strtab, &nstrtab, &mstrtab, 1+strlen(s->dynldname)+1);
+ *p++ = '_';
+ strcpy(p, s->dynldname);
+
+ dat = grow(&linkdata, &nlinkdata, &mlinkdata, 8+ptrsize);
+ dat[0] = x;
+ dat[1] = x>>8;
+ dat[2] = x>>16;
+ dat[3] = x>>24;
+ dat[4] = 0x01; // type: N_EXT - external symbol
+
+ if(needlib(s->dynldlib)) {
+ if(ndylib%32 == 0) {
+ dylib = realloc(dylib, (ndylib+32)*sizeof dylib[0]);
+ if(dylib == nil) {
+ diag("out of memory");
+ errorexit();
+ }
+ }
+ dylib[ndylib++] = s->dynldlib;
+ }
+ nsym++;
+ }
+ }
+
+ /*
+ * list of symbol table indexes.
+ * we don't take advantage of the opportunity
+ * to order the symbol table differently from
+ * this list, so it is boring: 0 1 2 3 4 ...
+ */
+ for(x=0; x<nsym; x++) {
+ dat = grow(&linkdata, &nlinkdata, &mlinkdata, 4);
+ dat[0] = x;
+ dat[1] = x>>8;
+ dat[2] = x>>16;
+ dat[3] = x>>24;
+ }
+
+ dynptrsize = nsym*ptrsize;
+}
+
+vlong
+domacholink(void)
+{
+ linkoff = 0;
+ if(nlinkdata > 0) {
+ linkoff = rnd(HEADR+textsize, INITRND) + rnd(datsize, INITRND);
+ seek(cout, linkoff, 0);
+ write(cout, linkdata, nlinkdata);
+ write(cout, strtab, nstrtab);
+ }
+ return rnd(nlinkdata+nstrtab, INITRND);
+}
+
+void
+asmbmacho(vlong symdatva, vlong symo)
+{
+ vlong v, w;
+ vlong va;
+ int a, i, ptrsize;
+ MachoHdr *mh;
+ MachoSect *msect;
+ MachoSeg *ms;
+ MachoDebug *md;
+ MachoLoad *ml;
+
+ /* apple MACH */
+ va = INITTEXT - HEADR;
+ mh = getMachoHdr();
+ switch(thechar){
+ default:
+ diag("unknown mach architecture");
+ errorexit();
+ case '6':
+ mh->cpu = MACHO_CPU_AMD64;
+ mh->subcpu = MACHO_SUBCPU_X86;
+ ptrsize = 8;
+ break;
+ case '8':
+ mh->cpu = MACHO_CPU_386;
+ mh->subcpu = MACHO_SUBCPU_X86;
+ ptrsize = 4;
+ break;
+ }
+
+ /* segment for zero page */
+ ms = newMachoSeg("__PAGEZERO", 0);
+ ms->vsize = va;
+
+ /* text */
+ v = rnd(HEADR+textsize, INITRND);
+ ms = newMachoSeg("__TEXT", 1);
+ ms->vaddr = va;
+ ms->vsize = v;
+ ms->filesize = v;
+ ms->prot1 = 7;
+ ms->prot2 = 5;
+
+ msect = newMachoSect(ms, "__text");
+ msect->addr = INITTEXT;
+ msect->size = textsize;
+ msect->off = INITTEXT - va;
+ msect->flag = 0x400; /* flag - some instructions */
+
+ /* data */
+ w = datsize+dynptrsize+bsssize;
+ ms = newMachoSeg("__DATA", 2+(dynptrsize>0));
+ ms->vaddr = va+v;
+ ms->vsize = w;
+ ms->fileoffset = v;
+ ms->filesize = datsize;
+ ms->prot1 = 7;
+ ms->prot2 = 3;
+
+ msect = newMachoSect(ms, "__data");
+ msect->addr = va+v;
+ msect->size = datsize;
+ msect->off = v;
+
+ if(dynptrsize > 0) {
+ msect = newMachoSect(ms, "__nl_symbol_ptr");
+ msect->addr = va+v+datsize;
+ msect->size = dynptrsize;
+ msect->align = 2;
+ msect->flag = 6; /* section with nonlazy symbol pointers */
+ /*
+ * The reserved1 field is supposed to be the index of
+ * the first entry in the list of symbol table indexes
+ * in isymtab for the symbols we need. We only use
+ * pointers, so we need the entire list, so the index
+ * here should be 0, which luckily is what the Mach-O
+ * writing code emits by default for this not really reserved field.
+ msect->reserved1 = 0; - first indirect symbol table entry we need
+ */
+ }
+
+ msect = newMachoSect(ms, "__bss");
+ msect->addr = va+v+datsize+dynptrsize;
+ msect->size = bsssize;
+ msect->flag = 1; /* flag - zero fill */
+
+ switch(thechar) {
+ default:
+ diag("unknown macho architecture");
+ errorexit();
+ case '6':
+ ml = newMachoLoad(5, 42+2); /* unix thread */
+ ml->data[0] = 4; /* thread type */
+ ml->data[1] = 42; /* word count */
+ ml->data[2+32] = entryvalue(); /* start pc */
+ ml->data[2+32+1] = entryvalue()>>32;
+ break;
+ case '8':
+ ml = newMachoLoad(5, 16+2); /* unix thread */
+ ml->data[0] = 1; /* thread type */
+ ml->data[1] = 16; /* word count */
+ ml->data[2+10] = entryvalue(); /* start pc */
+ break;
+ }
+
+ if(!debug['d']) {
+ int nsym;
+
+ nsym = dynptrsize/ptrsize;
+
+ ms = newMachoSeg("__LINKEDIT", 0);
+ ms->vaddr = va+v+rnd(datsize+dynptrsize+bsssize, INITRND);
+ ms->vsize = nlinkdata+nstrtab;
+ ms->fileoffset = linkoff;
+ ms->filesize = nlinkdata+nstrtab;
+ ms->prot1 = 7;
+ ms->prot2 = 3;
+
+ ml = newMachoLoad(2, 4); /* LC_SYMTAB */
+ ml->data[0] = linkoff; /* symoff */
+ ml->data[1] = nsym; /* nsyms */
+ ml->data[2] = linkoff + nlinkdata; /* stroff */
+ ml->data[3] = nstrtab; /* strsize */
+
+ ml = newMachoLoad(11, 18); /* LC_DYSYMTAB */
+ ml->data[0] = 0; /* ilocalsym */
+ ml->data[1] = 0; /* nlocalsym */
+ ml->data[2] = 0; /* iextdefsym */
+ ml->data[3] = 0; /* nextdefsym */
+ ml->data[4] = 0; /* iundefsym */
+ ml->data[5] = nsym; /* nundefsym */
+ ml->data[6] = 0; /* tocoffset */
+ ml->data[7] = 0; /* ntoc */
+ ml->data[8] = 0; /* modtaboff */
+ ml->data[9] = 0; /* nmodtab */
+ ml->data[10] = 0; /* extrefsymoff */
+ ml->data[11] = 0; /* nextrefsyms */
+ ml->data[12] = linkoff + nlinkdata - nsym*4; /* indirectsymoff */
+ ml->data[13] = nsym; /* nindirectsyms */
+ ml->data[14] = 0; /* extreloff */
+ ml->data[15] = 0; /* nextrel */
+ ml->data[16] = 0; /* locreloff */
+ ml->data[17] = 0; /* nlocrel */
+
+ ml = newMachoLoad(14, 6); /* LC_LOAD_DYLINKER */
+ ml->data[0] = 12; /* offset to string */
+ strcpy((char*)&ml->data[1], "/usr/lib/dyld");
+
+ for(i=0; i<ndylib; i++) {
+ ml = newMachoLoad(12, 4+(strlen(dylib[i])+1+7)/8*2); /* LC_LOAD_DYLIB */
+ ml->data[0] = 24; /* offset of string from beginning of load */
+ ml->data[1] = 0; /* time stamp */
+ ml->data[2] = 0; /* version */
+ ml->data[3] = 0; /* compatibility version */
+ strcpy((char*)&ml->data[4], dylib[i]);
+ }
+ }
+
+ if(!debug['s']) {
+ ms = newMachoSeg("__SYMDAT", 1);
+ ms->vaddr = symdatva;
+ ms->vsize = 8+symsize+lcsize;
+ ms->fileoffset = symo;
+ ms->filesize = 8+symsize+lcsize;
+ ms->prot1 = 7;
+ ms->prot2 = 5;
+
+ md = newMachoDebug();
+ md->fileoffset = symo+8;
+ md->filesize = symsize;
+
+ md = newMachoDebug();
+ md->fileoffset = symo+8+symsize;
+ md->filesize = lcsize;
+ }
+
+ a = machowrite();
+ if(a > MACHORESERVE)
+ diag("MACHORESERVE too small: %d > %d", a, MACHORESERVE);
+}
diff --git a/src/cmd/ld/macho.h b/src/cmd/ld/macho.h
index 747adac2d..a96b2a383 100644
--- a/src/cmd/ld/macho.h
+++ b/src/cmd/ld/macho.h
@@ -61,10 +61,17 @@ void machoinit(void);
* for Header, PHeaders, and SHeaders.
* May waste some.
*/
-#define MACHORESERVE 4096
+#define MACHORESERVE 3*1024
enum {
MACHO_CPU_AMD64 = (1<<24)|7,
MACHO_CPU_386 = 7,
MACHO_SUBCPU_X86 = 3,
+
+ MACHO32SYMSIZE = 12,
+ MACHO64SYMSIZE = 16,
};
+
+void domacho(void);
+vlong domacholink(void);
+void asmbmacho(vlong, vlong);
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 */
+}
+
diff --git a/src/libmach/executable.c b/src/libmach/executable.c
index 0cc7d0f99..075738e9c 100644
--- a/src/libmach/executable.c
+++ b/src/libmach/executable.c
@@ -1,11 +1,11 @@
// Inferno libmach/executable.c
// http://code.google.com/p/inferno-os/source/browse/utils/libmach/executable.c
//
-// Copyright © 1994-1999 Lucent Technologies Inc.
-// Power PC support Copyright © 1995-2004 C H Forsyth (forsyth@terzarima.net).
-// Portions Copyright © 1997-1999 Vita Nuova Limited.
-// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).
-// Revisions Copyright © 2000-2004 Lucent Technologies Inc. and others.
+// Copyright © 1994-1999 Lucent Technologies Inc.
+// Power PC support Copyright © 1995-2004 C H Forsyth (forsyth@terzarima.net).
+// Portions Copyright © 1997-1999 Vita Nuova Limited.
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).
+// Revisions Copyright © 2000-2004 Lucent Technologies Inc. and others.
// Portions Copyright © 2009 The Go Authors. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -1126,6 +1126,8 @@ machdotout(int fd, Fhdr *fp, ExecHdr *hp)
goto bad;
}
sect32++;
+ if (strcmp(sect32->sectname, "__nl_symbol_ptr") == 0)
+ sect32++;
if (strcmp(sect32->sectname, "__bss") == 0) {
bsssize = swal(sect32->size);
} else {
diff --git a/src/make.bash b/src/make.bash
index 5f3643c01..ca5304512 100755
--- a/src/make.bash
+++ b/src/make.bash
@@ -19,7 +19,7 @@ rm -f $GOBIN/quietgcc
cp quietgcc.bash $GOBIN/quietgcc
chmod +x $GOBIN/quietgcc
-for i in lib9 libbio libmach libregexp cmd pkg cmd/ebnflint cmd/godoc cmd/gofmt
+for i in lib9 libbio libmach libregexp cmd pkg libcgo cmd/cgo cmd/ebnflint cmd/godoc cmd/gofmt
do
# The ( ) here are to preserve the current directory
# for the next round despite the cd $i below.
diff --git a/src/pkg/runtime/386/asm.s b/src/pkg/runtime/386/asm.s
index 67b72f73f..5aa73a6b8 100644
--- a/src/pkg/runtime/386/asm.s
+++ b/src/pkg/runtime/386/asm.s
@@ -9,10 +9,19 @@ TEXT _rt0_386(SB),7,$0
MOVL 0(SP), AX // argc
LEAL 4(SP), BX // argv
SUBL $128, SP // plenty of scratch
- ANDL $~7, SP
+ ANDL $~15, SP
MOVL AX, 120(SP) // save argc, argv away
MOVL BX, 124(SP)
+ // if there is an initcgo, call it to let it
+ // initialize and to set up GS. if not,
+ // we set up GS ourselves.
+ MOVL initcgo(SB), AX
+ TESTL AX, AX
+ JZ 3(PC)
+ CALL AX
+ JMP ok
+
// set up %gs
CALL ldt0setup(SB)
@@ -21,9 +30,8 @@ TEXT _rt0_386(SB),7,$0
MOVL tls0(SB), AX
CMPL AX, $0x123
JEQ ok
- MOVL AX, 0
+ MOVL AX, 0 // abort
ok:
-
// set up m and g "registers"
LEAL g0(SB), CX
MOVL CX, g
@@ -285,13 +293,29 @@ TEXT ldt0setup(SB),7,$16
CALL setldt(SB)
RET
-GLOBL m0+0(SB), $1024
-GLOBL g0+0(SB), $1024
-
-GLOBL tls0+0(SB), $32
-
TEXT emptyfunc(SB),0,$0
RET
TEXT abort(SB),7,$0
INT $0x3
+
+// runcgo(void(*fn)(void*), void *arg)
+// Just call fn(arg), but first align the stack
+// appropriately for the gcc ABI.
+TEXT runcgo(SB),7,$16
+ MOVL fn+0(FP), AX
+ MOVL arg+4(FP), BX
+ MOVL SP, CX
+ ANDL $~15, SP // alignment for gcc ABI
+ MOVL CX, 4(SP)
+ MOVL BX, 0(SP)
+ CALL AX
+ MOVL 4(SP), SP
+ RET
+
+
+GLOBL m0(SB), $1024
+GLOBL g0(SB), $1024
+GLOBL tls0(SB), $32
+GLOBL initcgo(SB), $4
+
diff --git a/src/pkg/runtime/Makefile b/src/pkg/runtime/Makefile
index efd3f37de..ab24a7765 100644
--- a/src/pkg/runtime/Makefile
+++ b/src/pkg/runtime/Makefile
@@ -83,8 +83,8 @@ clean: clean-local
clean-local:
rm -f runtime.acid cgo2c */asm.h
-$(GOARCH)/asm.h: runtime.acid mkasmh
- ./mkasmh >$@.x
+$(GOARCH)/asm.h: runtime.acid mkasmh.sh
+ ./mkasmh.sh >$@.x
mv -f $@.x $@
cgo2c: cgo2c.c
diff --git a/src/pkg/runtime/amd64/asm.s b/src/pkg/runtime/amd64/asm.s
index 0674d518c..6cb6d5c77 100644
--- a/src/pkg/runtime/amd64/asm.s
+++ b/src/pkg/runtime/amd64/asm.s
@@ -270,3 +270,23 @@ TEXT jmpdefer(SB), 7, $0
LEAQ -8(BX), SP // caller sp after CALL
SUBQ $5, (SP) // return to CALL again
JMP AX // but first run the deferred function
+
+// runcgo(void(*fn)(void*), void *arg)
+// Call fn(arg), but align the stack
+// appropriately for the gcc ABI
+// and also save g and m across the call,
+// since the foreign code might reuse them.
+TEXT runcgo(SB),7,$32
+ MOVQ fn+0(FP),AX
+ MOVQ arg+8(FP),DI // DI = first argument in AMD64 ABI
+ MOVQ SP, CX
+ ANDQ $~15, SP // alignment for gcc ABI
+ MOVQ g, 24(SP) // save old g, m, SP
+ MOVQ m, 16(SP)
+ MOVQ CX, 8(SP)
+ CALL AX
+ MOVQ 16(SP), m // restore
+ MOVQ 24(SP), g
+ MOVQ 8(SP), SP
+ RET
+
diff --git a/src/pkg/runtime/cgocall.c b/src/pkg/runtime/cgocall.c
index 9022267a1..a47560395 100644
--- a/src/pkg/runtime/cgocall.c
+++ b/src/pkg/runtime/cgocall.c
@@ -5,39 +5,32 @@
#include "runtime.h"
#include "cgocall.h"
-Cgo *cgo; /* filled in by dynamic linker when Cgo is available */
+void *initcgo; /* filled in by dynamic linker when Cgo is available */
int64 ncgocall;
+void sys·entersyscall(void);
+void sys·exitsyscall(void);
void
cgocall(void (*fn)(void*), void *arg)
{
- CgoWork w;
- CgoServer *s;
+ if(initcgo == nil)
+ throw("cgocall unavailable");
ncgocall++;
- if(cgo == nil)
- throw("cgocall unavailable");
-
- noteclear(&w.note);
- w.next = nil;
- w.fn = fn;
- w.arg = arg;
- lock(&cgo->lock);
- if((s = cgo->idle) != nil) {
- cgo->idle = s->next;
- s->work = &w;
- unlock(&cgo->lock);
- notewakeup(&s->note);
- } else {
- if(cgo->whead == nil) {
- cgo->whead = &w;
- } else
- cgo->wtail->next = &w;
- cgo->wtail = &w;
- unlock(&cgo->lock);
- }
- notesleep(&w.note);
+ /*
+ * Announce we are entering a system call
+ * so that the scheduler knows to create another
+ * M to run goroutines while we are in the
+ * foreign code.
+ */
+ sys·entersyscall();
+ g->cgofn = fn;
+ g->cgoarg = arg;
+ g->status = Gcgocall;
+ gosched();
+ sys·exitsyscall();
+ return;
}
void
diff --git a/src/pkg/runtime/cgocall.h b/src/pkg/runtime/cgocall.h
index 5352a2f92..816c426d7 100644
--- a/src/pkg/runtime/cgocall.h
+++ b/src/pkg/runtime/cgocall.h
@@ -4,39 +4,8 @@
/*
* Cgo interface.
- * Dynamically linked shared libraries compiled with gcc
- * know these data structures and functions too.
- * See ../../libcgo/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;
-};
-
void cgocall(void (*fn)(void*), void*);
-
void *cmalloc(uintptr);
void cfree(void*);
diff --git a/src/pkg/runtime/darwin/386/sys.s b/src/pkg/runtime/darwin/386/sys.s
index bded7e421..617847039 100644
--- a/src/pkg/runtime/darwin/386/sys.s
+++ b/src/pkg/runtime/darwin/386/sys.s
@@ -137,9 +137,6 @@ TEXT bsdthread_start(SB),7,$0
POPL AX
POPL AX
POPAL
- SHLL $3, DI // segment# is ldt*8 + 7.
- ADDL $7, DI
- MOVW DI, GS
// Now segment is established. Initialize m, g.
MOVL AX, g
@@ -243,36 +240,51 @@ int i386_set_ldt(int, const union ldt_entry *, int);
// setldt(int entry, int address, int limit)
TEXT setldt(SB),7,$32
+ MOVL address+4(FP), BX // aka base
+ MOVL limit+8(FP), CX
+
+ /*
+ * When linking against the system libraries,
+ * we use its pthread_create and let it set up %gs
+ * for us. When we do that, the private storage
+ * we get is not at 0(GS) but at 0x468(GS).
+ * To insulate the rest of the tool chain from this ugliness,
+ * 8l rewrites 0(GS) into 0x468(GS) for us.
+ * To accommodate that rewrite, we translate the
+ * address and limit here so that 0x468(GS) maps to 0(address).
+ *
+ * See ../../../../libcgo/darwin_386.c for the derivation
+ * of the constant.
+ */
+ SUBL $0x468, BX
+ ADDL $0x468, CX
+
// set up data_desc
LEAL 16(SP), AX // struct data_desc
MOVL $0, 0(AX)
MOVL $0, 4(AX)
- MOVL address+4(FP), BX // aka base
MOVW BX, 2(AX)
SHRL $16, BX
MOVB BX, 4(AX)
SHRL $8, BX
MOVB BX, 7(AX)
- MOVL limit+8(FP), BX
- MOVW BX, 0(AX)
- SHRL $16, BX
- ANDL $0x0F, BX
- ORL $0x40, BX // 32-bit operand size
- MOVB BX, 6(AX)
+ MOVW CX, 0(AX)
+ SHRL $16, CX
+ ANDL $0x0F, CX
+ ORL $0x40, CX // 32-bit operand size
+ MOVB CX, 6(AX)
MOVL $0xF2, 5(AX) // r/w data descriptor, dpl=3, present
// call i386_set_ldt(entry, desc, 1)
- MOVL entry+0(FP), BX
- MOVL BX, 0(SP)
+ MOVL $0xffffffff, 0(SP) // auto-allocate entry and return in AX
MOVL AX, 4(SP)
MOVL $1, 8(SP)
CALL i386_set_ldt(SB)
// compute segment selector - (entry*8+7)
- MOVL entry+0(FP), AX
SHLL $3, AX
ADDL $7, AX
MOVW AX, GS
@@ -285,3 +297,4 @@ TEXT i386_set_ldt(SB),7,$0
CALL notok(SB)
RET
+GLOBL tlsoffset(SB),$4
diff --git a/src/pkg/runtime/darwin/thread.c b/src/pkg/runtime/darwin/thread.c
index c394ab490..7e6d7c2d7 100644
--- a/src/pkg/runtime/darwin/thread.c
+++ b/src/pkg/runtime/darwin/thread.c
@@ -144,15 +144,18 @@ notewakeup(Note *n)
void
osinit(void)
{
- // Register our thread-creation callback (see {amd64,386}/sys.s).
- bsdthread_register();
+ // Register our thread-creation callback (see {amd64,386}/sys.s)
+ // but only if we're not using cgo. If we are using cgo we need
+ // to let the C pthread libary install its own thread-creation callback.
+ extern void (*libcgo_thread_start)(void*);
+ if(libcgo_thread_start == nil)
+ bsdthread_register();
}
void
newosproc(M *m, G *g, void *stk, void (*fn)(void))
{
m->tls[0] = m->id; // so 386 asm can find it
-
if(0){
printf("newosproc stk=%p m=%p g=%p fn=%p id=%d/%d ostk=%p\n",
stk, m, g, fn, m->id, m->tls[0], &m);
diff --git a/src/pkg/runtime/linux/386/rt0.s b/src/pkg/runtime/linux/386/rt0.s
index d5d270be2..4d2345d8c 100755
--- a/src/pkg/runtime/linux/386/rt0.s
+++ b/src/pkg/runtime/linux/386/rt0.s
@@ -5,11 +5,5 @@
// Darwin and Linux use the same linkage to main
TEXT _rt0_386_linux(SB),7,$0
- MOVL initcgo(SB), AX
- TESTL AX, AX
- JZ 2(PC)
- CALL AX
-
JMP _rt0_386(SB)
-GLOBL initcgo(SB), $4
diff --git a/src/pkg/runtime/linux/386/sys.s b/src/pkg/runtime/linux/386/sys.s
index fe07ddd54..3b0babcd0 100755
--- a/src/pkg/runtime/linux/386/sys.s
+++ b/src/pkg/runtime/linux/386/sys.s
@@ -198,15 +198,28 @@ TEXT sigaltstack(SB),7,$-8
// setldt(int entry, int address, int limit)
TEXT setldt(SB),7,$32
+ MOVL entry+0(FP), BX // entry
+ MOVL address+4(FP), CX // base address
+
+ /*
+ * When linking against the system libraries,
+ * we use its pthread_create and let it set up %gs
+ * for us. When we do that, the private storage
+ * we get is not at 0(GS), 4(GS), but -8(GS), -4(GS).
+ * To insulate the rest of the tool chain from this
+ * ugliness, 8l rewrites 0(GS) into -8(GS) for us.
+ * To accommodate that rewrite, we translate
+ * the address here and bump the limit to 0xffffffff (no limit)
+ * so that -8(GS) maps to 0(address).
+ */
+ ADDL $0x8, CX // address
+
// set up user_desc
LEAL 16(SP), AX // struct user_desc
- MOVL entry+0(FP), BX // entry
MOVL BX, 0(AX)
- MOVL address+4(FP), BX // base address
- MOVL BX, 4(AX)
- MOVL limit+8(FP), BX // limit
- MOVL BX, 8(AX)
- MOVL $(SEG_32BIT|USEABLE|CONTENTS_DATA), 12(AX) // flag bits
+ MOVL CX, 4(AX)
+ MOVL $0xfffff, 8(AX)
+ MOVL $(SEG_32BIT|LIMIT_IN_PAGES|USEABLE|CONTENTS_DATA), 12(AX) // flag bits
// call modify_ldt
MOVL $1, BX // func = 1 (write)
diff --git a/src/pkg/runtime/linux/thread.c b/src/pkg/runtime/linux/thread.c
index a1d927c7b..fd488d4da 100644
--- a/src/pkg/runtime/linux/thread.c
+++ b/src/pkg/runtime/linux/thread.c
@@ -92,7 +92,7 @@ futexwakeup(uint32 *addr)
//
// A reminder: compare-and-swap cas(addr, old, new) does
// if(*addr == old) { *addr = new; return 1; }
-// else return 0;
+// else return 0;
// but atomically.
static void
diff --git a/src/pkg/runtime/mkasmh.sh b/src/pkg/runtime/mkasmh.sh
index 30df16665..291ee042d 100755
--- a/src/pkg/runtime/mkasmh.sh
+++ b/src/pkg/runtime/mkasmh.sh
@@ -13,11 +13,17 @@ EOF
case "$GOARCH" in
386)
- # The offsets 0 and 4 are known to nacl/thread.c:/^newosproc too.
+ # The offsets 0 and 4 are also known to:
+ # nacl/thread.c:/^newosproc
+ # ../../cmd/8l/pass.c:/D_GS
+ # ../../libcgo/linux_386.c:/^start
+ # ../../libcgo/darwin_386.c:/^start
echo '#define g 0(GS)'
echo '#define m 4(GS)'
;;
amd64)
+ # These registers are also known to:
+ # ../../libcgo/linux_amd64.c:/^start
echo '#define g R15'
echo '#define m R14'
;;
diff --git a/src/pkg/runtime/proc.c b/src/pkg/runtime/proc.c
index 590c277dd..e3c7beccd 100644
--- a/src/pkg/runtime/proc.c
+++ b/src/pkg/runtime/proc.c
@@ -389,7 +389,20 @@ mstart(void)
scheduler();
}
-// Kick of new ms as needed (up to mcpumax).
+// When running with cgo, we call libcgo_thread_start
+// to start threads for us so that we can play nicely with
+// foreign code.
+void (*libcgo_thread_start)(void*);
+
+typedef struct CgoThreadStart CgoThreadStart;
+struct CgoThreadStart
+{
+ M *m;
+ G *g;
+ void (*fn)(void);
+};
+
+// Kick off new ms as needed (up to mcpumax).
// There are already `other' other cpus that will
// start looking for goroutines shortly.
// Sched is locked.
@@ -405,7 +418,21 @@ matchmg(void)
m = malloc(sizeof(M));
m->g0 = malg(8192);
m->id = sched.mcount++;
- newosproc(m, m->g0, m->g0->stackbase, mstart);
+
+ if(libcgo_thread_start != nil) {
+ CgoThreadStart ts;
+ // pthread_create will make us a stack,
+ // so free the one malg made.
+ stackfree(m->g0->stack0);
+ m->g0->stack0 = nil;
+ m->g0->stackguard = nil;
+ m->g0->stackbase = nil;
+ ts.m = m;
+ ts.g = m->g0;
+ ts.fn = mstart;
+ runcgo(libcgo_thread_start, &ts);
+ } else
+ newosproc(m, m->g0, m->g0->stackbase, mstart);
}
mnextg(m, g);
}
@@ -419,6 +446,17 @@ scheduler(void)
lock(&sched);
if(gosave(&m->sched) != 0){
+ gp = m->curg;
+ if(gp->status == Gcgocall){
+ // Runtime call into external code (FFI).
+ // When running with FFI, the scheduler stack is a
+ // native pthread stack, so it suffices to switch to the
+ // scheduler stack and make the call.
+ runcgo(gp->cgofn, gp->cgoarg);
+ gp->status = Grunning;
+ gogo(&gp->sched, 1);
+ }
+
// Jumped here via gosave/gogo, so didn't
// execute lock(&sched) above.
lock(&sched);
@@ -426,8 +464,7 @@ scheduler(void)
if(sched.predawn)
throw("init sleeping");
- // Just finished running m->curg.
- gp = m->curg;
+ // Just finished running gp.
gp->m = nil;
sched.mcpu--;
diff --git a/src/pkg/runtime/runtime.h b/src/pkg/runtime/runtime.h
index 34bc26252..d3027b9ce 100644
--- a/src/pkg/runtime/runtime.h
+++ b/src/pkg/runtime/runtime.h
@@ -94,6 +94,7 @@ enum
Gwaiting,
Gmoribund,
Gdead,
+ Gcgocall,
};
enum
{
@@ -156,11 +157,11 @@ struct Gobuf
};
struct G
{
- byte* stackguard; // cannot move - also known to linker, libmach
- byte* stackbase; // cannot move - also known to libmach
+ byte* stackguard; // cannot move - also known to linker, libmach, libcgo
+ byte* stackbase; // cannot move - also known to libmach, libcgo
Defer* defer;
Gobuf sched; // cannot move - also known to libmach
- byte* stack0; // first stack segment
+ byte* stack0;
byte* entry; // initial function
G* alllink; // on allg
void* param; // passed parameter on wakeup
@@ -171,6 +172,8 @@ struct G
bool readyonstop;
M* m; // for debuggers, but offset not hard-coded
M* lockedm;
+ void (*cgofn)(void*); // for cgo/ffi
+ void *cgoarg;
};
struct Mem
{
@@ -375,6 +378,7 @@ void exit(int32);
void breakpoint(void);
void gosched(void);
void goexit(void);
+void runcgo(void (*fn)(void*), void*);
#pragma varargck argpos printf 1