summaryrefslogtreecommitdiff
path: root/src/cmd/ld/lib.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/ld/lib.c')
-rw-r--r--src/cmd/ld/lib.c398
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;
+}