summaryrefslogtreecommitdiff
path: root/src/cmd/ld/go.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/ld/go.c')
-rw-r--r--src/cmd/ld/go.c869
1 files changed, 869 insertions, 0 deletions
diff --git a/src/cmd/ld/go.c b/src/cmd/ld/go.c
new file mode 100644
index 000000000..fd7278a7b
--- /dev/null
+++ b/src/cmd/ld/go.c
@@ -0,0 +1,869 @@
+// 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.
+
+// go-specific code shared across loaders (5l, 6l, 8l).
+
+#include "l.h"
+#include "../ld/lib.h"
+
+// accumulate all type information from .6 files.
+// check for inconsistencies.
+
+// TODO:
+// generate debugging section in binary.
+// once the dust settles, try to move some code to
+// libmach, so that other linkers and ar can share.
+
+/*
+ * package import data
+ */
+typedef struct Import Import;
+struct Import
+{
+ Import *hash; // next in hash table
+ char *prefix; // "type", "var", "func", "const"
+ char *name;
+ char *def;
+ char *file;
+};
+enum {
+ NIHASH = 1024
+};
+static Import *ihash[NIHASH];
+static int nimport;
+static void imported(char *pkg, char *import);
+
+static int
+hashstr(char *name)
+{
+ int h;
+ char *cp;
+
+ h = 0;
+ for(cp = name; *cp; h += *cp++)
+ h *= 1119;
+ // not if(h < 0) h = ~h, because gcc 4.3 -O2 miscompiles it.
+ h &= 0xffffff;
+ return h;
+}
+
+static Import *
+ilookup(char *name)
+{
+ int h;
+ Import *x;
+
+ h = hashstr(name) % NIHASH;
+ for(x=ihash[h]; x; x=x->hash)
+ if(x->name[0] == name[0] && strcmp(x->name, name) == 0)
+ return x;
+ x = mal(sizeof *x);
+ x->name = strdup(name);
+ x->hash = ihash[h];
+ ihash[h] = x;
+ nimport++;
+ return x;
+}
+
+static void loadpkgdata(char*, char*, char*, int);
+static void loaddynimport(char*, char*, char*, int);
+static void loaddynexport(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)
+{
+ char *data, *p0, *p1, *name;
+
+ if(debug['g'])
+ return;
+
+ if((int)len != len) {
+ fprint(2, "%s: too much pkg data in %s\n", argv0, filename);
+ if(debug['u'])
+ errorexit();
+ return;
+ }
+ data = mal(len+1);
+ if(Bread(f, data, len) != len) {
+ fprint(2, "%s: short pkg read %s\n", argv0, filename);
+ if(debug['u'])
+ errorexit();
+ return;
+ }
+ data[len] = '\0';
+
+ // first \n$$ marks beginning of exports - skip rest of line
+ p0 = strstr(data, "\n$$");
+ if(p0 == nil) {
+ if(debug['u'] && whence != ArchiveObj) {
+ fprint(2, "%s: cannot find export data in %s\n", argv0, filename);
+ errorexit();
+ }
+ return;
+ }
+ p0 += 3;
+ while(*p0 != '\n' && *p0 != '\0')
+ p0++;
+
+ // second marks end of exports / beginning of local data
+ p1 = strstr(p0, "\n$$");
+ if(p1 == nil) {
+ fprint(2, "%s: cannot find end of exports in %s\n", argv0, filename);
+ if(debug['u'])
+ errorexit();
+ return;
+ }
+ while(p0 < p1 && (*p0 == ' ' || *p0 == '\t' || *p0 == '\n'))
+ p0++;
+ if(p0 < p1) {
+ if(strncmp(p0, "package ", 8) != 0) {
+ fprint(2, "%s: bad package section in %s - %s\n", argv0, filename, p0);
+ if(debug['u'])
+ errorexit();
+ return;
+ }
+ p0 += 8;
+ while(p0 < p1 && (*p0 == ' ' || *p0 == '\t' || *p0 == '\n'))
+ p0++;
+ name = p0;
+ while(p0 < p1 && *p0 != ' ' && *p0 != '\t' && *p0 != '\n')
+ p0++;
+ if(debug['u'] && whence != ArchiveObj &&
+ (p0+6 > p1 || memcmp(p0, " safe\n", 6) != 0)) {
+ fprint(2, "%s: load of unsafe package %s\n", argv0, filename);
+ nerrors++;
+ errorexit();
+ }
+ if(p0 < p1) {
+ if(*p0 == '\n')
+ *p0++ = '\0';
+ else {
+ *p0++ = '\0';
+ while(p0 < p1 && *p0++ != '\n')
+ ;
+ }
+ }
+ if(strcmp(pkg, "main") == 0 && strcmp(name, "main") != 0) {
+ fprint(2, "%s: %s: not package main (package %s)\n", argv0, filename, name);
+ nerrors++;
+ errorexit();
+ }
+ loadpkgdata(filename, pkg, p0, p1 - p0);
+ }
+
+ // The __.PKGDEF archive summary has no local types.
+ if(whence == Pkgdef)
+ return;
+
+ // local types begin where exports end.
+ // skip rest of line after $$ we found above
+ p0 = p1 + 3;
+ while(*p0 != '\n' && *p0 != '\0')
+ p0++;
+
+ // local types end at next \n$$.
+ p1 = strstr(p0, "\n$$");
+ if(p1 == nil) {
+ fprint(2, "%s: cannot find end of local types in %s\n", argv0, filename);
+ if(debug['u'])
+ errorexit();
+ return;
+ }
+
+ loadpkgdata(filename, pkg, p0, p1 - p0);
+
+ // look for dynimport section
+ p0 = strstr(p1, "\n$$ // dynimport");
+ if(p0 != nil) {
+ p0 = strchr(p0+1, '\n');
+ if(p0 == nil) {
+ fprint(2, "%s: found $$ // dynimport but no newline in %s\n", argv0, filename);
+ if(debug['u'])
+ errorexit();
+ return;
+ }
+ p1 = strstr(p0, "\n$$");
+ if(p1 == nil)
+ p1 = strstr(p0, "\n!\n");
+ if(p1 == nil) {
+ fprint(2, "%s: cannot find end of // dynimport section in %s\n", argv0, filename);
+ if(debug['u'])
+ errorexit();
+ return;
+ }
+ loaddynimport(filename, pkg, p0 + 1, p1 - (p0+1));
+ }
+
+ // look for dynexp section
+ p0 = strstr(p1, "\n$$ // dynexport");
+ if(p0 != nil) {
+ p0 = strchr(p0+1, '\n');
+ if(p0 == nil) {
+ fprint(2, "%s: found $$ // dynexporg but no newline in %s\n", argv0, filename);
+ if(debug['u'])
+ errorexit();
+ return;
+ }
+ p1 = strstr(p0, "\n$$");
+ if(p1 == nil)
+ p1 = strstr(p0, "\n!\n");
+ if(p1 == nil) {
+ fprint(2, "%s: cannot find end of // dynexporg section in %s\n", argv0, filename);
+ if(debug['u'])
+ errorexit();
+ return;
+ }
+ loaddynexport(filename, pkg, p0 + 1, p1 - (p0+1));
+ }
+}
+
+static void
+loadpkgdata(char *file, char *pkg, char *data, int len)
+{
+ char *p, *ep, *prefix, *name, *def;
+ Import *x;
+
+ file = strdup(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 = def;
+ x->file = file;
+ } else if(strcmp(x->prefix, prefix) != 0) {
+ fprint(2, "%s: conflicting definitions for %s\n", argv0, name);
+ fprint(2, "%s:\t%s %s ...\n", x->file, x->prefix, name);
+ fprint(2, "%s:\t%s %s ...\n", file, prefix, name);
+ nerrors++;
+ } else if(strcmp(x->def, def) != 0) {
+ fprint(2, "%s: conflicting definitions for %s\n", argv0, name);
+ fprint(2, "%s:\t%s %s %s\n", x->file, x->prefix, name, x->def);
+ fprint(2, "%s:\t%s %s %s\n", file, prefix, name, def);
+ nerrors++;
+ }
+ }
+}
+
+// replace all "". with pkg.
+char*
+expandpkg(char *t0, char *pkg)
+{
+ int n;
+ char *p;
+ char *w, *w0, *t;
+
+ n = 0;
+ for(p=t0; (p=strstr(p, "\"\".")) != nil; p+=3)
+ n++;
+
+ if(n == 0)
+ return t0;
+
+ // use malloc, not mal, so that caller can free
+ w0 = malloc(strlen(t0) + strlen(pkg)*n);
+ if(w0 == nil) {
+ diag("out of memory");
+ errorexit();
+ }
+ w = w0;
+ for(p=t=t0; (p=strstr(p, "\"\".")) != nil; p=t) {
+ memmove(w, t, p - t);
+ w += p-t;
+ strcpy(w, pkg);
+ w += strlen(pkg);
+ t = p+2;
+ }
+ strcpy(w, t);
+ return w0;
+}
+
+static int
+parsepkgdata(char *file, char *pkg, char **pp, char *ep, char **prefixp, char **namep, char **defp)
+{
+ char *p, *prefix, *name, *def, *edef, *meth;
+ int n, inquote;
+
+ // skip white space
+ p = *pp;
+loop:
+ while(p < ep && (*p == ' ' || *p == '\t' || *p == '\n'))
+ p++;
+ if(p == ep || strncmp(p, "$$\n", 3) == 0)
+ return 0;
+
+ // prefix: (var|type|func|const)
+ prefix = p;
+ if(p + 7 > ep)
+ return -1;
+ if(strncmp(p, "var ", 4) == 0)
+ p += 4;
+ else if(strncmp(p, "type ", 5) == 0)
+ p += 5;
+ else if(strncmp(p, "func ", 5) == 0)
+ p += 5;
+ else if(strncmp(p, "const ", 6) == 0)
+ p += 6;
+ else if(strncmp(p, "import ", 7) == 0) {
+ p += 7;
+ while(p < ep && *p != ' ')
+ p++;
+ p++;
+ name = p;
+ while(p < ep && *p != '\n')
+ p++;
+ if(p >= ep) {
+ fprint(2, "%s: %s: confused in import line\n", argv0, file);
+ nerrors++;
+ return -1;
+ }
+ *p++ = '\0';
+ imported(pkg, name);
+ goto loop;
+ }
+ else {
+ fprint(2, "%s: %s: confused in pkg data near <<%.40s>>\n", argv0, file, prefix);
+ nerrors++;
+ return -1;
+ }
+ p[-1] = '\0';
+
+ // name: a.b followed by space
+ name = p;
+ inquote = 0;
+ while(p < ep) {
+ if (*p == ' ' && !inquote)
+ break;
+
+ if(*p == '\\')
+ p++;
+ else if(*p == '"')
+ inquote = !inquote;
+
+ p++;
+ }
+
+ if(p >= ep)
+ return -1;
+ *p++ = '\0';
+
+ // def: free form to new line
+ def = p;
+ while(p < ep && *p != '\n')
+ p++;
+ if(p >= ep)
+ return -1;
+ edef = p;
+ *p++ = '\0';
+
+ // include methods on successive lines in def of named type
+ while(parsemethod(&p, ep, &meth) > 0) {
+ *edef++ = '\n'; // overwrites '\0'
+ if(edef+1 > meth) {
+ // We want to indent methods with a single \t.
+ // 6g puts at least one char of indent before all method defs,
+ // so there will be room for the \t. If the method def wasn't
+ // indented we could do something more complicated,
+ // but for now just diagnose the problem and assume
+ // 6g will keep indenting for us.
+ fprint(2, "%s: %s: expected methods to be indented %p %p %.10s\n", argv0,
+ file, edef, meth, meth);
+ nerrors++;
+ return -1;
+ }
+ *edef++ = '\t';
+ n = strlen(meth);
+ memmove(edef, meth, n);
+ edef += n;
+ }
+
+ name = expandpkg(name, pkg);
+ def = expandpkg(def, pkg);
+
+ // done
+ *pp = p;
+ *prefixp = prefix;
+ *namep = name;
+ *defp = def;
+ return 1;
+}
+
+static int
+parsemethod(char **pp, char *ep, char **methp)
+{
+ char *p;
+
+ // skip white space
+ p = *pp;
+ while(p < ep && (*p == ' ' || *p == '\t'))
+ p++;
+ if(p == ep)
+ return 0;
+
+ // if it says "func (", it's a method
+ if(p + 6 >= ep || strncmp(p, "func (", 6) != 0)
+ return 0;
+
+ // definition to end of line
+ *methp = p;
+ while(p < ep && *p != '\n')
+ p++;
+ if(p >= ep) {
+ fprint(2, "%s: lost end of line in method definition\n", argv0);
+ *pp = ep;
+ return -1;
+ }
+ *p++ = '\0';
+ *pp = p;
+ return 1;
+}
+
+static void
+loaddynimport(char *file, char *pkg, char *p, int n)
+{
+ char *pend, *next, *name, *def, *p0, *lib, *q;
+ Sym *s;
+
+ USED(file);
+ pend = p + n;
+ for(; p<pend; p=next) {
+ next = strchr(p, '\n');
+ if(next == nil)
+ next = "";
+ else
+ *next++ = '\0';
+ p0 = p;
+ if(strncmp(p, "dynimport ", 10) != 0)
+ goto err;
+ p += 10;
+ name = p;
+ p = strchr(name, ' ');
+ if(p == nil)
+ goto err;
+ while(*p == ' ')
+ p++;
+ def = p;
+ p = strchr(def, ' ');
+ if(p == nil)
+ goto err;
+ while(*p == ' ')
+ p++;
+ lib = p;
+
+ // successful parse: now can edit the line
+ *strchr(name, ' ') = 0;
+ *strchr(def, ' ') = 0;
+
+ if(debug['d']) {
+ fprint(2, "%s: %s: cannot use dynamic imports with -d flag\n", argv0, file);
+ nerrors++;
+ return;
+ }
+
+ if(strcmp(name, "_") == 0 && strcmp(def, "_") == 0) {
+ // allow #pragma dynimport _ _ "foo.so"
+ // to force a link of foo.so.
+ havedynamic = 1;
+ adddynlib(lib);
+ continue;
+ }
+
+ name = expandpkg(name, pkg);
+ q = strchr(def, '@');
+ if(q)
+ *q++ = '\0';
+ s = lookup(name, 0);
+ if(s->type == 0 || s->type == SXREF) {
+ s->dynimplib = lib;
+ s->dynimpname = def;
+ s->dynimpvers = q;
+ s->type = SDYNIMPORT;
+ havedynamic = 1;
+ }
+ }
+ return;
+
+err:
+ fprint(2, "%s: %s: invalid dynimport line: %s\n", argv0, file, p0);
+ nerrors++;
+}
+
+static void
+loaddynexport(char *file, char *pkg, char *p, int n)
+{
+ char *pend, *next, *local, *elocal, *remote, *p0;
+ Sym *s;
+
+ USED(file);
+ pend = p + n;
+ for(; p<pend; p=next) {
+ next = strchr(p, '\n');
+ if(next == nil)
+ next = "";
+ else
+ *next++ = '\0';
+ p0 = p;
+ if(strncmp(p, "dynexport ", 10) != 0)
+ goto err;
+ p += 10;
+ local = p;
+ p = strchr(local, ' ');
+ if(p == nil)
+ goto err;
+ while(*p == ' ')
+ p++;
+ remote = p;
+
+ // successful parse: now can edit the line
+ *strchr(local, ' ') = 0;
+
+ elocal = expandpkg(local, pkg);
+
+ s = lookup(elocal, 0);
+ if(s->dynimplib != nil) {
+ fprint(2, "%s: symbol is both dynimport and dynexport %s\n", argv0, local);
+ nerrors++;
+ }
+ s->dynimpname = remote;
+ s->dynexport = 1;
+
+ if(ndynexp%32 == 0)
+ dynexp = realloc(dynexp, (ndynexp+32)*sizeof dynexp[0]);
+ dynexp[ndynexp++] = s;
+
+ if (elocal != local)
+ free(elocal);
+ }
+ return;
+
+err:
+ fprint(2, "%s: invalid dynexport line: %s\n", argv0, p0);
+ nerrors++;
+}
+
+static int markdepth;
+
+static void
+marktext(Sym *s)
+{
+ Auto *a;
+ Prog *p;
+
+ if(s == S)
+ return;
+ markdepth++;
+ if(debug['v'] > 1)
+ Bprint(&bso, "%d marktext %s\n", markdepth, s->name);
+ for(a=s->autom; a; a=a->link)
+ mark(a->gotype);
+ for(p=s->text; p != P; p=p->link) {
+ if(p->from.sym)
+ mark(p->from.sym);
+ if(p->to.sym)
+ mark(p->to.sym);
+ }
+ markdepth--;
+}
+
+void
+mark(Sym *s)
+{
+ int i;
+
+ if(s == S || s->reachable)
+ return;
+ if(strncmp(s->name, "weak.", 5) == 0)
+ return;
+ s->reachable = 1;
+ if(s->text)
+ marktext(s);
+ for(i=0; i<s->nr; i++)
+ mark(s->r[i].sym);
+ if(s->gotype)
+ mark(s->gotype);
+ if(s->sub)
+ mark(s->sub);
+ if(s->outer)
+ mark(s->outer);
+}
+
+static char*
+morename[] =
+{
+ "runtime.morestack",
+ "runtime.morestackx",
+
+ "runtime.morestack00",
+ "runtime.morestack10",
+ "runtime.morestack01",
+ "runtime.morestack11",
+
+ "runtime.morestack8",
+ "runtime.morestack16",
+ "runtime.morestack24",
+ "runtime.morestack32",
+ "runtime.morestack40",
+ "runtime.morestack48",
+};
+
+static int
+isz(Auto *a)
+{
+ for(; a; a=a->link)
+ if(a->type == D_FILE || a->type == D_FILE1)
+ return 1;
+ return 0;
+}
+
+static void
+addz(Sym *s, Auto *z)
+{
+ Auto *a, *last;
+
+ // strip out non-z
+ last = nil;
+ for(a = z; a != nil; a = a->link) {
+ if(a->type == D_FILE || a->type == D_FILE1) {
+ if(last == nil)
+ z = a;
+ else
+ last->link = a;
+ last = a;
+ }
+ }
+ if(last) {
+ last->link = s->autom;
+ s->autom = z;
+ }
+}
+
+void
+deadcode(void)
+{
+ int i;
+ Sym *s, *last;
+ Auto *z;
+
+ if(debug['v'])
+ Bprint(&bso, "%5.2f deadcode\n", cputime());
+
+ mark(lookup(INITENTRY, 0));
+ for(i=0; i<nelem(morename); i++)
+ mark(lookup(morename[i], 0));
+
+ for(i=0; i<ndynexp; i++)
+ mark(dynexp[i]);
+
+ // remove dead text but keep file information (z symbols).
+ last = nil;
+ z = nil;
+ for(s = textp; s != nil; s = s->next) {
+ if(!s->reachable) {
+ if(isz(s->autom))
+ z = s->autom;
+ continue;
+ }
+ if(last == nil)
+ textp = s;
+ else
+ last->next = s;
+ last = s;
+ if(z != nil) {
+ if(!isz(s->autom))
+ addz(s, z);
+ z = nil;
+ }
+ }
+ if(last == nil)
+ textp = nil;
+ else
+ last->next = nil;
+
+ for(s = allsym; s != S; s = s->allsym)
+ if(strncmp(s->name, "weak.", 5) == 0) {
+ s->special = 1; // do not lay out in data segment
+ s->reachable = 1;
+ s->hide = 1;
+ }
+}
+
+void
+doweak(void)
+{
+ Sym *s, *t;
+
+ // resolve weak references only if
+ // target symbol will be in binary anyway.
+ for(s = allsym; s != S; s = s->allsym) {
+ if(strncmp(s->name, "weak.", 5) == 0) {
+ t = rlookup(s->name+5, s->version);
+ if(t && t->type != 0 && t->reachable) {
+ s->value = t->value;
+ s->type = t->type;
+ } else {
+ s->type = SCONST;
+ s->value = 0;
+ }
+ continue;
+ }
+ }
+}
+
+void
+addexport(void)
+{
+ int i;
+
+ for(i=0; i<ndynexp; i++)
+ adddynsym(dynexp[i]);
+}
+
+/* %Z from gc, for quoting import paths */
+int
+Zconv(Fmt *fp)
+{
+ Rune r;
+ char *s, *se;
+ int n;
+
+ s = va_arg(fp->args, char*);
+ if(s == nil)
+ return fmtstrcpy(fp, "<nil>");
+
+ se = s + strlen(s);
+ while(s < se) {
+ n = chartorune(&r, s);
+ s += n;
+ switch(r) {
+ case Runeerror:
+ if(n == 1) {
+ fmtprint(fp, "\\x%02x", (uchar)*(s-1));
+ break;
+ }
+ // fall through
+ default:
+ if(r < ' ') {
+ fmtprint(fp, "\\x%02x", r);
+ break;
+ }
+ fmtrune(fp, r);
+ break;
+ case '\t':
+ fmtstrcpy(fp, "\\t");
+ break;
+ case '\n':
+ fmtstrcpy(fp, "\\n");
+ break;
+ case '\"':
+ case '\\':
+ fmtrune(fp, '\\');
+ fmtrune(fp, r);
+ break;
+ }
+ }
+ return 0;
+}
+
+
+typedef struct Pkg Pkg;
+struct Pkg
+{
+ uchar mark;
+ uchar checked;
+ Pkg *next;
+ char *path;
+ Pkg **impby;
+ int nimpby;
+ int mimpby;
+ Pkg *all;
+};
+
+static Pkg *phash[1024];
+static Pkg *pkgall;
+
+static Pkg*
+getpkg(char *path)
+{
+ Pkg *p;
+ int h;
+
+ h = hashstr(path) % nelem(phash);
+ for(p=phash[h]; p; p=p->next)
+ if(strcmp(p->path, path) == 0)
+ return p;
+ p = mal(sizeof *p);
+ p->path = strdup(path);
+ p->next = phash[h];
+ phash[h] = p;
+ p->all = pkgall;
+ pkgall = p;
+ return p;
+}
+
+static void
+imported(char *pkg, char *import)
+{
+ Pkg *p, *i;
+
+ // everyone imports runtime, even runtime.
+ if(strcmp(import, "\"runtime\"") == 0)
+ return;
+
+ pkg = smprint("\"%Z\"", pkg); // turn pkg path into quoted form, freed below
+ p = getpkg(pkg);
+ i = getpkg(import);
+ if(i->nimpby >= i->mimpby) {
+ i->mimpby *= 2;
+ if(i->mimpby == 0)
+ i->mimpby = 16;
+ i->impby = realloc(i->impby, i->mimpby*sizeof i->impby[0]);
+ }
+ i->impby[i->nimpby++] = p;
+ free(pkg);
+}
+
+static Pkg*
+cycle(Pkg *p)
+{
+ int i;
+ Pkg *bad;
+
+ if(p->checked)
+ return 0;
+
+ if(p->mark) {
+ nerrors++;
+ print("import cycle:\n");
+ print("\t%s\n", p->path);
+ return p;
+ }
+ p->mark = 1;
+ for(i=0; i<p->nimpby; i++) {
+ if((bad = cycle(p->impby[i])) != nil) {
+ p->mark = 0;
+ p->checked = 1;
+ print("\timports %s\n", p->path);
+ if(bad == p)
+ return nil;
+ return bad;
+ }
+ }
+ p->checked = 1;
+ p->mark = 0;
+ return 0;
+}
+
+void
+importcycles(void)
+{
+ Pkg *p;
+
+ for(p=pkgall; p; p=p->all)
+ cycle(p);
+}