diff options
Diffstat (limited to 'src')
45 files changed, 1156 insertions, 633 deletions
| 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 | 
