From 60f783d92bc07fa7ca78e8efccbbc841d9f9cbcb Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 7 Mar 2013 09:19:02 -0500 Subject: [PATCH] cmd/ld: host linking support for linux/amd64 Still to do: non-linux and non-amd64. It may work on other ELF-based amd64 systems too, but untested. "go test -ldflags -hostobj $GOROOT/misc/cgo/test" passes. Much may yet change, but this seems a reasonable checkpoint. R=iant CC=golang-dev https://golang.org/cl/7369057 --- src/cmd/5l/asm.c | 18 +- src/cmd/5l/l.h | 2 +- src/cmd/5l/obj.c | 3 + src/cmd/6l/asm.c | 26 +-- src/cmd/6l/l.h | 2 +- src/cmd/6l/obj.c | 6 + src/cmd/8l/asm.c | 20 +-- src/cmd/8l/l.h | 2 +- src/cmd/8l/obj.c | 4 + src/cmd/ld/data.c | 14 +- src/cmd/ld/dwarf.c | 8 +- src/cmd/ld/go.c | 39 ++-- src/cmd/ld/ldelf.c | 2 +- src/cmd/ld/ldmacho.c | 2 +- src/cmd/ld/lib.c | 303 ++++++++++++++++++++++++++++---- src/cmd/ld/lib.h | 22 ++- src/cmd/ld/macho.c | 15 +- src/cmd/ld/pe.c | 4 +- src/cmd/ld/symtab.c | 12 +- src/pkg/runtime/cgo/callbacks.c | 6 +- src/pkg/runtime/cgocall.c | 6 + 21 files changed, 393 insertions(+), 123 deletions(-) diff --git a/src/cmd/5l/asm.c b/src/cmd/5l/asm.c index 04f2a9c6cb0..097b74e31bb 100644 --- a/src/cmd/5l/asm.c +++ b/src/cmd/5l/asm.c @@ -125,7 +125,7 @@ adddynrel(Sym *s, Reloc *r) // Handle relocations found in ELF object files. case 256 + R_ARM_PLT32: r->type = D_CALL; - if(targ->dynimpname != nil && !targ->dynexport) { + if(targ->dynimpname != nil && !(targ->cgoexport & CgoExportDynamic)) { addpltsym(targ); r->sym = lookup(".plt", 0); r->add = braddoff(r->add, targ->plt / 4); @@ -138,7 +138,7 @@ adddynrel(Sym *s, Reloc *r) return; case 256 + R_ARM_GOT32: // R_ARM_GOT_BREL - if(targ->dynimpname == nil || targ->dynexport) { + if(targ->dynimpname == nil || (targ->cgoexport & CgoExportDynamic)) { addgotsyminternal(targ); } else { addgotsym(targ); @@ -149,7 +149,7 @@ adddynrel(Sym *s, Reloc *r) return; case 256 + R_ARM_GOT_PREL: // GOT(S) + A - P - if(targ->dynimpname == nil || targ->dynexport) { + if(targ->dynimpname == nil || (targ->cgoexport & CgoExportDynamic)) { addgotsyminternal(targ); } else { addgotsym(targ); @@ -171,7 +171,7 @@ adddynrel(Sym *s, Reloc *r) case 256 + R_ARM_CALL: r->type = D_CALL; - if(targ->dynimpname != nil && !targ->dynexport) { + if(targ->dynimpname != nil && !(targ->cgoexport & CgoExportDynamic)) { addpltsym(targ); r->sym = lookup(".plt", 0); r->add = braddoff(r->add, targ->plt / 4); @@ -184,7 +184,7 @@ adddynrel(Sym *s, Reloc *r) return; case 256 + R_ARM_ABS32: - if(targ->dynimpname != nil && !targ->dynexport) + if(targ->dynimpname != nil && !(targ->cgoexport & CgoExportDynamic)) diag("unexpected R_ARM_ABS32 relocation for dynamic symbol %s", targ->name); r->type = D_ADDR; return; @@ -201,7 +201,7 @@ adddynrel(Sym *s, Reloc *r) case 256 + R_ARM_PC24: case 256 + R_ARM_JUMP24: r->type = D_CALL; - if(targ->dynimpname != nil && !targ->dynexport) { + if(targ->dynimpname != nil && !(targ->cgoexport & CgoExportDynamic)) { addpltsym(targ); r->sym = lookup(".plt", 0); r->add = braddoff(r->add, targ->plt / 4); @@ -210,7 +210,7 @@ adddynrel(Sym *s, Reloc *r) } // Handle references to ELF symbols from our own object files. - if(targ->dynimpname == nil || targ->dynexport) + if(targ->dynimpname == nil || (targ->cgoexport & CgoExportDynamic)) return; switch(r->type) { @@ -464,7 +464,7 @@ adddynsym(Sym *s) /* type */ t = STB_GLOBAL << 4; - if(s->dynexport && (s->type&SMASK) == STEXT) + if((s->cgoexport & CgoExportDynamic) && (s->type&SMASK) == STEXT) t |= STT_FUNC; else t |= STT_OBJECT; @@ -472,7 +472,7 @@ adddynsym(Sym *s) adduint8(d, 0); /* shndx */ - if(!s->dynexport && s->dynimpname != nil) + if(!(s->cgoexport & CgoExportDynamic) && s->dynimpname != nil) adduint16(d, SHN_UNDEF); else { switch(s->type) { diff --git a/src/cmd/5l/l.h b/src/cmd/5l/l.h index 62dd8947f06..059e269a91d 100644 --- a/src/cmd/5l/l.h +++ b/src/cmd/5l/l.h @@ -137,7 +137,7 @@ struct Sym short version; uchar dupok; uchar reachable; - uchar dynexport; + uchar cgoexport; uchar leaf; int32 dynid; int32 plt; diff --git a/src/cmd/5l/obj.c b/src/cmd/5l/obj.c index 6aa7fdd69bf..58ac77052d9 100644 --- a/src/cmd/5l/obj.c +++ b/src/cmd/5l/obj.c @@ -82,6 +82,7 @@ main(int argc, char *argv[]) INITRND = -1; INITENTRY = 0; LIBINITENTRY = 0; + linkmode = LinkInternal; // TODO: LinkAuto once everything works. nuxiinit(); p = getgoarm(); @@ -126,6 +127,7 @@ main(int argc, char *argv[]) flagcount("v", "print link trace", &debug['v']); flagcount("w", "disable DWARF generation", &debug['w']); flagcount("shared", "generate shared object", &flag_shared); + // TODO: link mode flag flagparse(&argc, &argv, usage); @@ -268,6 +270,7 @@ main(int argc, char *argv[]) reloc(); asmb(); undef(); + hostlink(); if(debug['c']) print("ARM size = %d\n", armsize); diff --git a/src/cmd/6l/asm.c b/src/cmd/6l/asm.c index 5fb75ba4d00..627e10af0a2 100644 --- a/src/cmd/6l/asm.c +++ b/src/cmd/6l/asm.c @@ -131,7 +131,7 @@ adddynrel(Sym *s, Reloc *r) // Handle relocations found in ELF object files. case 256 + R_X86_64_PC32: - if(targ->dynimpname != nil && !targ->dynexport) + if(targ->dynimpname != nil && !(targ->cgoexport & CgoExportDynamic)) diag("unexpected R_X86_64_PC32 relocation for dynamic symbol %s", targ->name); if(targ->type == 0 || targ->type == SXREF) diag("unknown symbol %s in pcrel", targ->name); @@ -142,7 +142,7 @@ adddynrel(Sym *s, Reloc *r) case 256 + R_X86_64_PLT32: r->type = D_PCREL; r->add += 4; - if(targ->dynimpname != nil && !targ->dynexport) { + if(targ->dynimpname != nil && !(targ->cgoexport & CgoExportDynamic)) { addpltsym(targ); r->sym = lookup(".plt", 0); r->add += targ->plt; @@ -150,7 +150,7 @@ adddynrel(Sym *s, Reloc *r) return; case 256 + R_X86_64_GOTPCREL: - if(targ->dynimpname == nil || targ->dynexport) { + if(targ->dynimpname == nil || (targ->cgoexport & CgoExportDynamic)) { // have symbol if(r->off >= 2 && s->p[r->off-2] == 0x8b) { // turn MOVQ of GOT entry into LEAQ of symbol itself @@ -171,7 +171,7 @@ adddynrel(Sym *s, Reloc *r) return; case 256 + R_X86_64_64: - if(targ->dynimpname != nil && !targ->dynexport) + if(targ->dynimpname != nil && !(targ->cgoexport & CgoExportDynamic)) diag("unexpected R_X86_64_64 relocation for dynamic symbol %s", targ->name); r->type = D_ADDR; return; @@ -182,12 +182,12 @@ adddynrel(Sym *s, Reloc *r) case 512 + MACHO_X86_64_RELOC_BRANCH*2 + 0: // TODO: What is the difference between all these? r->type = D_ADDR; - if(targ->dynimpname != nil && !targ->dynexport) + if(targ->dynimpname != nil && !(targ->cgoexport & CgoExportDynamic)) diag("unexpected reloc for dynamic symbol %s", targ->name); return; case 512 + MACHO_X86_64_RELOC_BRANCH*2 + 1: - if(targ->dynimpname != nil && !targ->dynexport) { + if(targ->dynimpname != nil && !(targ->cgoexport & CgoExportDynamic)) { addpltsym(targ); r->sym = lookup(".plt", 0); r->add = targ->plt; @@ -201,12 +201,12 @@ adddynrel(Sym *s, Reloc *r) case 512 + MACHO_X86_64_RELOC_SIGNED_2*2 + 1: case 512 + MACHO_X86_64_RELOC_SIGNED_4*2 + 1: r->type = D_PCREL; - if(targ->dynimpname != nil && !targ->dynexport) + if(targ->dynimpname != nil && !(targ->cgoexport & CgoExportDynamic)) diag("unexpected pc-relative reloc for dynamic symbol %s", targ->name); return; case 512 + MACHO_X86_64_RELOC_GOT_LOAD*2 + 1: - if(targ->dynimpname == nil || targ->dynexport) { + if(targ->dynimpname == nil || (targ->cgoexport & CgoExportDynamic)) { // have symbol // turn MOVQ of GOT entry into LEAQ of symbol itself if(r->off < 2 || s->p[r->off-2] != 0x8b) { @@ -219,7 +219,7 @@ adddynrel(Sym *s, Reloc *r) } // fall through case 512 + MACHO_X86_64_RELOC_GOT*2 + 1: - if(targ->dynimpname == nil || targ->dynexport) + if(targ->dynimpname == nil || (targ->cgoexport & CgoExportDynamic)) diag("unexpected GOT reloc for non-dynamic symbol %s", targ->name); addgotsym(targ); r->type = D_PCREL; @@ -229,7 +229,7 @@ adddynrel(Sym *s, Reloc *r) } // Handle references to ELF symbols from our own object files. - if(targ->dynimpname == nil || targ->dynexport) + if(targ->dynimpname == nil || (targ->cgoexport & CgoExportDynamic)) return; switch(r->type) { @@ -470,7 +470,7 @@ adddynsym(Sym *s) adduint32(d, addstring(lookup(".dynstr", 0), name)); /* type */ t = STB_GLOBAL << 4; - if(s->dynexport && (s->type&SMASK) == STEXT) + if((s->cgoexport & CgoExportDynamic) && (s->type&SMASK) == STEXT) t |= STT_FUNC; else t |= STT_OBJECT; @@ -480,7 +480,7 @@ adddynsym(Sym *s) adduint8(d, 0); /* section where symbol is defined */ - if(!s->dynexport && s->dynimpname != nil) + if(!(s->cgoexport & CgoExportDynamic) && s->dynimpname != nil) adduint16(d, SHN_UNDEF); else { switch(s->type) { @@ -510,7 +510,7 @@ adddynsym(Sym *s) /* size of object */ adduint64(d, s->size); - if(!s->dynexport && s->dynimplib && needlib(s->dynimplib)) { + if(!(s->cgoexport & CgoExportDynamic) && s->dynimplib && needlib(s->dynimplib)) { elfwritedynent(lookup(".dynamic", 0), DT_NEEDED, addstring(lookup(".dynstr", 0), s->dynimplib)); } diff --git a/src/cmd/6l/l.h b/src/cmd/6l/l.h index ffb8a45522e..22fb23b1b70 100644 --- a/src/cmd/6l/l.h +++ b/src/cmd/6l/l.h @@ -144,7 +144,7 @@ struct Sym short version; uchar dupok; uchar reachable; - uchar dynexport; + uchar cgoexport; uchar special; uchar stkcheck; uchar hide; diff --git a/src/cmd/6l/obj.c b/src/cmd/6l/obj.c index 10e4a986019..6ced8be7fd6 100644 --- a/src/cmd/6l/obj.c +++ b/src/cmd/6l/obj.c @@ -83,6 +83,7 @@ main(int argc, char *argv[]) INITRND = -1; INITENTRY = 0; LIBINITENTRY = 0; + linkmode = LinkInternal; // TODO: LinkAuto once everything works. nuxiinit(); flagcount("1", "use alternate profiling code", &debug['1']); @@ -122,6 +123,10 @@ main(int argc, char *argv[]) flagcount("shared", "generate shared object", &flag_shared); flagparse(&argc, &argv, usage); + + // TODO: link mode flag instead of isobj + if(isobj) + linkmode = LinkExternal; if(argc != 1) usage(); @@ -282,6 +287,7 @@ main(int argc, char *argv[]) reloc(); asmb(); undef(); + hostlink(); if(debug['v']) { Bprint(&bso, "%5.2f cpu time\n", cputime()); Bprint(&bso, "%d symbols\n", nsymbol); diff --git a/src/cmd/8l/asm.c b/src/cmd/8l/asm.c index a00174c3681..3563c0849f3 100644 --- a/src/cmd/8l/asm.c +++ b/src/cmd/8l/asm.c @@ -128,7 +128,7 @@ adddynrel(Sym *s, Reloc *r) // Handle relocations found in ELF object files. case 256 + R_386_PC32: - if(targ->dynimpname != nil && !targ->dynexport) + if(targ->dynimpname != nil && !(targ->cgoexport & CgoExportDynamic)) diag("unexpected R_386_PC32 relocation for dynamic symbol %s", targ->name); if(targ->type == 0 || targ->type == SXREF) diag("unknown symbol %s in pcrel", targ->name); @@ -139,7 +139,7 @@ adddynrel(Sym *s, Reloc *r) case 256 + R_386_PLT32: r->type = D_PCREL; r->add += 4; - if(targ->dynimpname != nil && !targ->dynexport) { + if(targ->dynimpname != nil && !(targ->cgoexport & CgoExportDynamic)) { addpltsym(targ); r->sym = lookup(".plt", 0); r->add += targ->plt; @@ -147,7 +147,7 @@ adddynrel(Sym *s, Reloc *r) return; case 256 + R_386_GOT32: - if(targ->dynimpname == nil || targ->dynexport) { + if(targ->dynimpname == nil || (targ->cgoexport & CgoExportDynamic)) { // have symbol // turn MOVL of GOT entry into LEAL of symbol itself if(r->off < 2 || s->p[r->off-2] != 0x8b) { @@ -175,19 +175,19 @@ adddynrel(Sym *s, Reloc *r) return; case 256 + R_386_32: - if(targ->dynimpname != nil && !targ->dynexport) + if(targ->dynimpname != nil && !(targ->cgoexport & CgoExportDynamic)) diag("unexpected R_386_32 relocation for dynamic symbol %s", targ->name); r->type = D_ADDR; return; case 512 + MACHO_GENERIC_RELOC_VANILLA*2 + 0: r->type = D_ADDR; - if(targ->dynimpname != nil && !targ->dynexport) + if(targ->dynimpname != nil && !(targ->cgoexport & CgoExportDynamic)) diag("unexpected reloc for dynamic symbol %s", targ->name); return; case 512 + MACHO_GENERIC_RELOC_VANILLA*2 + 1: - if(targ->dynimpname != nil && !targ->dynexport) { + if(targ->dynimpname != nil && !(targ->cgoexport & CgoExportDynamic)) { addpltsym(targ); r->sym = lookup(".plt", 0); r->add = targ->plt; @@ -198,7 +198,7 @@ adddynrel(Sym *s, Reloc *r) return; case 512 + MACHO_FAKE_GOTPCREL: - if(targ->dynimpname == nil || targ->dynexport) { + if(targ->dynimpname == nil || (targ->cgoexport & CgoExportDynamic)) { // have symbol // turn MOVL of GOT entry into LEAL of symbol itself if(r->off < 2 || s->p[r->off-2] != 0x8b) { @@ -217,7 +217,7 @@ adddynrel(Sym *s, Reloc *r) } // Handle references to ELF symbols from our own object files. - if(targ->dynimpname == nil || targ->dynexport) + if(targ->dynimpname == nil || (targ->cgoexport & CgoExportDynamic)) return; switch(r->type) { @@ -461,7 +461,7 @@ adddynsym(Sym *s) /* type */ t = STB_GLOBAL << 4; - if(s->dynexport && (s->type&SMASK) == STEXT) + if((s->cgoexport & CgoExportDynamic) && (s->type&SMASK) == STEXT) t |= STT_FUNC; else t |= STT_OBJECT; @@ -469,7 +469,7 @@ adddynsym(Sym *s) adduint8(d, 0); /* shndx */ - if(!s->dynexport && s->dynimpname != nil) + if(!(s->cgoexport & CgoExportDynamic) && s->dynimpname != nil) adduint16(d, SHN_UNDEF); else { switch(s->type) { diff --git a/src/cmd/8l/l.h b/src/cmd/8l/l.h index f88f058e35f..f6b164d9e40 100644 --- a/src/cmd/8l/l.h +++ b/src/cmd/8l/l.h @@ -126,7 +126,7 @@ struct Sym short version; uchar dupok; uchar reachable; - uchar dynexport; + uchar cgoexport; uchar special; uchar stkcheck; uchar hide; diff --git a/src/cmd/8l/obj.c b/src/cmd/8l/obj.c index dcb8390b979..f06bc5d9dc9 100644 --- a/src/cmd/8l/obj.c +++ b/src/cmd/8l/obj.c @@ -90,6 +90,7 @@ main(int argc, char *argv[]) INITRND = -1; INITENTRY = 0; LIBINITENTRY = 0; + linkmode = LinkInternal; // TODO: LinkAuto once everything works. nuxiinit(); flagcount("1", "use alternate profiling code", &debug['1']); @@ -125,6 +126,7 @@ main(int argc, char *argv[]) flagcount("u", "reject unsafe packages", &debug['u']); flagcount("v", "print link trace", &debug['v']); flagcount("w", "disable DWARF generation", &debug['w']); + // TODO: link mode flag flagparse(&argc, &argv, usage); @@ -308,6 +310,8 @@ main(int argc, char *argv[]) reloc(); asmb(); undef(); + hostlink(); + if(debug['v']) { Bprint(&bso, "%5.2f cpu time\n", cputime()); Bprint(&bso, "%d symbols\n", nsymbol); diff --git a/src/cmd/ld/data.c b/src/cmd/ld/data.c index 6c6b1be4335..b344abd9b07 100644 --- a/src/cmd/ld/data.c +++ b/src/cmd/ld/data.c @@ -135,11 +135,7 @@ addrel(Sym *s) s->maxr = 4; else s->maxr <<= 1; - s->r = realloc(s->r, s->maxr*sizeof s->r[0]); - if(s->r == 0) { - diag("out of memory"); - errorexit(); - } + s->r = erealloc(s->r, s->maxr*sizeof s->r[0]); memset(s->r+s->nr, 0, (s->maxr-s->nr)*sizeof s->r[0]); } return &s->r[s->nr++]; @@ -300,7 +296,7 @@ dynrelocsym(Sym *s) for(r=s->r; rr+s->nr; r++) { if(r->sym != S && r->sym->type == SDYNIMPORT || r->type >= 256) adddynrel(s, r); - if(flag_shared && r->sym != S && (r->sym->dynimpname == nil || r->sym->dynexport) && r->type == D_ADDR + if(flag_shared && r->sym != S && (r->sym->dynimpname == nil || (r->sym->cgoexport & CgoExportDynamic)) && r->type == D_ADDR && (s == got || s->type == SDATA || s->type == SGOSTRING || s->type == STYPE || s->type == SRODATA)) { // Create address based RELATIVE relocation adddynrela(rel, s, r); @@ -342,11 +338,7 @@ symgrow(Sym *s, int32 siz) s->maxp = 8; while(s->maxp < siz) s->maxp <<= 1; - s->p = realloc(s->p, s->maxp); - if(s->p == nil) { - diag("out of memory"); - errorexit(); - } + s->p = erealloc(s->p, s->maxp); memset(s->p+s->np, 0, s->maxp-s->np); } s->np = siz; diff --git a/src/cmd/ld/dwarf.c b/src/cmd/ld/dwarf.c index d6a357e4982..949752994b1 100644 --- a/src/cmd/ld/dwarf.c +++ b/src/cmd/ld/dwarf.c @@ -1260,7 +1260,7 @@ dwarfaddfrag(int n, char *frag) if (n >= ftabsize) { s = ftabsize; ftabsize = 1 + n + (n >> 2); - ftab = realloc(ftab, ftabsize * sizeof(ftab[0])); + ftab = erealloc(ftab, ftabsize * sizeof(ftab[0])); memset(ftab + s, 0, (ftabsize - s) * sizeof(ftab[0])); } @@ -1342,7 +1342,7 @@ addhistfile(char *zentry) if (histfilesize == histfilecap) { histfilecap = 2 * histfilecap + 2; - histfile = realloc(histfile, histfilecap * sizeof(char*)); + histfile = erealloc(histfile, histfilecap * sizeof(char*)); } if (histfilesize == 0) histfile[histfilesize++] = ""; @@ -1412,7 +1412,7 @@ checknesting(void) includestacksize += 1; includestacksize <<= 2; // print("checknesting: growing to %d\n", includestacksize); - includestack = realloc(includestack, includestacksize * sizeof *includestack); + includestack = erealloc(includestack, includestacksize * sizeof *includestack); } } @@ -1651,7 +1651,7 @@ writelines(void) lang = guesslang(histfile[1]); finddebugruntimepath(); - dwinfo = newdie(&dwroot, DW_ABRV_COMPUNIT, strdup(histfile[1])); + dwinfo = newdie(&dwroot, DW_ABRV_COMPUNIT, estrdup(histfile[1])); newattr(dwinfo, DW_AT_language, DW_CLS_CONSTANT,lang, 0); newattr(dwinfo, DW_AT_stmt_list, DW_CLS_PTR, unitstart - lineo, 0); newattr(dwinfo, DW_AT_low_pc, DW_CLS_ADDRESS, s->text->pc, 0); diff --git a/src/cmd/ld/go.c b/src/cmd/ld/go.c index 2b6fdd6b5eb..403b800b9bc 100644 --- a/src/cmd/ld/go.c +++ b/src/cmd/ld/go.c @@ -59,7 +59,7 @@ ilookup(char *name) if(x->name[0] == name[0] && strcmp(x->name, name) == 0) return x; x = mal(sizeof *x); - x->name = strdup(name); + x->name = estrdup(name); x->hash = ihash[h]; ihash[h] = x; nimport++; @@ -71,8 +71,6 @@ static void loadcgo(char*, char*, char*, int); static int parsemethod(char**, char*, char**); static int parsepkgdata(char*, char*, char**, char*, char**, char**, char**); -static Sym **dynexp; - void ldpkg(Biobuf *f, char *pkg, int64 len, char *filename, int whence) { @@ -205,14 +203,14 @@ loadpkgdata(char *file, char *pkg, char *data, int len) char *p, *ep, *prefix, *name, *def; Import *x; - file = strdup(file); + file = estrdup(file); p = data; ep = data + len; while(parsepkgdata(file, pkg, &p, ep, &prefix, &name, &def) > 0) { x = ilookup(name); if(x->prefix == nil) { x->prefix = prefix; - x->def = strdup(def); + x->def = estrdup(def); x->file = file; } else if(strcmp(x->prefix, prefix) != 0) { fprint(2, "%s: conflicting definitions for %s\n", argv0, name); @@ -244,7 +242,7 @@ expandpkg(char *t0, char *pkg) n++; if(n == 0) - return strdup(t0); + return estrdup(t0); // use malloc, not mal, so that caller can free w0 = malloc(strlen(t0) + strlen(pkg)*n); @@ -429,7 +427,7 @@ loadcgo(char *file, char *pkg, char *p, int n) *next++ = '\0'; free(p0); - p0 = strdup(p); // save for error message + p0 = estrdup(p); // save for error message nf = tokenize(p, f, nelem(f)); if(strcmp(f[0], "cgo_import_dynamic") == 0) { @@ -487,9 +485,14 @@ loadcgo(char *file, char *pkg, char *p, int n) continue; } - // TODO: cgo_export_static + if(strcmp(f[0], "cgo_export_static") == 0 || strcmp(f[0], "cgo_export_dynamic") == 0) { + // TODO: Make Mach-O code happier. Right now it sees the dynimpname and + // includes CgoExportStatic symbols in the dynamic table, and then dyld + // cannot find them when we run the binary. Disabling Windows too + // because it probably has the same issue. + if(strcmp(f[0], "cgo_export_static") == 0 && (HEADTYPE == Hdarwin || HEADTYPE == Hwindows)) + continue; - if(strcmp(f[0], "cgo_export_dynamic") == 0) { if(nf < 2 || nf > 3) goto err; local = f[1]; @@ -503,11 +506,15 @@ loadcgo(char *file, char *pkg, char *p, int n) fprint(2, "%s: symbol is both imported and exported: %s\n", argv0, local); nerrors++; } - s->dynexport = 1; + + if(strcmp(f[0], "cgo_export_static") == 0) + s->cgoexport |= CgoExportStatic; + else + s->cgoexport |= CgoExportDynamic; if(s->dynimpname == nil) { s->dynimpname = remote; if(ndynexp%32 == 0) - dynexp = realloc(dynexp, (ndynexp+32)*sizeof dynexp[0]); + dynexp = erealloc(dynexp, (ndynexp+32)*sizeof dynexp[0]); dynexp[ndynexp++] = s; } else if(strcmp(s->dynimpname, remote) != 0) { fprint(2, "%s: conflicting cgo_export directives: %s as %s and %s\n", argv0, s->name, s->dynimpname, remote); @@ -530,7 +537,7 @@ loadcgo(char *file, char *pkg, char *p, int n) return; } free(interpreter); - interpreter = strdup(f[1]); + interpreter = estrdup(f[1]); } continue; } @@ -539,8 +546,8 @@ loadcgo(char *file, char *pkg, char *p, int n) if(nf != 2) goto err; if(nldflag%32 == 0) - ldflag = realloc(ldflag, (nldflag+32)*sizeof ldflag[0]); - ldflag[nldflag++] = strdup(f[1]); + ldflag = erealloc(ldflag, (nldflag+32)*sizeof ldflag[0]); + ldflag[nldflag++] = estrdup(f[1]); continue; } } @@ -844,7 +851,7 @@ getpkg(char *path) if(strcmp(p->path, path) == 0) return p; p = mal(sizeof *p); - p->path = strdup(path); + p->path = estrdup(path); p->next = phash[h]; phash[h] = p; p->all = pkgall; @@ -868,7 +875,7 @@ imported(char *pkg, char *import) i->mimpby *= 2; if(i->mimpby == 0) i->mimpby = 16; - i->impby = realloc(i->impby, i->mimpby*sizeof i->impby[0]); + i->impby = erealloc(i->impby, i->mimpby*sizeof i->impby[0]); } i->impby[i->nimpby++] = p; free(pkg); diff --git a/src/cmd/ld/ldelf.c b/src/cmd/ld/ldelf.c index 2bbf4f83e35..750dff4e601 100644 --- a/src/cmd/ld/ldelf.c +++ b/src/cmd/ld/ldelf.c @@ -595,7 +595,7 @@ ldelf(Biobuf *f, char *pkg, int64 len, char *pn) s->sub = sect->sym->sub; sect->sym->sub = s; s->type = sect->sym->type | (s->type&~SMASK) | SSUB; - if(!s->dynexport) { + if(!(s->cgoexport & CgoExportDynamic)) { s->dynimplib = nil; // satisfy dynimport s->dynimpname = nil; // satisfy dynimport } diff --git a/src/cmd/ld/ldmacho.c b/src/cmd/ld/ldmacho.c index 41852f17c67..2509be471c3 100644 --- a/src/cmd/ld/ldmacho.c +++ b/src/cmd/ld/ldmacho.c @@ -639,7 +639,7 @@ ldmacho(Biobuf *f, char *pkg, int64 len, char *pn) s->size = (sym+1)->value - sym->value; else s->size = sect->addr + sect->size - sym->value; - if(!s->dynexport) { + if(!(s->cgoexport & CgoExportDynamic)) { s->dynimplib = nil; // satisfy dynimport s->dynimpname = nil; // satisfy dynimport } diff --git a/src/cmd/ld/lib.c b/src/cmd/ld/lib.c index 26fa4f2ac7a..db6b7761b7b 100644 --- a/src/cmd/ld/lib.c +++ b/src/cmd/ld/lib.c @@ -44,6 +44,8 @@ int nlibdir = 0; static int maxlibdir = 0; static int cout = -1; +static void hostlinksetup(void); + char* goroot; char* goarch; char* goos; @@ -59,11 +61,7 @@ Lflag(char *arg) maxlibdir = 8; else maxlibdir *= 2; - p = realloc(libdir, maxlibdir * sizeof(*p)); - if (p == nil) { - print("too many -L's: %d\n", nlibdir); - usage(); - } + p = erealloc(libdir, maxlibdir * sizeof(*p)); libdir = p; } libdir[nlibdir++] = arg; @@ -95,7 +93,7 @@ libinit(void) #endif cout = create(outfile, 1, 0775); if(cout < 0) { - diag("cannot create %s", outfile); + diag("cannot create %s: %r", outfile); errorexit(); } @@ -242,7 +240,7 @@ addlibpath(char *srcref, char *objref, char *file, char *pkg) if(libraryp == nlibrary){ nlibrary = 50 + 2*libraryp; - library = realloc(library, sizeof library[0] * nlibrary); + library = erealloc(library, sizeof library[0] * nlibrary); } l = &library[libraryp++]; @@ -288,7 +286,7 @@ loadinternal(char *name) void loadlib(void) { - int i; + int i, w, x; loadinternal("runtime"); if(thechar == '5') @@ -303,6 +301,28 @@ loadlib(void) objfile(library[i].file, library[i].pkg); } + // If we got this far in automatic mode, there were no + // cgo uses that suggest we need external mode. + // Switch to internal. + if(linkmode == LinkAuto) + linkmode = LinkInternal; + + // Now that we know the link mode, trim the dynexp list. + x = CgoExportDynamic; + if(linkmode == LinkExternal) + x = CgoExportStatic; + w = 0; + for(i=0; icgoexport & x) + dynexp[w++] = dynexp[i]; + ndynexp = w; + + // In internal link mode, read the host object files. + if(linkmode == LinkInternal) + hostobjs(); + else + hostlinksetup(); + // We've loaded all the code now. // If there are no dynamic libraries needed, gcc disables dynamic linking. // Because of this, glibc's dynamic ELF loader occasionally (like in version 2.13) @@ -375,7 +395,7 @@ objfile(char *file, char *pkg) /* load it as a regular file */ l = Bseek(f, 0L, 2); Bseek(f, 0L, 0); - ldobj(f, pkg, l, file, FileObj); + ldobj(f, pkg, l, file, file, FileObj); Bterm(f); free(pkg); return; @@ -434,7 +454,7 @@ objfile(char *file, char *pkg) l--; snprint(pname, sizeof pname, "%s(%.*s)", file, utfnlen(arhdr.name, l), arhdr.name); l = atolwhex(arhdr.size); - ldobj(f, pkg, l, pname, ArchiveObj); + ldobj(f, pkg, l, pname, file, ArchiveObj); } out: @@ -442,8 +462,213 @@ out: free(pkg); } +static void +dowrite(int fd, char *p, int n) +{ + int m; + + while(n > 0) { + m = write(fd, p, n); + if(m <= 0) { + cursym = S; + diag("write error: %r"); + errorexit(); + } + n -= m; + p += m; + } +} + +typedef struct Hostobj Hostobj; + +struct Hostobj +{ + void (*ld)(Biobuf*, char*, int64, char*); + char *pkg; + char *pn; + char *file; + int64 off; + int64 len; +}; + +Hostobj *hostobj; +int nhostobj; +int mhostobj; + +// These packages can use internal linking mode. +// Others trigger external mode. +const char *internalpkg[] = { + "net", + "os/user", + "runtime/cgo" +}; + void -ldobj(Biobuf *f, char *pkg, int64 len, char *pn, int whence) +ldhostobj(void (*ld)(Biobuf*, char*, int64, char*), Biobuf *f, char *pkg, int64 len, char *pn, char *file) +{ + int i, isinternal; + Hostobj *h; + + isinternal = 0; + for(i=0; i= mhostobj) { + if(mhostobj == 0) + mhostobj = 16; + else + mhostobj *= 2; + hostobj = erealloc(hostobj, mhostobj*sizeof hostobj[0]); + } + h = &hostobj[nhostobj++]; + h->ld = ld; + h->pkg = pkg; + h->pn = estrdup(pn); + h->file = estrdup(file); + h->off = Boffset(f); + h->len = len; +} + +void +hostobjs(void) +{ + int i; + Biobuf *f; + Hostobj *h; + + for(i=0; ifile, OREAD); + if(f == nil) { + cursym = S; + diag("cannot reopen %s: %r", h->pn); + errorexit(); + } + Bseek(f, h->off, 0); + h->ld(f, h->pkg, h->len, h->pn); + Bterm(f); + } +} + +static char *tmpdir; + +static void +rmtemp(void) +{ + removeall(tmpdir); +} + +static void +hostlinksetup(void) +{ + char *p; + + if(linkmode != LinkExternal) + return; + + // create temporary directory and arrange cleanup + // TODO: Add flag to specify tempdir, which is then not cleaned up. + tmpdir = mktempdir(); + atexit(rmtemp); + + // change our output to temporary object file + close(cout); + p = smprint("%s/go.o", tmpdir); + cout = create(p, 1, 0775); + if(cout < 0) { + diag("cannot create %s: %r", p); + errorexit(); + } + free(p); +} + +void +hostlink(void) +{ + char *p, **argv; + int i, w, n, argc, len; + Hostobj *h; + Biobuf *f; + static char buf[64<<10]; + + if(linkmode != LinkExternal) + return; + + argv = malloc((10+nhostobj+nldflag)*sizeof argv[0]); + argc = 0; + // TODO: Add command-line flag to override gcc path and specify additional leading options. + // TODO: Add command-line flag to specify additional trailing options. + argv[argc++] = "gcc"; + if(!debug['s']) + argv[argc++] = "-ggdb"; + argv[argc++] = "-o"; + argv[argc++] = outfile; + + // Force global symbols to be exported for dlopen, etc. + // NOTE: May not work on OS X or Windows. We'll see. + argv[argc++] = "-rdynamic"; + + // already wrote main object file + // copy host objects to temporary directory + for(i=0; ifile, OREAD); + if(f == nil) { + cursym = S; + diag("cannot reopen %s: %r", h->pn); + errorexit(); + } + Bseek(f, h->off, 0); + p = smprint("%s/%06d.o", tmpdir, i); + argv[argc++] = p; + w = create(p, 1, 0775); + if(w < 0) { + diag("cannot create %s: %r", p); + errorexit(); + } + len = h->len; + while(len > 0 && (n = Bread(f, buf, sizeof buf)) > 0){ + if(n > len) + n = len; + dowrite(w, buf, n); + len -= n; + } + if(close(w) < 0) { + diag("cannot write %s: %r", p); + errorexit(); + } + Bterm(f); + } + + argv[argc++] = smprint("%s/go.o", tmpdir); + for(i=0; i strlen(t)) { if(theline == nil) - theline = strdup(line+10); + theline = estrdup(line+10); else if(strcmp(theline, line+10) != 0) { line[n] = '\0'; diag("%s: object is [%s] expected [%s]", pn, line+10, theline); @@ -1460,23 +1682,6 @@ Yconv(Fmt *fp) vlong coutpos; -static void -dowrite(int fd, char *p, int n) -{ - int m; - - while(n > 0) { - m = write(fd, p, n); - if(m <= 0) { - cursym = S; - diag("write error: %r"); - errorexit(); - } - n -= m; - p += m; - } -} - void cflush(void) { @@ -1576,7 +1781,7 @@ genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)) put(s, s->name, 'T', s->value, s->size, s->version, 0); for(s=allsym; s!=S; s=s->allsym) { - if(s->hide) + if(s->hide || (s->name[0] == '.' && s->version == 0)) continue; switch(s->type&SMASK) { case SCONST: @@ -1664,3 +1869,27 @@ genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)) Bprint(&bso, "symsize = %ud\n", symsize); Bflush(&bso); } + +char* +estrdup(char *p) +{ + p = strdup(p); + if(p == nil) { + cursym = S; + diag("out of memory"); + errorexit(); + } + return p; +} + +void* +erealloc(void *p, long n) +{ + p = realloc(p, n); + if(p == nil) { + cursym = S; + diag("out of memory"); + errorexit(); + } + return p; +} diff --git a/src/cmd/ld/lib.h b/src/cmd/ld/lib.h index 25fe45675e2..0b7e48c8a28 100644 --- a/src/cmd/ld/lib.h +++ b/src/cmd/ld/lib.h @@ -138,6 +138,7 @@ EXTERN char* outfile; EXTERN int32 nsymbol; EXTERN char* thestring; EXTERN int ndynexp; +EXTERN Sym** dynexp; EXTERN int nldflag; EXTERN char** ldflag; EXTERN int havedynamic; @@ -149,6 +150,21 @@ EXTERN int flag_shared; EXTERN char* tracksym; EXTERN char* interpreter; +enum +{ + LinkAuto = 0, + LinkInternal, + LinkExternal, +}; +EXTERN int linkmode; + +// for dynexport field of Sym +enum +{ + CgoExportDynamic = 1<<0, + CgoExportStatic = 1<<1, +}; + EXTERN Segment segtext; EXTERN Segment segdata; EXTERN Segment segsym; @@ -187,7 +203,7 @@ void adddynrel(Sym*, Reloc*); void adddynrela(Sym*, Sym*, Reloc*); Sym* lookuprel(void); void ldobj1(Biobuf *f, char*, int64 len, char *pn); -void ldobj(Biobuf*, char*, int64, char*, int); +void ldobj(Biobuf*, char*, int64, char*, char*, int); void ldelf(Biobuf*, char*, int64, char*); void ldmacho(Biobuf*, char*, int64, char*); void ldpe(Biobuf*, char*, int64, char*); @@ -242,6 +258,10 @@ void usage(void); void setinterp(char*); Sym* listsort(Sym*, int(*cmp)(Sym*, Sym*), int); int valuecmp(Sym*, Sym*); +void hostobjs(void); +void hostlink(void); +char* estrdup(char*); +void* erealloc(void*, long); int pathchar(void); void* mal(uint32); diff --git a/src/cmd/ld/macho.c b/src/cmd/ld/macho.c index 6781c25a43d..75be9d94023 100644 --- a/src/cmd/ld/macho.c +++ b/src/cmd/ld/macho.c @@ -56,11 +56,7 @@ newMachoLoad(uint32 type, uint32 ndata) mload = 1; else mload *= 2; - load = realloc(load, mload*sizeof load[0]); - if(load == nil) { - diag("out of memory"); - errorexit(); - } + load = erealloc(load, mload*sizeof load[0]); } if(macho64 && (ndata & 1)) @@ -286,13 +282,8 @@ machoadddynlib(char *lib) load_budget += 4096; } - if(ndylib%32 == 0) { - dylib = realloc(dylib, (ndylib+32)*sizeof dylib[0]); - if(dylib == nil) { - diag("out of memory"); - errorexit(); - } - } + if(ndylib%32 == 0) + dylib = erealloc(dylib, (ndylib+32)*sizeof dylib[0]); dylib[ndylib++] = lib; } diff --git a/src/cmd/ld/pe.c b/src/cmd/ld/pe.c index f2903ba0f92..85f622dbc01 100644 --- a/src/cmd/ld/pe.c +++ b/src/cmd/ld/pe.c @@ -195,7 +195,7 @@ initdynimport(void) dr = nil; m = nil; for(s = allsym; s != S; s = s->allsym) { - if(!s->reachable || !s->dynimpname || s->dynexport) + if(!s->reachable || !s->dynimpname || (s->cgoexport & CgoExportDynamic)) continue; for(d = dr; d != nil; d = d->next) { if(strcmp(d->name,s->dynimplib) == 0) { @@ -335,7 +335,7 @@ initdynexport(void) nexport = 0; for(s = allsym; s != S; s = s->allsym) { - if(!s->reachable || !s->dynimpname || !s->dynexport) + if(!s->reachable || !s->dynimpname || !(s->cgoexport & CgoExportDynamic)) continue; if(nexport+1 > sizeof(dexport)/sizeof(dexport[0])) { diag("pe dynexport table is full"); diff --git a/src/cmd/ld/symtab.c b/src/cmd/ld/symtab.c index 89a594872e8..d8b8b932893 100644 --- a/src/cmd/ld/symtab.c +++ b/src/cmd/ld/symtab.c @@ -121,7 +121,17 @@ putelfsym(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go) // One pass for each binding: STB_LOCAL, STB_GLOBAL, // maybe one day STB_WEAK. - bind = (ver || (x->type & SHIDDEN)) ? STB_LOCAL : STB_GLOBAL; + bind = STB_GLOBAL; + if(ver || (x->type & SHIDDEN)) + bind = STB_LOCAL; + + // In external linking mode, we have to invoke gcc with -rdynamic + // to get the exported symbols put into the dynamic symbol table. + // To avoid filling the dynamic table with lots of unnecessary symbols, + // mark all Go symbols local (not global) in the final executable. + if(linkmode == LinkExternal && !(x->cgoexport&CgoExportStatic)) + bind = STB_LOCAL; + if(bind != elfbind) return; diff --git a/src/pkg/runtime/cgo/callbacks.c b/src/pkg/runtime/cgo/callbacks.c index 51bd529ecb4..19f6115a665 100644 --- a/src/pkg/runtime/cgo/callbacks.c +++ b/src/pkg/runtime/cgo/callbacks.c @@ -12,8 +12,10 @@ // void crosscall2(void (*fn)(void *, int), void *, int); // // We need to export the symbol crosscall2 in order to support -// callbacks from shared libraries. -#pragma dynexport crosscall2 crosscall2 +// callbacks from shared libraries. This applies regardless of +// linking mode. +#pragma cgo_export_static crosscall2 +#pragma cgo_export_dynamic crosscall2 // Allocate memory. This allocates the requested number of bytes in // memory controlled by the Go runtime. The allocated memory will be diff --git a/src/pkg/runtime/cgocall.c b/src/pkg/runtime/cgocall.c index 590bf9b6724..3ed1243aacc 100644 --- a/src/pkg/runtime/cgocall.c +++ b/src/pkg/runtime/cgocall.c @@ -280,3 +280,9 @@ runtime·cgounimpl(void) // called from (incomplete) assembly { runtime·throw("runtime: cgo not implemented"); } + +// For cgo-using programs with external linking, +// export "main" (defined in assembly) so that libc can handle basic +// C runtime startup and call the Go program as if it were +// the C main function. +#pragma cgo_export_static main