diff options
Diffstat (limited to 'src/cmd/ld/go.c')
-rw-r--r-- | src/cmd/ld/go.c | 869 |
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); +} |