diff options
Diffstat (limited to 'src/cmd/ld/lib.c')
-rw-r--r-- | src/cmd/ld/lib.c | 1625 |
1 files changed, 321 insertions, 1304 deletions
diff --git a/src/cmd/ld/lib.c b/src/cmd/ld/lib.c index da522dc0c..da6194e4f 100644 --- a/src/cmd/ld/lib.c +++ b/src/cmd/ld/lib.c @@ -32,10 +32,14 @@ #include "l.h" #include "lib.h" #include "../ld/elf.h" +#include "../ld/dwarf.h" #include "../../pkg/runtime/stack.h" #include "../../pkg/runtime/funcdata.h" #include <ar.h> +#if !(defined(_WIN32) || defined(PLAN9)) +#include <sys/stat.h> +#endif enum { @@ -48,18 +52,9 @@ int iconv(Fmt*); char symname[] = SYMDEF; char pkgname[] = "__.PKGDEF"; -char** libdir; -int nlibdir = 0; -static int maxlibdir = 0; static int cout = -1; -// symbol version, incremented each time a file is loaded. -// version==1 is reserved for savehist. -enum -{ - HistVersion = 1, -}; -int version = HistVersion; +extern int version; // 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. @@ -77,15 +72,32 @@ Lflag(char *arg) { char **p; - if(nlibdir >= maxlibdir) { - if (maxlibdir == 0) - maxlibdir = 8; + if(ctxt->nlibdir >= ctxt->maxlibdir) { + if (ctxt->maxlibdir == 0) + ctxt->maxlibdir = 8; else - maxlibdir *= 2; - p = erealloc(libdir, maxlibdir * sizeof(*p)); - libdir = p; + ctxt->maxlibdir *= 2; + p = erealloc(ctxt->libdir, ctxt->maxlibdir * sizeof(*p)); + ctxt->libdir = p; } - libdir[nlibdir++] = arg; + ctxt->libdir[ctxt->nlibdir++] = arg; +} + +/* + * Unix doesn't like it when we write to a running (or, sometimes, + * recently run) binary, so remove the output file before writing it. + * On Windows 7, remove() can force a subsequent create() to fail. + * S_ISREG() does not exist on Plan 9. + */ +static void +mayberemoveoutfile(void) +{ +#if !(defined(_WIN32) || defined(PLAN9)) + struct stat st; + if(lstat(outfile, &st) == 0 && !S_ISREG(st.st_mode)) + return; +#endif + remove(outfile); } void @@ -93,12 +105,11 @@ libinit(void) { char *suffix, *suffixsep; + funcalign = FuncAlign; fmtinstall('i', iconv); fmtinstall('Y', Yconv); fmtinstall('Z', Zconv); mywhatsys(); // get goroot, goarch, goos - if(strcmp(goarch, thestring) != 0) - print("goarch is not known: %s\n", goarch); // add goroot to the end of the libdir list. suffix = ""; @@ -112,12 +123,7 @@ libinit(void) } Lflag(smprint("%s/pkg/%s_%s%s%s", goroot, goos, goarch, suffixsep, suffix)); - // Unix doesn't like it when we write to a running (or, sometimes, - // recently run) binary, so remove the output file before writing it. - // On Windows 7, remove() can force the following create() to fail. -#ifndef _WIN32 - remove(outfile); -#endif + mayberemoveoutfile(); cout = create(outfile, 1, 0775); if(cout < 0) { diag("cannot create %s: %r", outfile); @@ -132,7 +138,7 @@ libinit(void) sprint(INITENTRY, "_rt0_%s_%s_lib", goarch, goos); } } - lookup(INITENTRY, 0)->type = SXREF; + linklookup(ctxt, INITENTRY, 0)->type = SXREF; } void @@ -140,165 +146,25 @@ errorexit(void) { if(nerrors) { if(cout >= 0) - remove(outfile); + mayberemoveoutfile(); exits("error"); } exits(0); } void -addlib(char *src, char *obj) -{ - char name[1024], pname[1024], comp[256], *p; - int i, search; - - if(histfrogp <= 0) - return; - - search = 0; - if(histfrog[0]->name[1] == '/') { - sprint(name, ""); - i = 1; - } else - if(isalpha((uchar)histfrog[0]->name[1]) && histfrog[0]->name[2] == ':') { - strcpy(name, histfrog[0]->name+1); - i = 1; - } else - if(histfrog[0]->name[1] == '.') { - sprint(name, "."); - i = 0; - } else { - sprint(name, ""); - i = 0; - search = 1; - } - - for(; i<histfrogp; i++) { - snprint(comp, sizeof comp, "%s", histfrog[i]->name+1); - for(;;) { - p = strstr(comp, "$O"); - if(p == 0) - break; - memmove(p+1, p+2, strlen(p+2)+1); - p[0] = thechar; - } - for(;;) { - p = strstr(comp, "$M"); - if(p == 0) - break; - if(strlen(comp)+strlen(thestring)-2+1 >= sizeof comp) { - diag("library component too long"); - return; - } - memmove(p+strlen(thestring), p+2, strlen(p+2)+1); - memmove(p, thestring, strlen(thestring)); - } - if(strlen(name) + strlen(comp) + 3 >= sizeof(name)) { - diag("library component too long"); - return; - } - if(i > 0 || !search) - strcat(name, "/"); - strcat(name, comp); - } - cleanname(name); - - // runtime.a -> runtime - p = nil; - if(strlen(name) > 2 && name[strlen(name)-2] == '.') { - p = name+strlen(name)-2; - *p = '\0'; - } - - // already loaded? - for(i=0; i<libraryp; i++) - if(strcmp(library[i].pkg, name) == 0) - return; - - // runtime -> runtime.a for search - if(p != nil) - *p = '.'; - - if(search) { - // try dot, -L "libdir", and then goroot. - for(i=0; i<nlibdir; i++) { - snprint(pname, sizeof pname, "%s/%s", libdir[i], name); - if(access(pname, AEXIST) >= 0) - break; - } - }else - strcpy(pname, name); - cleanname(pname); - - /* runtime.a -> runtime */ - if(p != nil) - *p = '\0'; - - if(debug['v'] > 1) - Bprint(&bso, "%5.2f addlib: %s %s pulls in %s\n", cputime(), obj, src, pname); - - addlibpath(src, obj, pname, name); -} - -/* - * add library to library list. - * srcref: src file referring to package - * objref: object file referring to package - * file: object file, e.g., /home/rsc/go/pkg/container/vector.a - * pkg: package import path, e.g. container/vector - */ -void -addlibpath(char *srcref, char *objref, char *file, char *pkg) -{ - int i; - Library *l; - char *p; - - for(i=0; i<libraryp; i++) - if(strcmp(file, library[i].file) == 0) - return; - - if(debug['v'] > 1) - Bprint(&bso, "%5.2f addlibpath: srcref: %s objref: %s file: %s pkg: %s\n", - cputime(), srcref, objref, file, pkg); - - if(libraryp == nlibrary){ - nlibrary = 50 + 2*libraryp; - library = erealloc(library, sizeof library[0] * nlibrary); - } - - l = &library[libraryp++]; - - p = mal(strlen(objref) + 1); - strcpy(p, objref); - l->objref = p; - - p = mal(strlen(srcref) + 1); - strcpy(p, srcref); - l->srcref = p; - - p = mal(strlen(file) + 1); - strcpy(p, file); - l->file = p; - - p = mal(strlen(pkg) + 1); - strcpy(p, pkg); - l->pkg = p; -} - -void loadinternal(char *name) { char pname[1024]; int i, found; found = 0; - for(i=0; i<nlibdir; i++) { - snprint(pname, sizeof pname, "%s/%s.a", libdir[i], name); + for(i=0; i<ctxt->nlibdir; i++) { + snprint(pname, sizeof pname, "%s/%s.a", ctxt->libdir[i], name); if(debug['v']) Bprint(&bso, "searching for %s.a in %s\n", name, pname); if(access(pname, AEXIST) >= 0) { - addlibpath("internal", "internal", pname, name); + addlibpath(ctxt, "internal", "internal", pname, name); found = 1; break; } @@ -311,12 +177,13 @@ void loadlib(void) { int i, w, x; - Sym *s, *gmsym; + LSym *s, *gmsym; + char* cgostrsym; if(flag_shared) { - s = lookup("runtime.islibrary", 0); + s = linklookup(ctxt, "runtime.islibrary", 0); s->dupok = 1; - adduint8(s, 1); + adduint8(ctxt, s, 1); } loadinternal("runtime"); @@ -324,27 +191,36 @@ loadlib(void) loadinternal("math"); if(flag_race) loadinternal("runtime/race"); - if(linkmode == LinkExternal) { + + for(i=0; i<ctxt->libraryp; i++) { + if(debug['v'] > 1) + Bprint(&bso, "%5.2f autolib: %s (from %s)\n", cputime(), ctxt->library[i].file, ctxt->library[i].objref); + iscgo |= strcmp(ctxt->library[i].pkg, "runtime/cgo") == 0; + objfile(ctxt->library[i].file, ctxt->library[i].pkg); + } + + if(linkmode == LinkExternal && !iscgo) { // 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"); + if(i < ctxt->libraryp) + objfile(ctxt->library[i].file, ctxt->library[i].pkg); + // 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 = linklookup(ctxt, "go.importpath.runtime/cgo.", 0); s->type = SDATA; s->dupok = 1; s->reachable = 1; - } - for(i=0; i<libraryp; i++) { - if(debug['v'] > 1) - Bprint(&bso, "%5.2f autolib: %s (from %s)\n", cputime(), library[i].file, library[i].objref); - iscgo |= strcmp(library[i].pkg, "runtime/cgo") == 0; - objfile(library[i].file, library[i].pkg); + // Provided by the code that imports the package. + // Since we are simulating the import, we have to provide this string. + cgostrsym = "go.string.\"runtime/cgo\""; + if(linkrlookup(ctxt, cgostrsym, 0) == nil) + addstrdata(cgostrsym, "runtime/cgo"); } - + if(linkmode == LinkAuto) { if(iscgo && externalobj) linkmode = LinkExternal; @@ -355,7 +231,7 @@ loadlib(void) 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) + for(s = ctxt->allsym; s != S; s = s->allsym) if(s->type == SHOSTOBJ) { // If a symbol was marked both // cgo_import_static and cgo_import_dynamic, @@ -368,14 +244,12 @@ loadlib(void) } } - gmsym = lookup("runtime.tlsgm", 0); + gmsym = linklookup(ctxt, "runtime.tlsgm", 0); gmsym->type = STLSBSS; gmsym->size = 2*PtrSize; gmsym->hide = 1; - if(linkmode == LinkExternal && iself && HEADTYPE != Hopenbsd) - gmsym->reachable = 1; - else - gmsym->reachable = 0; + gmsym->reachable = 1; + ctxt->gmsym = gmsym; // Now that we know the link mode, trim the dynexp list. x = CgoExportDynamic; @@ -402,7 +276,9 @@ loadlib(void) // // Exception: on OS X, programs such as Shark only work with dynamic // binaries, so leave it enabled on OS X (Mach-O) binaries. - if(!flag_shared && !havedynamic && HEADTYPE != Hdarwin) + // Also leave it enabled on Solaris which doesn't support + // statically linked binaries. + if(!flag_shared && !havedynamic && HEADTYPE != Hdarwin && HEADTYPE != Hsolaris) debug['d'] = 1; importcycles(); @@ -412,8 +288,8 @@ loadlib(void) * look for the next file in an archive. * adapted from libmach. */ -int -nextar(Biobuf *bp, int off, struct ar_hdr *a) +static vlong +nextar(Biobuf *bp, vlong off, struct ar_hdr *a) { int r; int32 arsize; @@ -443,7 +319,7 @@ nextar(Biobuf *bp, int off, struct ar_hdr *a) void objfile(char *file, char *pkg) { - int32 off, l; + vlong off, l; Biobuf *f; char magbuf[SARMAG]; char pname[150]; @@ -470,25 +346,24 @@ objfile(char *file, char *pkg) return; } - /* skip over __.GOSYMDEF */ + /* skip over optional __.GOSYMDEF and process __.PKGDEF */ off = Boffset(f); - if((l = nextar(f, off, &arhdr)) <= 0) { + l = nextar(f, off, &arhdr); + if(l <= 0) { diag("%s: short read on archive file symbol header", file); goto out; } - if(strncmp(arhdr.name, symname, strlen(symname))) { - diag("%s: first entry not symbol header", file); - goto out; - } - off += l; - - /* skip over (or process) __.PKGDEF */ - if((l = nextar(f, off, &arhdr)) <= 0) { - diag("%s: short read on archive file symbol header", file); - goto out; + if(strncmp(arhdr.name, symname, strlen(symname)) == 0) { + off += l; + l = nextar(f, off, &arhdr); + if(l <= 0) { + diag("%s: short read on archive file symbol header", file); + goto out; + } } + if(strncmp(arhdr.name, pkgname, strlen(pkgname))) { - diag("%s: second entry not package header", file); + diag("%s: cannot find package header", file); goto out; } off += l; @@ -539,7 +414,7 @@ dowrite(int fd, char *p, int n) while(n > 0) { m = write(fd, p, n); if(m <= 0) { - cursym = S; + ctxt->cursym = S; diag("write error: %r"); errorexit(); } @@ -628,7 +503,7 @@ hostobjs(void) h = &hostobj[i]; f = Bopen(h->file, OREAD); if(f == nil) { - cursym = S; + ctxt->cursym = S; diag("cannot reopen %s: %r", h->pn); errorexit(); } @@ -697,7 +572,7 @@ hostlink(void) p = strchr(p + 1, ' '); } - argv = malloc((13+nhostobj+nldflag+c)*sizeof argv[0]); + argv = malloc((14+nhostobj+nldflag+c)*sizeof argv[0]); argc = 0; if(extld == nil) extld = "gcc"; @@ -720,6 +595,8 @@ hostlink(void) } if(HEADTYPE == Hdarwin) argv[argc++] = "-Wl,-no_pie,-pagezero_size,4000000"; + if(HEADTYPE == Hopenbsd) + argv[argc++] = "-Wl,-nopie"; if(iself && AssumeGoldLinker) argv[argc++] = "-Wl,--rosegment"; @@ -738,13 +615,16 @@ hostlink(void) if(iself) argv[argc++] = "-rdynamic"; + if(strstr(argv[0], "clang") != nil) + argv[argc++] = "-Qunused-arguments"; + // 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; + ctxt->cursym = S; diag("cannot reopen %s: %r", h->pn); errorexit(); } @@ -753,7 +633,7 @@ hostlink(void) argv[argc++] = p; w = create(p, 1, 0775); if(w < 0) { - cursym = S; + ctxt->cursym = S; diag("cannot create %s: %r", p); errorexit(); } @@ -765,7 +645,7 @@ hostlink(void) len -= n; } if(close(w) < 0) { - cursym = S; + ctxt->cursym = S; diag("cannot write %s: %r", p); errorexit(); } @@ -783,6 +663,20 @@ hostlink(void) if(*p == '\0') break; argv[argc++] = p; + + // clang, unlike GCC, passes -rdynamic to the linker + // even when linking with -static, causing a linker + // error when using GNU ld. So take out -rdynamic if + // we added it. We do it in this order, rather than + // only adding -rdynamic later, so that -extldflags + // can override -rdynamic without using -static. + if(iself && strncmp(p, "-static", 7) == 0 && (p[7]==' ' || p[7]=='\0')) { + for(i=0; i<argc; i++) { + if(strcmp(argv[i], "-rdynamic") == 0) + argv[i] = "-static"; + } + } + p = strchr(p + 1, ' '); } @@ -798,7 +692,7 @@ hostlink(void) } if(runcmd(argv) < 0) { - cursym = S; + ctxt->cursym = S; diag("%s: running %s failed: %r", argv0, argv[0]); errorexit(); } @@ -866,8 +760,8 @@ ldobj(Biobuf *f, char *pkg, int64 len, char *pn, char *file, int whence) return; } - // First, check that the basic goos, string, and version match. - t = smprint("%s %s %s ", goos, thestring, getgoversion()); + // First, check that the basic goos, goarch, and version match. + t = smprint("%s %s %s ", goos, getgoarch(), getgoversion()); line[n] = ' '; if(strncmp(line+10, t, strlen(t)) != 0 && !debug['f']) { line[n] = '\0'; @@ -913,7 +807,7 @@ ldobj(Biobuf *f, char *pkg, int64 len, char *pn, char *file, int whence) ldpkg(f, pkg, import1 - import0 - 2, pn, whence); // -2 for !\n Bseek(f, import1, 0); - ldobj1(f, pkg, eof - Boffset(f), pn); + ldobjfile(ctxt, f, pkg, eof - Boffset(f), pn); free(pn); return; @@ -922,318 +816,12 @@ eof: free(pn); } -Sym* -newsym(char *symb, int v) -{ - Sym *s; - int l; - - l = strlen(symb) + 1; - s = mal(sizeof(*s)); - if(debug['v'] > 1) - Bprint(&bso, "newsym %s\n", symb); - - s->dynid = -1; - s->plt = -1; - s->got = -1; - s->name = mal(l + 1); - memmove(s->name, symb, l); - - s->type = 0; - s->version = v; - s->value = 0; - s->sig = 0; - s->size = 0; - nsymbol++; - - s->allsym = allsym; - allsym = s; - - return s; -} - -static Sym* -_lookup(char *symb, int v, int creat) -{ - Sym *s; - char *p; - uint32 h; - int c; - - h = v; - for(p=symb; c = *p; p++) - h = h+h+h + c; - // 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(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; - - return s; -} - -Sym* -lookup(char *name, int v) -{ - return _lookup(name, v, 1); -} - -// read-only lookup -Sym* -rlookup(char *name, int v) -{ - return _lookup(name, v, 0); -} - -void -copyhistfrog(char *buf, int nbuf) -{ - char *p, *ep; - int i; - - p = buf; - ep = buf + nbuf; - for(i=0; i<histfrogp; i++) { - p = seprint(p, ep, "%s", histfrog[i]->name+1); - if(i+1<histfrogp && (p == buf || p[-1] != '/')) - p = seprint(p, ep, "/"); - } -} - -void -addhist(int32 line, int type) -{ - Auto *u; - Sym *s; - int i, j, k; - - u = mal(sizeof(Auto)); - s = mal(sizeof(Sym)); - s->name = mal(2*(histfrogp+1) + 1); - - u->asym = s; - u->type = type; - u->aoffset = line; - u->link = curhist; - curhist = u; - - s->name[0] = 0; - j = 1; - for(i=0; i<histfrogp; i++) { - k = histfrog[i]->value; - s->name[j+0] = k>>8; - s->name[j+1] = k; - j += 2; - } - s->name[j] = 0; - s->name[j+1] = 0; -} - -void -histtoauto(void) -{ - Auto *l; - - while(l = curhist) { - curhist = l->link; - l->link = curauto; - curauto = l; - } -} - -void -collapsefrog(Sym *s) -{ - int i; - - /* - * bad encoding of path components only allows - * MAXHIST components. if there is an overflow, - * first try to collapse xxx/.. - */ - for(i=1; i<histfrogp; i++) - if(strcmp(histfrog[i]->name+1, "..") == 0) { - memmove(histfrog+i-1, histfrog+i+1, - (histfrogp-i-1)*sizeof(histfrog[0])); - histfrogp--; - goto out; - } - - /* - * next try to collapse . - */ - for(i=0; i<histfrogp; i++) - if(strcmp(histfrog[i]->name+1, ".") == 0) { - memmove(histfrog+i, histfrog+i+1, - (histfrogp-i-1)*sizeof(histfrog[0])); - goto out; - } - - /* - * last chance, just truncate from front - */ - memmove(histfrog+0, histfrog+1, - (histfrogp-1)*sizeof(histfrog[0])); - -out: - histfrog[histfrogp-1] = s; -} - -void -nuxiinit(void) -{ - int i, c; - - for(i=0; i<4; i++) { - c = find1(0x04030201L, i+1); - if(i < 2) - inuxi2[i] = c; - if(i < 1) - inuxi1[i] = c; - inuxi4[i] = c; - if(c == i) { - inuxi8[i] = c; - inuxi8[i+4] = c+4; - } else { - inuxi8[i] = c+4; - inuxi8[i+4] = c; - } - fnuxi4[i] = c; - fnuxi8[i] = c; - fnuxi8[i+4] = c+4; - } - if(debug['v']) { - Bprint(&bso, "inuxi = "); - for(i=0; i<1; i++) - Bprint(&bso, "%d", inuxi1[i]); - Bprint(&bso, " "); - for(i=0; i<2; i++) - Bprint(&bso, "%d", inuxi2[i]); - Bprint(&bso, " "); - for(i=0; i<4; i++) - Bprint(&bso, "%d", inuxi4[i]); - Bprint(&bso, " "); - for(i=0; i<8; i++) - Bprint(&bso, "%d", inuxi8[i]); - Bprint(&bso, "\nfnuxi = "); - for(i=0; i<4; i++) - Bprint(&bso, "%d", fnuxi4[i]); - Bprint(&bso, " "); - for(i=0; i<8; i++) - Bprint(&bso, "%d", fnuxi8[i]); - Bprint(&bso, "\n"); - } - Bflush(&bso); -} - -int -find1(int32 l, int c) -{ - char *p; - int i; - - p = (char*)&l; - for(i=0; i<4; i++) - if(*p++ == c) - return i; - return 0; -} - -int -find2(int32 l, int c) -{ - union { - int32 l; - short p[2]; - } u; - short *p; - int i; - - u.l = l; - p = u.p; - for(i=0; i<4; i+=2) { - if(((*p >> 8) & 0xff) == c) - return i; - if((*p++ & 0xff) == c) - return i+1; - } - return 0; -} - -int32 -ieeedtof(Ieee *e) -{ - int exp; - int32 v; - - if(e->h == 0) - return 0; - exp = (e->h>>20) & ((1L<<11)-1L); - exp -= (1L<<10) - 2L; - v = (e->h & 0xfffffL) << 3; - v |= (e->l >> 29) & 0x7L; - if((e->l >> 28) & 1) { - v++; - if(v & 0x800000L) { - v = (v & 0x7fffffL) >> 1; - exp++; - } - } - if(-148 <= exp && exp <= -126) { - v |= 1<<23; - v >>= -125 - exp; - exp = -126; - } - else if(exp < -148 || exp >= 130) - diag("double fp to single fp overflow: %.17g", ieeedtod(e)); - v |= ((exp + 126) & 0xffL) << 23; - v |= e->h & 0x80000000L; - return v; -} - -double -ieeedtod(Ieee *ieeep) -{ - Ieee e; - double fr; - int exp; - - if(ieeep->h & (1L<<31)) { - e.h = ieeep->h & ~(1L<<31); - e.l = ieeep->l; - return -ieeedtod(&e); - } - if(ieeep->l == 0 && ieeep->h == 0) - return 0; - exp = (ieeep->h>>20) & ((1L<<11)-1L); - exp -= (1L<<10) - 2L; - fr = ieeep->l & ((1L<<16)-1L); - fr /= 1L<<16; - fr += (ieeep->l>>16) & ((1L<<16)-1L); - fr /= 1L<<16; - if(exp == -(1L<<10) - 2L) { - fr += (ieeep->h & (1L<<20)-1L); - exp++; - } else - fr += (ieeep->h & (1L<<20)-1L) | (1L<<20); - fr /= 1L<<21; - return ldexp(fr, exp); -} - void zerosig(char *sp) { - Sym *s; + LSym *s; - s = lookup(sp, 0); + s = linklookup(ctxt, sp, 0); s->sig = 0; } @@ -1242,7 +830,10 @@ mywhatsys(void) { goroot = getgoroot(); goos = getgoos(); - goarch = thestring; // ignore $GOARCH - we know who we are + goarch = getgoarch(); + + if(strncmp(goarch, thestring, strlen(thestring)) != 0) + sysfatal("cannot use %cc with GOARCH=%s", thechar, goarch); } int @@ -1303,7 +894,8 @@ unmal(void *v, uint32 n) * Invalid bytes turn into %xx. Right now the only bytes that need * escaping are %, ., and ", but we escape all control characters too. * - * Must be same as ../gc/subr.c:/^pathtoprefix. + * If you edit this, edit ../gc/subr.c:/^pathtoprefix too. + * If you edit this, edit ../../pkg/debug/goobj/read.go:/importPathToPrefix too. */ static char* pathtoprefix(char *s) @@ -1357,13 +949,6 @@ iconv(Fmt *fp) return 0; } -void -mangle(char *file) -{ - fprint(2, "%s: mangled input file\n", file); - errorexit(); -} - Section* addsection(Segment *seg, char *name, int rwx) { @@ -1381,235 +966,6 @@ addsection(Segment *seg, char *name, int rwx) return sect; } -void -addvarint(Sym *s, uint32 val) -{ - int32 n; - uint32 v; - uchar *p; - - n = 0; - for(v = val; v >= 0x80; v >>= 7) - n++; - n++; - - symgrow(s, s->np+n); - - p = s->p + s->np - n; - for(v = val; v >= 0x80; v >>= 7) - *p++ = v | 0x80; - *p = v; -} - -// funcpctab appends to dst a pc-value table mapping the code in func to the values -// returned by valfunc parameterized by arg. The invocation of valfunc to update the -// current value is, for each p, -// -// val = valfunc(func, val, p, 0, arg); -// record val as value at p->pc; -// val = valfunc(func, val, p, 1, arg); -// -// where func is the function, val is the current value, p is the instruction being -// considered, and arg can be used to further parameterize valfunc. -void -funcpctab(Sym *dst, Sym *func, char *desc, int32 (*valfunc)(Sym*, int32, Prog*, int32, int32), int32 arg) -{ - int dbg, i, start; - int32 oldval, val, started; - uint32 delta; - vlong pc; - Prog *p; - - // To debug a specific function, uncomment second line and change name. - dbg = 0; - //dbg = strcmp(func->name, "main.main") == 0; - - debug['O'] += dbg; - - start = dst->np; - - if(debug['O']) - Bprint(&bso, "funcpctab %s -> %s [valfunc=%s]\n", func->name, dst->name, desc); - - val = -1; - oldval = val; - pc = func->value; - - if(debug['O']) - Bprint(&bso, "%6llux %6d %P\n", pc, val, func->text); - - started = 0; - for(p=func->text; p != P; p = p->link) { - // Update val. If it's not changing, keep going. - val = valfunc(func, val, p, 0, arg); - if(val == oldval && started) { - val = valfunc(func, val, p, 1, arg); - if(debug['O']) - Bprint(&bso, "%6llux %6s %P\n", (vlong)p->pc, "", p); - continue; - } - - // If the pc of the next instruction is the same as the - // pc of this instruction, this instruction is not a real - // instruction. Keep going, so that we only emit a delta - // for a true instruction boundary in the program. - if(p->link && p->link->pc == p->pc) { - val = valfunc(func, val, p, 1, arg); - if(debug['O']) - Bprint(&bso, "%6llux %6s %P\n", (vlong)p->pc, "", p); - continue; - } - - // The table is a sequence of (value, pc) pairs, where each - // pair states that the given value is in effect from the current position - // up to the given pc, which becomes the new current position. - // To generate the table as we scan over the program instructions, - // we emit a "(value" when pc == func->value, and then - // each time we observe a change in value we emit ", pc) (value". - // When the scan is over, we emit the closing ", pc)". - // - // The table is delta-encoded. The value deltas are signed and - // transmitted in zig-zag form, where a complement bit is placed in bit 0, - // and the pc deltas are unsigned. Both kinds of deltas are sent - // as variable-length little-endian base-128 integers, - // where the 0x80 bit indicates that the integer continues. - - if(debug['O']) - Bprint(&bso, "%6llux %6d %P\n", (vlong)p->pc, val, p); - - if(started) { - addvarint(dst, (p->pc - pc) / MINLC); - pc = p->pc; - } - delta = val - oldval; - if(delta>>31) - delta = 1 | ~(delta<<1); - else - delta <<= 1; - addvarint(dst, delta); - oldval = val; - started = 1; - val = valfunc(func, val, p, 1, arg); - } - - if(started) { - if(debug['O']) - Bprint(&bso, "%6llux done\n", (vlong)func->value+func->size); - addvarint(dst, (func->value+func->size - pc) / MINLC); - addvarint(dst, 0); // terminator - } - - if(debug['O']) { - Bprint(&bso, "wrote %d bytes\n", dst->np - start); - for(i=start; i<dst->np; i++) - Bprint(&bso, " %02ux", dst->p[i]); - Bprint(&bso, "\n"); - } - - debug['O'] -= dbg; -} - -// pctofileline computes either the file number (arg == 0) -// or the line number (arg == 1) to use at p. -// Because p->lineno applies to p, phase == 0 (before p) -// takes care of the update. -static int32 -pctofileline(Sym *sym, int32 oldval, Prog *p, int32 phase, int32 arg) -{ - int32 f, l; - - if(p->as == ATEXT || p->as == ANOP || p->as == AUSEFIELD || p->line == 0 || phase == 1) - return oldval; - getline(sym->hist, p->line, &f, &l); - if(f == 0) { - // print("getline failed for %s %P\n", cursym->name, p); - return oldval; - } - if(arg == 0) - return f; - return l; -} - -// pctospadj computes the sp adjustment in effect. -// It is oldval plus any adjustment made by p itself. -// The adjustment by p takes effect only after p, so we -// apply the change during phase == 1. -static int32 -pctospadj(Sym *sym, int32 oldval, Prog *p, int32 phase, int32 arg) -{ - USED(arg); - USED(sym); - - if(oldval == -1) // starting - oldval = 0; - if(phase == 0) - return oldval; - if(oldval + p->spadj < -10000 || oldval + p->spadj > 1100000000) { - diag("overflow in spadj: %d + %d = %d", oldval, p->spadj, oldval + p->spadj); - errorexit(); - } - return oldval + p->spadj; -} - -// pctopcdata computes the pcdata value in effect at p. -// A PCDATA instruction sets the value in effect at future -// non-PCDATA instructions. -// Since PCDATA instructions have no width in the final code, -// it does not matter which phase we use for the update. -static int32 -pctopcdata(Sym *sym, int32 oldval, Prog *p, int32 phase, int32 arg) -{ - USED(sym); - - if(phase == 0 || p->as != APCDATA || p->from.offset != arg) - return oldval; - if((int32)p->to.offset != p->to.offset) { - diag("overflow in PCDATA instruction: %P", p); - errorexit(); - } - return p->to.offset; -} - -#define LOG 5 -void -mkfwd(void) -{ - Prog *p; - int i; - int32 dwn[LOG], cnt[LOG]; - Prog *lst[LOG]; - - for(i=0; i<LOG; i++) { - if(i == 0) - cnt[i] = 1; - else - cnt[i] = LOG * cnt[i-1]; - dwn[i] = 1; - lst[i] = P; - } - i = 0; - for(cursym = textp; cursym != nil; cursym = cursym->next) { - for(p = cursym->text; p != P; p = p->link) { - if(p->link == P) { - if(cursym->next) - p->forwd = cursym->next->text; - break; - } - i--; - if(i < 0) - i = LOG-1; - p->forwd = P; - dwn[i]--; - if(dwn[i] <= 0) { - dwn[i] = cnt[i]; - if(lst[i] != P) - lst[i]->forwd = p; - lst[i] = p; - } - } - } -} - uint16 le16(uchar *b) { @@ -1652,7 +1008,7 @@ Endian le = { le16, le32, le64 }; typedef struct Chain Chain; struct Chain { - Sym *sym; + LSym *sym; Chain *up; int limit; // limit on entry to sym }; @@ -1660,67 +1016,87 @@ struct Chain static int stkcheck(Chain*, int); static void stkprint(Chain*, int); static void stkbroke(Chain*, int); -static Sym *morestack; -static Sym *newstack; +static LSym *morestack; +static LSym *newstack; enum { HasLinkRegister = (thechar == '5'), - CallSize = (!HasLinkRegister)*PtrSize, // bytes of stack required for a call }; +// TODO: Record enough information in new object files to +// allow stack checks here. + +static int +callsize(void) +{ + if(thechar == '5') + return 0; + return RegSize; +} + void dostkcheck(void) { Chain ch; - Sym *s; + LSym *s; - morestack = lookup("runtime.morestack", 0); - newstack = lookup("runtime.newstack", 0); - - // First the nosplits on their own. - for(s = textp; s != nil; s = s->next) { - if(s->text == nil || s->text->link == nil || (s->text->textflag & NOSPLIT) == 0) + morestack = linklookup(ctxt, "runtime.morestack", 0); + newstack = linklookup(ctxt, "runtime.newstack", 0); + + // Every splitting function ensures that there are at least StackLimit + // bytes available below SP when the splitting prologue finishes. + // If the splitting function calls F, then F begins execution with + // at least StackLimit - callsize() bytes available. + // Check that every function behaves correctly with this amount + // of stack, following direct calls in order to piece together chains + // of non-splitting functions. + ch.up = nil; + ch.limit = StackLimit - callsize(); + + // Check every function, but do the nosplit functions in a first pass, + // to make the printed failure chains as short as possible. + for(s = ctxt->textp; s != nil; s = s->next) { + // runtime.racesymbolizethunk is called from gcc-compiled C + // code running on the operating system thread stack. + // It uses more than the usual amount of stack but that's okay. + if(strcmp(s->name, "runtime.racesymbolizethunk") == 0) continue; - cursym = s; - ch.up = nil; + + if(s->nosplit) { + ctxt->cursym = s; ch.sym = s; - ch.limit = StackLimit - CallSize; stkcheck(&ch, 0); - s->stkcheck = 1; } - - // Check calling contexts. - // Some nosplits get called a little further down, - // like newproc and deferproc. We could hard-code - // that knowledge but it's more robust to look at - // the actual call sites. - for(s = textp; s != nil; s = s->next) { - if(s->text == nil || s->text->link == nil || (s->text->textflag & NOSPLIT) != 0) - continue; - cursym = s; - ch.up = nil; + } + for(s = ctxt->textp; s != nil; s = s->next) { + if(!s->nosplit) { + ctxt->cursym = s; ch.sym = s; - ch.limit = StackLimit - CallSize; stkcheck(&ch, 0); } } +} static int stkcheck(Chain *up, int depth) { Chain ch, ch1; - Prog *p; - Sym *s; - int limit, prolog; + LSym *s; + int limit; + Reloc *r, *endr; + Pciter pcsp; limit = up->limit; s = up->sym; - p = s->text; - // Small optimization: don't repeat work at top. - if(s->stkcheck && limit == StackLimit-CallSize) + // Don't duplicate work: only need to consider each + // function at top of safe zone once. + if(limit == StackLimit-callsize()) { + if(s->stkcheck) return 0; + s->stkcheck = 1; + } if(depth > 100) { diag("nosplit stack check too deep"); @@ -1728,11 +1104,11 @@ stkcheck(Chain *up, int depth) return -1; } - if(p == nil || p->link == nil) { + if(s->external || s->pcln == nil) { // external function. // should never be called directly. // only diagnose the direct caller. - if(depth == 1) + if(depth == 1 && s->type != SXREF) diag("call to external function %s", s->name); return -1; } @@ -1748,50 +1124,56 @@ stkcheck(Chain *up, int depth) return 0; ch.up = up; - prolog = (s->text->textflag & NOSPLIT) == 0; - for(p = s->text; p != P; p = p->link) { - limit -= p->spadj; - if(prolog && p->spadj != 0) { - // The first stack adjustment in a function with a - // split-checking prologue marks the end of the - // prologue. Assuming the split check is correct, - // after the adjustment there should still be at least - // StackLimit bytes available below the stack pointer. - // If this is not the top call in the chain, no need - // to duplicate effort, so just stop. - if(depth > 0) - return 0; - prolog = 0; - limit = StackLimit; - } - if(limit < 0) { - stkbroke(up, limit); + + // Walk through sp adjustments in function, consuming relocs. + r = s->r; + endr = r + s->nr; + for(pciterinit(ctxt, &pcsp, &s->pcln->pcsp); !pcsp.done; pciternext(&pcsp)) { + // pcsp.value is in effect for [pcsp.pc, pcsp.nextpc). + + // Check stack size in effect for this span. + if(limit - pcsp.value < 0) { + stkbroke(up, limit - pcsp.value); return -1; } - if(iscall(p)) { - limit -= CallSize; - ch.limit = limit; - if(p->to.type == D_BRANCH) { + + // Process calls in this span. + for(; r < endr && r->off < pcsp.nextpc; r++) { + switch(r->type) { + case R_CALL: + case R_CALLARM: // Direct call. - ch.sym = p->to.sym; + ch.limit = limit - pcsp.value - callsize(); + ch.sym = r->sym; if(stkcheck(&ch, depth+1) < 0) return -1; - } else { - // Indirect call. Assume it is a splitting function, + + // If this is a call to morestack, we've just raised our limit back + // to StackLimit beyond the frame size. + if(strncmp(r->sym->name, "runtime.morestack", 17) == 0) { + limit = StackLimit + s->locals; + if(thechar == '5') + limit += 4; // saved LR + } + break; + + case R_CALLIND: + // Indirect call. Assume it is a call to a splitting function, // so we have to make sure it can call morestack. - limit -= CallSize; + // Arrange the data structures to report both calls, so that + // if there is an error, stkprint shows all the steps involved. + ch.limit = limit - pcsp.value - callsize(); ch.sym = nil; - ch1.limit = limit; + ch1.limit = ch.limit - callsize(); // for morestack in called prologue ch1.up = &ch; ch1.sym = morestack; if(stkcheck(&ch1, depth+2) < 0) return -1; - limit += CallSize; + break; } - limit += CallSize; + } } - } return 0; } @@ -1814,7 +1196,7 @@ stkprint(Chain *ch, int limit) if(ch->up == nil) { // top of chain. ch->sym != nil. - if(ch->sym->text->textflag & NOSPLIT) + if(ch->sym->nosplit) print("\t%d\tassumed on entry to %s\n", ch->limit, name); else print("\t%d\tguaranteed after split check in %s\n", ch->limit, name); @@ -1828,52 +1210,14 @@ stkprint(Chain *ch, int limit) } int -headtype(char *name) -{ - int i; - - for(i=0; headers[i].name; i++) - if(strcmp(name, headers[i].name) == 0) { - headstring = headers[i].name; - return headers[i].val; - } - fprint(2, "unknown header type -H %s\n", name); - errorexit(); - return -1; // not reached -} - -char* -headstr(int v) -{ - static char buf[20]; - int i; - - for(i=0; headers[i].name; i++) - if(v == headers[i].val) - return headers[i].name; - snprint(buf, sizeof buf, "%d", v); - return buf; -} - -void -undef(void) -{ - Sym *s; - - for(s = allsym; s != S; s = s->allsym) - if(s->type == SXREF) - diag("%s(%d): not defined", s->name, s->version); -} - -int Yconv(Fmt *fp) { - Sym *s; + LSym *s; Fmt fmt; int i; char *str; - s = va_arg(fp->args, Sym*); + s = va_arg(fp->args, LSym*); if (s == S) { fmtprint(fp, "<nil>"); } else { @@ -1967,6 +1311,14 @@ usage(void) void setheadtype(char *s) { + int h; + + h = headtype(s); + if(h < 0) { + fprint(2, "unknown header type -H %s\n", s); + errorexit(); + } + headstring = s; HEADTYPE = headtype(s); } @@ -1985,22 +1337,22 @@ doversion(void) } void -genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)) +genasmsym(void (*put)(LSym*, char*, int, vlong, vlong, int, LSym*)) { Auto *a; - Sym *s; + LSym *s; int32 off; // These symbols won't show up in the first loop below because we // skip STEXT symbols. Normal STEXT symbols are emitted by walking textp. - s = lookup("text", 0); + s = linklookup(ctxt, "text", 0); if(s->type == STEXT) put(s, s->name, 'T', s->value, s->size, s->version, 0); - s = lookup("etext", 0); + s = linklookup(ctxt, "etext", 0); if(s->type == STEXT) put(s, s->name, 'T', s->value, s->size, s->version, 0); - for(s=allsym; s!=S; s=s->allsym) { + for(s=ctxt->allsym; s!=S; s=s->allsym) { if(s->hide || (s->name[0] == '.' && s->version == 0 && strcmp(s->name, ".rathole") != 0)) continue; switch(s->type&SMASK) { @@ -2036,20 +1388,20 @@ genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)) } } - for(s = textp; s != nil; s = s->next) { - if(s->text == nil) - continue; - + for(s = ctxt->textp; s != nil; s = s->next) { put(s, s->name, 'T', s->value, s->size, s->version, s->gotype); + // NOTE(ality): acid can't produce a stack trace without .frame symbols + put(nil, ".frame", 'm', s->locals+PtrSize, 0, 0, 0); + for(a=s->autom; a; a=a->link) { // Emit a or p according to actual offset, even if label is wrong. // This avoids negative offsets, which cannot be encoded. - if(a->type != D_AUTO && a->type != D_PARAM) + if(a->type != A_AUTO && a->type != A_PARAM) continue; // compute offset relative to FP - if(a->type == D_PARAM) + if(a->type == A_PARAM) off = a->aoffset; else off = a->aoffset - PtrSize; @@ -2075,461 +1427,126 @@ genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)) 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) +vlong +symaddr(LSym *s) { - p = realloc(p, n); - if(p == nil) { - cursym = S; - diag("out of memory"); - errorexit(); - } - return p; + if(!s->reachable) + diag("unreachable symbol in symaddr - %s", s->name); + return s->value; } -// Saved history stacks encountered while reading archives. -// Keeping them allows us to answer virtual lineno -> file:line -// queries. -// -// The history stack is a complex data structure, described best at the -// bottom of http://plan9.bell-labs.com/magic/man2html/6/a.out. -// One of the key benefits of interpreting it here is that the runtime -// does not have to. Perhaps some day the compilers could generate -// a simpler linker input too. - -struct Hist -{ - int32 line; - int32 off; - Sym *file; -}; - -static Hist *histcopy; -static Hist *hist; -static int32 nhist; -static int32 maxhist; -static int32 histdepth; -static int32 nhistfile; -static Sym *filesyms; - -// savehist processes a single line, off history directive -// found in the input object file. void -savehist(int32 line, int32 off) +xdefine(char *p, int t, vlong v) { - char tmp[1024]; - Sym *file; - Hist *h; - - // NOTE(rsc): We used to do the copyhistfrog first and this - // condition was if(tmp[0] != '\0') to check for an empty string, - // implying that histfrogp == 0, implying that this is a history pop. - // However, on Windows in the misc/cgo test, the linker is - // presented with an ANAME corresponding to an empty string, - // that ANAME ends up being the only histfrog, and thus we have - // a situation where histfrogp > 0 (not a pop) but the path we find - // is the empty string. Really that shouldn't happen, but it doesn't - // seem to be bothering anyone yet, and it's easier to fix the condition - // to test histfrogp than to track down where that empty string is - // coming from. Probably it is coming from go tool pack's P command. - if(histfrogp > 0) { - tmp[0] = '\0'; - copyhistfrog(tmp, sizeof tmp); - file = lookup(tmp, HistVersion); - if(file->type != SFILEPATH) { - file->value = ++nhistfile; - file->type = SFILEPATH; - file->next = filesyms; - filesyms = file; - } - } else - file = nil; + LSym *s; - if(file != nil && line == 1 && off == 0) { - // start of new stack - if(histdepth != 0) { - diag("history stack phase error: unexpected start of new stack depth=%d file=%s", histdepth, tmp); - errorexit(); - } - nhist = 0; - histcopy = nil; - } - - if(nhist >= maxhist) { - if(maxhist == 0) - maxhist = 1; - maxhist *= 2; - hist = erealloc(hist, maxhist*sizeof hist[0]); - } - h = &hist[nhist++]; - h->line = line; - h->off = off; - h->file = file; - - if(file != nil) { - if(off == 0) - histdepth++; - } else { - if(off != 0) { - diag("history stack phase error: bad offset in pop"); - errorexit(); - } - histdepth--; - } + s = linklookup(ctxt, p, 0); + s->type = t; + s->value = v; + s->reachable = 1; + s->special = 1; } -// gethist returns the history stack currently in effect. -// The result is valid indefinitely. -Hist* -gethist(void) +vlong +datoff(vlong addr) { - if(histcopy == nil) { - if(nhist == 0) - return nil; - histcopy = mal((nhist+1)*sizeof hist[0]); - memmove(histcopy, hist, nhist*sizeof hist[0]); - histcopy[nhist].line = -1; - } - return histcopy; + if(addr >= segdata.vaddr) + return addr - segdata.vaddr + segdata.fileoff; + if(addr >= segtext.vaddr) + return addr - segtext.vaddr + segtext.fileoff; + diag("datoff %#llx", addr); + return 0; } -typedef struct Hstack Hstack; -struct Hstack -{ - Hist *h; - int delta; -}; - -// getline sets *f to the file number and *l to the line number -// of the virtual line number line according to the history stack h. -void -getline(Hist *h, int32 line, int32 *f, int32 *l) +vlong +entryvalue(void) { - Hstack stk[100]; - int nstk, start; - Hist *top, *h0; - static Hist *lasth; - static int32 laststart, lastend, lastdelta, lastfile; - - h0 = h; - *f = 0; - *l = 0; - start = 0; - if(h == nil || line == 0) { - print("%s: getline: h=%p line=%d\n", cursym->name, h, line); - return; - } - - // Cache span used during last lookup, so that sequential - // translation of line numbers in compiled code is efficient. - if(!debug['O'] && lasth == h && laststart <= line && line < lastend) { - *f = lastfile; - *l = line - lastdelta; - return; - } - - if(debug['O']) - print("getline %d laststart=%d lastend=%d\n", line, laststart, lastend); - - nstk = 0; - for(; h->line != -1; h++) { - if(debug['O']) - print("\t%s %d %d\n", h->file ? h->file->name : "?", h->line, h->off); - - if(h->line > line) { - if(nstk == 0) { - diag("history stack phase error: empty stack at line %d", (int)line); - errorexit(); - } - top = stk[nstk-1].h; - lasth = h; - lastfile = top->file->value; - laststart = start; - lastend = h->line; - lastdelta = stk[nstk-1].delta; - *f = lastfile; - *l = line - lastdelta; - if(debug['O']) - print("\tgot %d %d [%d %d %d]\n", *f, *l, laststart, lastend, lastdelta); - return; - } - if(h->file == nil) { - // pop included file - if(nstk == 0) { - diag("history stack phase error: stack underflow"); - errorexit(); - } - nstk--; - if(nstk > 0) - stk[nstk-1].delta += h->line - stk[nstk].h->line; - start = h->line; - } else if(h->off == 0) { - // push included file - if(nstk >= nelem(stk)) { - diag("history stack phase error: stack overflow"); - errorexit(); - } - start = h->line; - stk[nstk].h = h; - stk[nstk].delta = h->line - 1; - nstk++; - } else { - // #line directive - if(nstk == 0) { - diag("history stack phase error: stack underflow"); - errorexit(); - } - stk[nstk-1].h = h; - stk[nstk-1].delta = h->line - h->off; - start = h->line; - } - if(debug['O']) - print("\t\tnstk=%d delta=%d\n", nstk, stk[nstk].delta); - } + char *a; + LSym *s; - diag("history stack phase error: cannot find line for %d", line); - nstk = 0; - for(h = h0; h->line != -1; h++) { - print("\t%d %d %s\n", h->line, h->off, h->file ? h->file->name : ""); - if(h->file == nil) - nstk--; - else if(h->off == 0) - nstk++; - } + a = INITENTRY; + if(*a >= '0' && *a <= '9') + return atolwhex(a); + s = linklookup(ctxt, a, 0); + if(s->type == 0) + return INITTEXT; + if(s->type != STEXT) + diag("entry not text: %s", s->name); + return s->value; } -// defgostring returns a symbol for the Go string containing text. -Sym* -defgostring(char *text) +static void +undefsym(LSym *s) { - char *p; - Sym *s; - int32 n; - - n = strlen(text); - p = smprint("go.string.\"%Z\"", text); - s = lookup(p, 0); - if(s->size == 0) { - s->type = SGOSTRING; - s->reachable = 1; - s->size = 2*PtrSize+n; - symgrow(s, 2*PtrSize+n); - setaddrplus(s, 0, s, 2*PtrSize); - setuintxx(s, PtrSize, n, PtrSize); - memmove(s->p+2*PtrSize, text, n); - } - s->reachable = 1; - return s; -} + int i; + Reloc *r; -// addpctab appends to f a pc-value table, storing its offset at off. -// The pc-value table is for func and reports the value of valfunc -// parameterized by arg. -static int32 -addpctab(Sym *f, int32 off, Sym *func, char *desc, int32 (*valfunc)(Sym*, int32, Prog*, int32, int32), int32 arg) -{ - int32 start; - - start = f->np; - funcpctab(f, func, desc, valfunc, arg); - if(start == f->np) { - // no table - return setuint32(f, off, 0); - } - if((int32)start > (int32)f->np) { - diag("overflow adding pc-table: symbol too large"); - errorexit(); + ctxt->cursym = s; + for(i=0; i<s->nr; i++) { + r = &s->r[i]; + if(r->sym == nil) // happens for some external ARM relocs + continue; + if(r->sym->type == Sxxx || r->sym->type == SXREF) + diag("undefined: %s", r->sym->name); + if(!r->sym->reachable) + diag("use of unreachable symbol: %s", r->sym->name); } - return setuint32(f, off, start); } -static int32 -ftabaddstring(Sym *ftab, char *s) +void +undef(void) { - int32 n, start; + LSym *s; - n = strlen(s)+1; - start = ftab->np; - symgrow(ftab, start+n+1); - strcpy((char*)ftab->p + start, s); - return start; + for(s = ctxt->textp; s != nil; s = s->next) + undefsym(s); + for(s = datap; s != nil; s = s->next) + undefsym(s); + if(nerrors > 0) + errorexit(); } -// pclntab initializes the pclntab symbol with -// runtime function and file name information. void -pclntab(void) +callgraph(void) { - Prog *p; - int32 i, n, nfunc, start, funcstart; - uint32 *havepc, *havefunc; - Sym *ftab, *s; - int32 npcdata, nfuncdata, off, end; - int64 funcdata_bytes; - - funcdata_bytes = 0; - ftab = lookup("pclntab", 0); - ftab->type = SPCLNTAB; - ftab->reachable = 1; - - // See golang.org/s/go12symtab for the format. Briefly: - // 8-byte header - // nfunc [PtrSize bytes] - // function table, alternating PC and offset to func struct [each entry PtrSize bytes] - // end PC [PtrSize bytes] - // offset to file table [4 bytes] - nfunc = 0; - for(cursym = textp; cursym != nil; cursym = cursym->next) - nfunc++; - symgrow(ftab, 8+PtrSize+nfunc*2*PtrSize+PtrSize+4); - setuint32(ftab, 0, 0xfffffffb); - setuint8(ftab, 6, MINLC); - setuint8(ftab, 7, PtrSize); - setuintxx(ftab, 8, nfunc, PtrSize); - - nfunc = 0; - for(cursym = textp; cursym != nil; cursym = cursym->next, nfunc++) { - funcstart = ftab->np; - funcstart += -ftab->np & (PtrSize-1); - - setaddr(ftab, 8+PtrSize+nfunc*2*PtrSize, cursym); - setuintxx(ftab, 8+PtrSize+nfunc*2*PtrSize+PtrSize, funcstart, PtrSize); - - npcdata = 0; - nfuncdata = 0; - for(p = cursym->text; p != P; p = p->link) { - if(p->as == APCDATA && p->from.offset >= npcdata) - npcdata = p->from.offset+1; - if(p->as == AFUNCDATA && p->from.offset >= nfuncdata) - nfuncdata = p->from.offset+1; - } - - // fixed size of struct, checked below - off = funcstart; - end = funcstart + PtrSize + 3*4 + 5*4 + npcdata*4 + nfuncdata*PtrSize; - if(nfuncdata > 0 && (end&(PtrSize-1))) - end += 4; - symgrow(ftab, end); - - // entry uintptr - off = setaddr(ftab, off, cursym); - - // name int32 - off = setuint32(ftab, off, ftabaddstring(ftab, cursym->name)); - - // args int32 - // TODO: Move into funcinfo. - if(cursym->text == nil) - off = setuint32(ftab, off, ArgsSizeUnknown); - else - off = setuint32(ftab, off, cursym->args); - - // frame int32 - // TODO: Remove entirely. The pcsp table is more precise. - // This is only used by a fallback case during stack walking - // when a called function doesn't have argument information. - // We need to make sure everything has argument information - // and then remove this. - if(cursym->text == nil) - off = setuint32(ftab, off, 0); - else - off = setuint32(ftab, off, (uint32)cursym->text->to.offset+PtrSize); - - // pcsp table (offset int32) - off = addpctab(ftab, off, cursym, "pctospadj", pctospadj, 0); - - // pcfile table (offset int32) - off = addpctab(ftab, off, cursym, "pctofileline file", pctofileline, 0); + LSym *s; + Reloc *r; + int i; - // pcln table (offset int32) - off = addpctab(ftab, off, cursym, "pctofileline line", pctofileline, 1); - - // npcdata int32 - off = setuint32(ftab, off, npcdata); - - // nfuncdata int32 - off = setuint32(ftab, off, nfuncdata); - - // tabulate which pc and func data we have. - n = ((npcdata+31)/32 + (nfuncdata+31)/32)*4; - havepc = mal(n); - havefunc = havepc + (npcdata+31)/32; - for(p = cursym->text; p != P; p = p->link) { - if(p->as == AFUNCDATA) { - if((havefunc[p->from.offset/32]>>(p->from.offset%32))&1) - diag("multiple definitions for FUNCDATA $%d", p->from.offset); - havefunc[p->from.offset/32] |= 1<<(p->from.offset%32); - } - if(p->as == APCDATA) - havepc[p->from.offset/32] |= 1<<(p->from.offset%32); - } + if(!debug['c']) + return; - // pcdata. - for(i=0; i<npcdata; i++) { - if(!(havepc[i/32]>>(i%32))&1) { - off = setuint32(ftab, off, 0); + for(s = ctxt->textp; s != nil; s = s->next) { + for(i=0; i<s->nr; i++) { + r = &s->r[i]; + if(r->sym == nil) continue; - } - off = addpctab(ftab, off, cursym, "pctopcdata", pctopcdata, i); - } - - unmal(havepc, n); - - // funcdata, must be pointer-aligned and we're only int32-aligned. - // Unlike pcdata, can gather in a single pass. - // Missing funcdata will be 0 (nil pointer). - if(nfuncdata > 0) { - if(off&(PtrSize-1)) - off += 4; - for(p = cursym->text; p != P; p = p->link) { - if(p->as == AFUNCDATA) { - i = p->from.offset; - if(p->to.type == D_CONST) - setuintxx(ftab, off+PtrSize*i, p->to.offset, PtrSize); - else { - // TODO: Dedup. - funcdata_bytes += p->to.sym->size; - setaddrplus(ftab, off+PtrSize*i, p->to.sym, p->to.offset); - } - } - } - off += nfuncdata*PtrSize; - } - - if(off != end) { - diag("bad math in functab: funcstart=%d off=%d but end=%d (npcdata=%d nfuncdata=%d)", funcstart, off, end, npcdata, nfuncdata); - errorexit(); + if((r->type == R_CALL || r->type == R_CALLARM) && r->sym->type == STEXT) + Bprint(&bso, "%s calls %s\n", s->name, r->sym->name); } - - // Final entry of table is just end pc. - if(cursym->next == nil) - setaddrplus(ftab, 8+PtrSize+(nfunc+1)*2*PtrSize, cursym, cursym->size); } - - // Start file table. - start = ftab->np; - start += -ftab->np & (PtrSize-1); - setuint32(ftab, 8+PtrSize+nfunc*2*PtrSize+PtrSize, start); +} - symgrow(ftab, start+(nhistfile+1)*4); - setuint32(ftab, start, nhistfile); - for(s = filesyms; s != S; s = s->next) - setuint32(ftab, start + s->value*4, ftabaddstring(ftab, s->name)); +void +diag(char *fmt, ...) +{ + char buf[1024], *tn, *sep; + va_list arg; - ftab->size = ftab->np; - - if(debug['v']) - Bprint(&bso, "%5.2f pclntab=%lld bytes, funcdata total %lld bytes\n", cputime(), (vlong)ftab->size, (vlong)funcdata_bytes); -} + tn = ""; + sep = ""; + if(ctxt->cursym != S) { + tn = ctxt->cursym->name; + sep = ": "; + } + va_start(arg, fmt); + vseprint(buf, buf+sizeof(buf), fmt, arg); + va_end(arg); + print("%s%s%s\n", tn, sep, buf); + + nerrors++; + if(nerrors > 20) { + print("too many errors\n"); + errorexit(); + } +} |