// 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) { uint32 h; char *cp; h = 0; for(cp = name; *cp; h += *cp++) h *= 1119; 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 = estrdup(name); x->hash = ihash[h]; ihash[h] = x; nimport++; return x; } static void loadpkgdata(char*, char*, char*, int); static void loadcgo(char*, char*, char*, int); static int parsemethod(char**, char*, char**); static int parsepkgdata(char*, char*, char**, char*, char**, char**, char**); 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); } // __.PKGDEF has no cgo section - those are in the C compiler-generated object files. if(whence == Pkgdef) return; // look for cgo section p0 = strstr(p1, "\n$$ // cgo"); if(p0 != nil) { p0 = strchr(p0+1, '\n'); if(p0 == nil) { fprint(2, "%s: found $$ // cgo 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 // cgo section in %s\n", argv0, filename); if(debug['u']) errorexit(); return; } loadcgo(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 = 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 = estrdup(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++; } free(name); free(def); } free(file); } 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; // might be a comment about the method if(p + 2 < ep && strncmp(p, "//", 2) == 0) goto useline; // if it says "func (", it's a method if(p + 6 < ep && strncmp(p, "func (", 6) == 0) goto useline; return 0; useline: // 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 loadcgo(char *file, char *pkg, char *p, int n) { char *pend, *next, *p0, *q; char *f[10], *local, *remote, *lib; int nf; LSym *s; USED(file); pend = p + n; p0 = nil; for(; p 4) goto err; local = f[1]; remote = local; if(nf > 2) remote = f[2]; lib = ""; if(nf > 3) lib = f[3]; if(debug['d']) { fprint(2, "%s: %s: cannot use dynamic imports with -d flag\n", argv0, file); nerrors++; return; } if(strcmp(local, "_") == 0 && strcmp(remote, "_") == 0) { // allow #pragma dynimport _ _ "foo.so" // to force a link of foo.so. havedynamic = 1; adddynlib(lib); continue; } local = expandpkg(local, pkg); q = strchr(remote, '#'); if(q) *q++ = '\0'; s = linklookup(ctxt, local, 0); if(local != f[1]) free(local); if(s->type == 0 || s->type == SXREF || s->type == SHOSTOBJ) { s->dynimplib = lib; s->extname = remote; s->dynimpvers = q; if(s->type != SHOSTOBJ) s->type = SDYNIMPORT; havedynamic = 1; } continue; } if(strcmp(f[0], "cgo_import_static") == 0) { if(nf != 2) goto err; local = f[1]; s = linklookup(ctxt, local, 0); s->type = SHOSTOBJ; s->size = 0; continue; } if(strcmp(f[0], "cgo_export_static") == 0 || strcmp(f[0], "cgo_export_dynamic") == 0) { // TODO: Remove once we know Windows is okay. if(strcmp(f[0], "cgo_export_static") == 0 && HEADTYPE == Hwindows) continue; if(nf < 2 || nf > 3) goto err; local = f[1]; if(nf > 2) remote = f[2]; else remote = local; local = expandpkg(local, pkg); s = linklookup(ctxt, local, 0); if(flag_shared && s == linklookup(ctxt, "main", 0)) continue; // export overrides import, for openbsd/cgo. // see issue 4878. if(s->dynimplib != nil) { s->dynimplib = nil; s->extname = nil; s->dynimpvers = nil; s->type = 0; } if(s->cgoexport == 0) { s->extname = remote; if(ndynexp%32 == 0) dynexp = erealloc(dynexp, (ndynexp+32)*sizeof dynexp[0]); dynexp[ndynexp++] = s; } else if(strcmp(s->extname, remote) != 0) { fprint(2, "%s: conflicting cgo_export directives: %s as %s and %s\n", argv0, s->name, s->extname, remote); nerrors++; return; } if(strcmp(f[0], "cgo_export_static") == 0) s->cgoexport |= CgoExportStatic; else s->cgoexport |= CgoExportDynamic; if(local != f[1]) free(local); continue; } if(strcmp(f[0], "cgo_dynamic_linker") == 0) { if(nf != 2) goto err; if(!debug['I']) { // not overridden by command line if(interpreter != nil && strcmp(interpreter, f[1]) != 0) { fprint(2, "%s: conflict dynlinker: %s and %s\n", argv0, interpreter, f[1]); nerrors++; return; } free(interpreter); interpreter = estrdup(f[1]); } continue; } if(strcmp(f[0], "cgo_ldflag") == 0) { if(nf != 2) goto err; if(nldflag%32 == 0) ldflag = erealloc(ldflag, (nldflag+32)*sizeof ldflag[0]); ldflag[nldflag++] = estrdup(f[1]); continue; } } free(p0); return; err: fprint(2, "%s: %s: invalid dynimport line: %s\n", argv0, file, p0); nerrors++; } static LSym *markq; static LSym *emarkq; static void mark1(LSym *s, LSym *parent) { if(s == S || s->reachable) return; if(strncmp(s->name, "go.weak.", 8) == 0) return; s->reachable = 1; s->reachparent = parent; if(markq == nil) markq = s; else emarkq->queue = s; emarkq = s; } void mark(LSym *s) { mark1(s, nil); } static void markflood(void) { Auto *a; LSym *s; int i; for(s=markq; s!=S; s=s->queue) { if(s->type == STEXT) { if(debug['v'] > 1) Bprint(&bso, "marktext %s\n", s->name); for(a=s->autom; a; a=a->link) mark1(a->gotype, s); } for(i=0; inr; i++) mark1(s->r[i].sym, s); if(s->pcln) { for(i=0; ipcln->nfuncdata; i++) mark1(s->pcln->funcdata[i], s); } mark1(s->gotype, s); mark1(s->sub, s); mark1(s->outer, s); } } static char* markextra[] = { "runtime.morestack", "runtime.morestackx", "runtime.morestack00", "runtime.morestack10", "runtime.morestack01", "runtime.morestack11", "runtime.morestack8", "runtime.morestack16", "runtime.morestack24", "runtime.morestack32", "runtime.morestack40", "runtime.morestack48", // on arm, lock in the div/mod helpers too "_div", "_divu", "_mod", "_modu", }; void deadcode(void) { int i; LSym *s, *last, *p; Fmt fmt; if(debug['v']) Bprint(&bso, "%5.2f deadcode\n", cputime()); mark(linklookup(ctxt, INITENTRY, 0)); for(i=0; iallsym; s != S; s = s->allsym) { if(strncmp(s->name, "go.typelink.", 12) == 0) s->reachable = s->nr==1 && s->r[0].sym->reachable; } // remove dead text but keep file information (z symbols). last = nil; for(s = ctxt->textp; s != nil; s = s->next) { if(!s->reachable) continue; // NOTE: Removing s from old textp and adding to new, shorter textp. if(last == nil) ctxt->textp = s; else last->next = s; last = s; } if(last == nil) ctxt->textp = nil; else last->next = nil; for(s = ctxt->allsym; s != S; s = s->allsym) if(strncmp(s->name, "go.weak.", 8) == 0) { s->special = 1; // do not lay out in data segment s->reachable = 1; s->hide = 1; } // record field tracking references fmtstrinit(&fmt); for(s = ctxt->allsym; s != S; s = s->allsym) { if(strncmp(s->name, "go.track.", 9) == 0) { s->special = 1; // do not lay out in data segment s->hide = 1; if(s->reachable) { fmtprint(&fmt, "%s", s->name+9); for(p=s->reachparent; p; p=p->reachparent) fmtprint(&fmt, "\t%s", p->name); fmtprint(&fmt, "\n"); } s->type = SCONST; s->value = 0; } } if(tracksym == nil) return; s = linklookup(ctxt, tracksym, 0); if(!s->reachable) return; addstrdata(tracksym, fmtstrflush(&fmt)); } void doweak(void) { LSym *s, *t; // resolve weak references only if // target symbol will be in binary anyway. for(s = ctxt->allsym; s != S; s = s->allsym) { if(strncmp(s->name, "go.weak.", 8) == 0) { t = linkrlookup(ctxt, s->name+8, s->version); if(t && t->type != 0 && t->reachable) { s->value = t->value; s->type = t->type; s->outer = t; } else { s->type = SCONST; s->value = 0; } continue; } } } void addexport(void) { int i; if(HEADTYPE == Hdarwin) return; for(i=0; iargs, char*); if(s == nil) return fmtstrcpy(fp, ""); se = s + strlen(s); // NOTE: Keep in sync with ../gc/go.c:/^Zconv. 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; case 0xFEFF: // BOM, basically disallowed in source code fmtstrcpy(fp, "\\uFEFF"); 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 = estrdup(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 = erealloc(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; inimpby; 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); } void setlinkmode(char *arg) { if(strcmp(arg, "internal") == 0) linkmode = LinkInternal; else if(strcmp(arg, "external") == 0) linkmode = LinkExternal; else if(strcmp(arg, "auto") == 0) linkmode = LinkAuto; else { fprint(2, "unknown link mode -linkmode %s\n", arg); errorexit(); } }