diff options
Diffstat (limited to 'src/cmd/ld/lib.c')
-rw-r--r-- | src/cmd/ld/lib.c | 398 |
1 files changed, 354 insertions, 44 deletions
diff --git a/src/cmd/ld/lib.c b/src/cmd/ld/lib.c index 26fa4f2ac..0a6bd3e8f 100644 --- a/src/cmd/ld/lib.c +++ b/src/cmd/ld/lib.c @@ -31,6 +31,7 @@ #include "l.h" #include "lib.h" +#include "../ld/elf.h" #include "../../pkg/runtime/stack.h" #include <ar.h> @@ -44,6 +45,12 @@ int nlibdir = 0; static int maxlibdir = 0; static int cout = -1; +// Set if we see an object compiled by the host compiler that is not +// from a package that is known to support internal linking mode. +static int externalobj = 0; + +static void hostlinksetup(void); + char* goroot; char* goarch; char* goos; @@ -59,11 +66,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 +98,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 +245,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,13 +291,27 @@ loadinternal(char *name) void loadlib(void) { - int i; + int i, w, x; + Sym *s; loadinternal("runtime"); if(thechar == '5') loadinternal("math"); if(flag_race) loadinternal("runtime/race"); + if(linkmode == LinkExternal) { + // This indicates a user requested -linkmode=external. + // The startup code uses an import of runtime/cgo to decide + // whether to initialize the TLS. So give it one. This could + // be handled differently but it's an unusual case. + loadinternal("runtime/cgo"); + // Pretend that we really imported the package. + // This will do no harm if we did in fact import it. + s = lookup("go.importpath.runtime/cgo.", 0); + s->type = SDATA; + s->dupok = 1; + s->reachable = 1; + } for(i=0; i<libraryp; i++) { if(debug['v']) @@ -303,6 +320,45 @@ loadlib(void) objfile(library[i].file, library[i].pkg); } + if(linkmode == LinkAuto) { + if(iscgo && externalobj) + linkmode = LinkExternal; + else + linkmode = LinkInternal; + } + + if(linkmode == LinkInternal) { + // Drop all the cgo_import_static declarations. + // Turns out we won't be needing them. + for(s = allsym; s != S; s = s->allsym) + if(s->type == SHOSTOBJ) { + // If a symbol was marked both + // cgo_import_static and cgo_import_dynamic, + // then we want to make it cgo_import_dynamic + // now. + if(s->extname != nil && s->dynimplib != nil && s->cgoexport == 0) { + s->type = SDYNIMPORT; + } else + s->type = 0; + } + } + + // Now that we know the link mode, trim the dynexp list. + x = CgoExportDynamic; + if(linkmode == LinkExternal) + x = CgoExportStatic; + w = 0; + for(i=0; i<ndynexp; i++) + if(dynexp[i]->cgoexport & 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) @@ -316,7 +372,6 @@ loadlib(void) debug['d'] = 1; importcycles(); - sortdynexp(); } /* @@ -375,7 +430,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 +489,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 +497,257 @@ 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[] = { + "crypto/x509", + "net", + "os/user", + "runtime/cgo", + "runtime/race" +}; + 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<nelem(internalpkg); i++) { + if(strcmp(pkg, internalpkg[i]) == 0) { + isinternal = 1; + break; + } + } + + if(!isinternal) + externalobj = 1; + + if(nhostobj >= mhostobj) { + if(mhostobj == 0) + mhostobj = 16; + else + mhostobj *= 2; + hostobj = erealloc(hostobj, mhostobj*sizeof hostobj[0]); + } + h = &hostobj[nhostobj++]; + h->ld = ld; + h->pkg = estrdup(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; i<nhostobj; i++) { + h = &hostobj[i]; + f = Bopen(h->file, 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); + } +} + +// provided by lib9 +int runcmd(char**); +char* mktempdir(void); +void removeall(char*); + +static void +rmtemp(void) +{ + removeall(tmpdir); +} + +static void +hostlinksetup(void) +{ + char *p; + + if(linkmode != LinkExternal) + return; + + // create temporary directory and arrange cleanup + if(tmpdir == nil) { + 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 c, i, w, n, argc, len; + Hostobj *h; + Biobuf *f; + static char buf[64<<10]; + + if(linkmode != LinkExternal || nerrors > 0) + return; + + c = 0; + p = extldflags; + while(p != nil) { + while(*p == ' ') + p++; + if(*p == '\0') + break; + c++; + p = strchr(p + 1, ' '); + } + + argv = malloc((10+nhostobj+nldflag+c)*sizeof argv[0]); + argc = 0; + if(extld == nil) + extld = "gcc"; + argv[argc++] = extld; + switch(thechar){ + case '8': + argv[argc++] = "-m32"; + break; + case '6': + argv[argc++] = "-m64"; + break; + } + if(!debug['s']) + argv[argc++] = "-gdwarf-2"; + if(HEADTYPE == Hdarwin) + argv[argc++] = "-Wl,-no_pie,-pagezero_size,4000000"; + argv[argc++] = "-o"; + argv[argc++] = outfile; + + if(rpath) + argv[argc++] = smprint("-Wl,-rpath,%s", rpath); + + // Force global symbols to be exported for dlopen, etc. + if(iself) + argv[argc++] = "-rdynamic"; + + // already wrote main object file + // copy host objects to temporary directory + for(i=0; i<nhostobj; i++) { + h = &hostobj[i]; + f = Bopen(h->file, 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) { + cursym = S; + 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) { + cursym = S; + diag("cannot write %s: %r", p); + errorexit(); + } + Bterm(f); + } + + argv[argc++] = smprint("%s/go.o", tmpdir); + for(i=0; i<nldflag; i++) + argv[argc++] = ldflag[i]; + + p = extldflags; + while(p != nil) { + while(*p == ' ') + *p++ = '\0'; + if(*p == '\0') + break; + argv[argc++] = p; + p = strchr(p + 1, ' '); + } + + argv[argc] = nil; + + quotefmtinstall(); + if(debug['v']) { + Bprint(&bso, "host link:"); + for(i=0; i<argc; i++) + Bprint(&bso, " %q", argv[i]); + Bprint(&bso, "\n"); + Bflush(&bso); + } + + if(runcmd(argv) < 0) { + cursym = S; + diag("%s: running %s failed: %r", argv0, argv[0]); + errorexit(); + } +} + +void +ldobj(Biobuf *f, char *pkg, int64 len, char *pn, char *file, int whence) { char *line; int n, c1, c2, c3, c4; @@ -453,7 +757,7 @@ ldobj(Biobuf *f, char *pkg, int64 len, char *pn, int whence) eof = Boffset(f) + len; - pn = strdup(pn); + pn = estrdup(pn); c1 = Bgetc(f); c2 = Bgetc(f); @@ -466,18 +770,15 @@ ldobj(Biobuf *f, char *pkg, int64 len, char *pn, int whence) magic = c1<<24 | c2<<16 | c3<<8 | c4; if(magic == 0x7f454c46) { // \x7F E L F - ldelf(f, pkg, len, pn); - free(pn); + ldhostobj(ldelf, f, pkg, len, pn, file); return; } if((magic&~1) == 0xfeedface || (magic&~0x01000000) == 0xcefaedfe) { - ldmacho(f, pkg, len, pn); - free(pn); + ldhostobj(ldmacho, f, pkg, len, pn, file); return; } if(c1 == 0x4c && c2 == 0x01 || c1 == 0x64 && c2 == 0x86) { - ldpe(f, pkg, len, pn); - free(pn); + ldhostobj(ldpe, f, pkg, len, pn, file); return; } @@ -524,7 +825,7 @@ ldobj(Biobuf *f, char *pkg, int64 len, char *pn, int whence) line[n] = '\0'; if(n-10 > 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); @@ -599,22 +900,22 @@ _lookup(char *symb, int v, int creat) Sym *s; char *p; int32 h; - int l, c; + int c; h = v; for(p=symb; c = *p; p++) h = h+h+h + c; - l = (p - symb) + 1; // not if(h < 0) h = ~h, because gcc 4.3 -O2 miscompiles it. h &= 0xffffff; h %= NHASH; for(s = hash[h]; s != S; s = s->hash) - if(memcmp(s->name, symb, l) == 0) + if(strcmp(s->name, symb) == 0) return s; if(!creat) return nil; s = newsym(symb, v); + s->extname = s->name; s->hash = hash[h]; hash[h] = s; @@ -1027,6 +1328,7 @@ addsection(Segment *seg, char *name, int rwx) sect->rwx = rwx; sect->name = name; sect->seg = seg; + sect->align = PtrSize; // everything is at least pointer-aligned *l = sect; return sect; } @@ -1460,23 +1762,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 +1861,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 && strcmp(s->name, ".rathole") != 0)) continue; switch(s->type&SMASK) { case SCONST: @@ -1591,8 +1876,6 @@ genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)) case SSTRING: case SGOSTRING: case SWINDOWS: - case SGCDATA: - case SGCBSS: if(!s->reachable) continue; put(s, s->name, 'D', symaddr(s), s->size, s->version, s->gotype); @@ -1630,7 +1913,10 @@ genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)) /* frame, locals, args, auto and param after */ put(nil, ".frame", 'm', (uint32)s->text->to.offset+PtrSize, 0, 0, 0); put(nil, ".locals", 'm', s->locals, 0, 0, 0); - put(nil, ".args", 'm', s->args, 0, 0, 0); + if(s->text->textflag & NOSPLIT) + put(nil, ".args", 'm', ArgsSizeUnknown, 0, 0, 0); + else + put(nil, ".args", 'm', s->args, 0, 0, 0); for(a=s->autom; a; a=a->link) { // Emit a or p according to actual offset, even if label is wrong. @@ -1664,3 +1950,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; +} |