diff options
| author | Russ Cox <rsc@golang.org> | 2015-02-28 13:48:09 -0500 |
|---|---|---|
| committer | Russ Cox <rsc@golang.org> | 2015-03-01 00:40:11 +0000 |
| commit | dca5f2e9b3212ef6df7e08ebd818b804ad27db91 (patch) | |
| tree | f7ef83263b2b01b7e1e47aa9e120e22e66db7f7f /src/cmd/ld/lib.c | |
| parent | 100d64b9206fb1e73c32ea513cedb31320e9c17e (diff) | |
| download | go-dca5f2e9b3212ef6df7e08ebd818b804ad27db91.tar.xz | |
cmd/5l etc: replace C code with Go code
mv cmd/new5l cmd/5l and so on.
Minimal changes to cmd/dist and cmd/go to keep things building.
More can be deleted in followup CLs.
Change-Id: I1449eca7654ce2580d1f413a56dc4a75f3d4618b
Reviewed-on: https://go-review.googlesource.com/6361
Reviewed-by: Rob Pike <r@golang.org>
Diffstat (limited to 'src/cmd/ld/lib.c')
| -rw-r--r-- | src/cmd/ld/lib.c | 1636 |
1 files changed, 0 insertions, 1636 deletions
diff --git a/src/cmd/ld/lib.c b/src/cmd/ld/lib.c deleted file mode 100644 index 612c755d5b..0000000000 --- a/src/cmd/ld/lib.c +++ /dev/null @@ -1,1636 +0,0 @@ -// Derived from Inferno utils/6l/obj.c and utils/6l/span.c -// http://code.google.com/p/inferno-os/source/browse/utils/6l/obj.c -// http://code.google.com/p/inferno-os/source/browse/utils/6l/span.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#include <u.h> -#include <libc.h> -#include <bio.h> -#include <link.h> -#include "lib.h" -#include "elf.h" -#include "dwarf.h" -#include "../../runtime/stack.h" -#include "../../runtime/funcdata.h" - -#include <ar.h> -#if !(defined(_WIN32) || defined(PLAN9)) -#include <sys/stat.h> -#endif - -enum -{ - // Whether to assume that the external linker is "gold" - // (http://sourceware.org/ml/binutils/2008-03/msg00162.html). - AssumeGoldLinker = 0, -}; - -int iconv(Fmt*); - -char symname[] = "__.GOSYMDEF"; -char pkgname[] = "__.PKGDEF"; -static int cout = -1; - -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. -static int externalobj = 0; - -static void hostlinksetup(void); - -char* goroot; -char* goarch; -char* goos; -char* theline; - -void -Lflag(char *arg) -{ - char **p; - - if(ctxt->nlibdir >= ctxt->maxlibdir) { - if (ctxt->maxlibdir == 0) - ctxt->maxlibdir = 8; - else - ctxt->maxlibdir *= 2; - p = erealloc(ctxt->libdir, ctxt->maxlibdir * sizeof(*p)); - ctxt->libdir = p; - } - 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 -libinit(void) -{ - char *suffix, *suffixsep; - - funcalign = thearch.funcalign; - fmtinstall('i', iconv); - fmtinstall('Y', Yconv); - fmtinstall('Z', Zconv); - mywhatsys(); // get goroot, goarch, goos - - // add goroot to the end of the libdir list. - suffix = ""; - suffixsep = ""; - if(flag_installsuffix != nil) { - suffixsep = "_"; - suffix = flag_installsuffix; - } else if(flag_race) { - suffixsep = "_"; - suffix = "race"; - } - Lflag(smprint("%s/pkg/%s_%s%s%s", goroot, goos, goarch, suffixsep, suffix)); - - mayberemoveoutfile(); - cout = create(outfile, 1, 0775); - if(cout < 0) { - diag("cannot create %s: %r", outfile); - errorexit(); - } - Binit(&coutbuf, cout, OWRITE); - - if(INITENTRY == nil) { - INITENTRY = mal(strlen(goarch)+strlen(goos)+20); - if(!flag_shared) { - sprint(INITENTRY, "_rt0_%s_%s", goarch, goos); - } else { - sprint(INITENTRY, "_rt0_%s_%s_lib", goarch, goos); - } - } - linklookup(ctxt, INITENTRY, 0)->type = SXREF; -} - -void -errorexit(void) -{ - if(cout >= 0) { - // For rmtemp run at atexit time on Windows. - close(cout); - } - if(nerrors) { - if(cout >= 0) - mayberemoveoutfile(); - exits("error"); - } - exits(0); -} - -void -loadinternal(char *name) -{ - char pname[1024]; - int i, found; - - found = 0; - 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(ctxt, "internal", "internal", pname, name); - found = 1; - break; - } - } - if(!found) - Bprint(&bso, "warning: unable to find %s.a\n", name); -} - -void -loadlib(void) -{ - int i, w, x; - LSym *s, *tlsg; - char* cgostrsym; - - if(flag_shared) { - s = linklookup(ctxt, "runtime.islibrary", 0); - s->dupok = 1; - adduint8(ctxt, s, 1); - } - - loadinternal("runtime"); - if(thearch.thechar == '5') - loadinternal("math"); - if(flag_race) - loadinternal("runtime/race"); - - 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 == LinkAuto) { - if(iscgo && externalobj) - linkmode = LinkExternal; - else - linkmode = LinkInternal; - - // Force external linking for android. - if(strcmp(goos, "android") == 0) - linkmode = LinkExternal; - - // cgo on Darwin must use external linking - // we can always use external linking, but then there will be circular - // dependency problems when compiling natively (external linking requires - // runtime/cgo, runtime/cgo requires cmd/cgo, but cmd/cgo needs to be - // compiled using external linking.) - if(thearch.thechar == '5' && HEADTYPE == Hdarwin && iscgo) - linkmode = LinkExternal; - } - - 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. - s = linklookup(ctxt, "go.importpath.runtime/cgo.", 0); - s->type = SDATA; - s->dupok = 1; - s->reachable = 1; - - // 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) { - s = linklookup(ctxt, cgostrsym, 0); - s->type = SRODATA; - s->reachable = 1; - addstrdata(cgostrsym, "runtime/cgo"); - } - } - - if(linkmode == LinkInternal) { - // Drop all the cgo_import_static declarations. - // Turns out we won't be needing them. - for(s = ctxt->allsym; s != nil; s = s->allsym) - if(s->type == SHOSTOBJ) { - // If a symbol was marked both - // cgo_import_static and cgo_import_dynamic, - // then we want to make it cgo_import_dynamic - // now. - if(s->extname != nil && s->dynimplib != nil && s->cgoexport == 0) { - s->type = SDYNIMPORT; - } else - s->type = 0; - } - } - - tlsg = linklookup(ctxt, "runtime.tlsg", 0); - // For most ports, runtime.tlsg is a placeholder symbol for TLS - // relocation. However, the Android and Darwin arm ports need it - // to be a real variable. - // - // TODO(crawshaw): android should require leaving the tlsg->type - // alone (as the runtime-provided SNOPTRBSS) just like darwin/arm. - // But some other part of the linker is expecting STLSBSS. - if (!(strcmp(goos, "darwin") == 0 && thearch.thechar == '5')) - tlsg->type = STLSBSS; - tlsg->size = thearch.ptrsize; - tlsg->hide = 1; - tlsg->reachable = 1; - ctxt->tlsg = tlsg; - - // Now that we know the link mode, trim the dynexp list. - x = CgoExportDynamic; - if(linkmode == LinkExternal) - x = CgoExportStatic; - w = 0; - for(i=0; i<ndynexp; i++) - if(dynexp[i]->cgoexport & x) - dynexp[w++] = dynexp[i]; - ndynexp = w; - - // In internal link mode, read the host object files. - if(linkmode == LinkInternal) - hostobjs(); - else - hostlinksetup(); - - // We've loaded all the code now. - // If there are no dynamic libraries needed, gcc disables dynamic linking. - // Because of this, glibc's dynamic ELF loader occasionally (like in version 2.13) - // assumes that a dynamic binary always refers to at least one dynamic library. - // Rather than be a source of test cases for glibc, disable dynamic linking - // the same way that gcc would. - // - // Exception: on OS X, programs such as Shark only work with dynamic - // binaries, so leave it enabled on OS X (Mach-O) binaries. - // 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(); -} - -/* - * look for the next file in an archive. - * adapted from libmach. - */ -static vlong -nextar(Biobuf *bp, vlong off, ArHdr *a) -{ - int r; - int32 arsize; - char *buf; - - if (off&01) - off++; - Bseek(bp, off, 0); - buf = Brdline(bp, '\n'); - r = Blinelen(bp); - if(buf == nil) { - if(r == 0) - return 0; - return -1; - } - if(r != SAR_HDR) - return -1; - memmove(a, buf, SAR_HDR); - if(strncmp(a->fmag, ARFMAG, sizeof a->fmag)) - return -1; - arsize = strtol(a->size, 0, 0); - if (arsize&1) - arsize++; - return arsize + r; -} - -void -objfile(char *file, char *pkg) -{ - vlong off, l; - Biobuf *f; - char magbuf[SARMAG]; - char pname[150]; - ArHdr arhdr; - - pkg = smprint("%i", pkg); - - if(debug['v'] > 1) - Bprint(&bso, "%5.2f ldobj: %s (%s)\n", cputime(), file, pkg); - Bflush(&bso); - f = Bopen(file, 0); - if(f == nil) { - diag("cannot open file: %s", file); - errorexit(); - } - l = Bread(f, magbuf, SARMAG); - if(l != SARMAG || strncmp(magbuf, ARMAG, SARMAG)){ - /* load it as a regular file */ - l = Bseek(f, 0L, 2); - Bseek(f, 0L, 0); - ldobj(f, pkg, l, file, file, FileObj); - Bterm(f); - free(pkg); - return; - } - - /* skip over optional __.GOSYMDEF and process __.PKGDEF */ - off = Boffset(f); - 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)) == 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: cannot find package header", file); - goto out; - } - off += l; - - if(debug['u']) - ldpkg(f, pkg, atolwhex(arhdr.size), file, Pkgdef); - - /* - * load all the object files from the archive now. - * this gives us sequential file access and keeps us - * from needing to come back later to pick up more - * objects. it breaks the usual C archive model, but - * this is Go, not C. the common case in Go is that - * we need to load all the objects, and then we throw away - * the individual symbols that are unused. - * - * loading every object will also make it possible to - * load foreign objects not referenced by __.GOSYMDEF. - */ - for(;;) { - l = nextar(f, off, &arhdr); - if(l == 0) - break; - if(l < 0) { - diag("%s: malformed archive", file); - goto out; - } - off += l; - - l = SARNAME; - while(l > 0 && arhdr.name[l-1] == ' ') - l--; - snprint(pname, sizeof pname, "%s(%.*s)", file, utfnlen(arhdr.name, l), arhdr.name); - l = atolwhex(arhdr.size); - ldobj(f, pkg, l, pname, file, ArchiveObj); - } - -out: - Bterm(f); - free(pkg); -} - -static void -dowrite(int fd, char *p, int n) -{ - int m; - - while(n > 0) { - m = write(fd, p, n); - if(m <= 0) { - ctxt->cursym = nil; - diag("write error: %r"); - errorexit(); - } - n -= m; - p += m; - } -} - -typedef struct Hostobj Hostobj; - -struct Hostobj -{ - void (*ld)(Biobuf*, char*, int64, char*); - char *pkg; - char *pn; - char *file; - int64 off; - int64 length; -}; - -Hostobj *hostobj; -int nhostobj; -int mhostobj; - -// These packages can use internal linking mode. -// Others trigger external mode. -const char *internalpkg[] = { - "crypto/x509", - "net", - "os/user", - "runtime/cgo", - "runtime/race" -}; - -void -ldhostobj(void (*ld)(Biobuf*, char*, int64, char*), Biobuf *f, char *pkg, int64 length, char *pn, char *file) -{ - int i, isinternal; - Hostobj *h; - - isinternal = 0; - for(i=0; i<nelem(internalpkg); i++) { - if(strcmp(pkg, internalpkg[i]) == 0) { - isinternal = 1; - break; - } - } - - // DragonFly declares errno with __thread, which results in a symbol - // type of R_386_TLS_GD or R_X86_64_TLSGD. The Go linker does not - // currently know how to handle TLS relocations, hence we have to - // force external linking for any libraries that link in code that - // uses errno. This can be removed if the Go linker ever supports - // these relocation types. - if(HEADTYPE == Hdragonfly) - if(strcmp(pkg, "net") == 0 || strcmp(pkg, "os/user") == 0) - isinternal = 0; - - if(!isinternal) - externalobj = 1; - - if(nhostobj >= mhostobj) { - if(mhostobj == 0) - mhostobj = 16; - else - mhostobj *= 2; - hostobj = erealloc(hostobj, mhostobj*sizeof hostobj[0]); - } - h = &hostobj[nhostobj++]; - h->ld = ld; - h->pkg = estrdup(pkg); - h->pn = estrdup(pn); - h->file = estrdup(file); - h->off = Boffset(f); - h->length = length; -} - -void -hostobjs(void) -{ - int i; - Biobuf *f; - Hostobj *h; - - for(i=0; i<nhostobj; i++) { - h = &hostobj[i]; - f = Bopen(h->file, OREAD); - if(f == nil) { - ctxt->cursym = nil; - diag("cannot reopen %s: %r", h->pn); - errorexit(); - } - Bseek(f, h->off, 0); - h->ld(f, h->pkg, h->length, h->pn); - Bterm(f); - } -} - -// provided by lib9 -int runcmd(char**); -char* mktempdir(void); -void removeall(char*); - -static void -rmtemp(void) -{ - removeall(tmpdir); -} - -static void -hostlinksetup(void) -{ - char *p; - - if(linkmode != LinkExternal) - return; - - // create temporary directory and arrange cleanup - if(tmpdir == nil) { - tmpdir = mktempdir(); - atexit(rmtemp); - } - - // change our output to temporary object file - close(cout); - p = smprint("%s/go.o", tmpdir); - cout = create(p, 1, 0775); - if(cout < 0) { - diag("cannot create %s: %r", p); - errorexit(); - } - Binit(&coutbuf, cout, OWRITE); - free(p); -} - -void -hostlink(void) -{ - char *p, **argv; - int c, i, w, n, argc, length; - Hostobj *h; - Biobuf *f; - static char buf[64<<10]; - - if(linkmode != LinkExternal || nerrors > 0) - return; - - c = 0; - p = extldflags; - while(p != nil) { - while(*p == ' ') - p++; - if(*p == '\x00') - break; - c++; - p = strchr(p + 1, ' '); - } - - argv = malloc((14+nhostobj+nldflag+c)*sizeof argv[0]); - argc = 0; - if(extld == nil) - extld = "gcc"; - argv[argc++] = extld; - switch(thearch.thechar){ - case '8': - argv[argc++] = "-m32"; - break; - case '6': - case '9': - argv[argc++] = "-m64"; - break; - case '5': - argv[argc++] = "-marm"; - break; - } - if(!debug['s'] && !debug_s) { - argv[argc++] = "-gdwarf-2"; - } else { - argv[argc++] = "-s"; - } - 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"; - - if(flag_shared) { - argv[argc++] = "-Wl,-Bsymbolic"; - argv[argc++] = "-shared"; - } - argv[argc++] = "-o"; - argv[argc++] = outfile; - - if(rpath) - argv[argc++] = smprint("-Wl,-rpath,%s", rpath); - - // Force global symbols to be exported for dlopen, etc. - if(iself) - argv[argc++] = "-rdynamic"; - - 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) { - ctxt->cursym = nil; - diag("cannot reopen %s: %r", h->pn); - errorexit(); - } - Bseek(f, h->off, 0); - p = smprint("%s/%06d.o", tmpdir, i); - argv[argc++] = p; - w = create(p, 1, 0775); - if(w < 0) { - ctxt->cursym = nil; - diag("cannot create %s: %r", p); - errorexit(); - } - length = h->length; - while(length > 0) { - n = Bread(f, buf, sizeof buf); - if(n <= 0) - break; - if(n > length) - n = length; - dowrite(w, buf, n); - length -= n; - } - if(close(w) < 0) { - ctxt->cursym = nil; - diag("cannot write %s: %r", p); - errorexit(); - } - Bterm(f); - } - - argv[argc++] = smprint("%s/go.o", tmpdir); - for(i=0; i<nldflag; i++) - argv[argc++] = ldflag[i]; - - p = extldflags; - while(p != nil) { - while(*p == ' ') - *p++ = '\x00'; - if(*p == '\x00') - 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]=='\x00')) { - for(i=0; i<argc; i++) { - if(strcmp(argv[i], "-rdynamic") == 0) - argv[i] = "-static"; - } - } - - p = strchr(p + 1, ' '); - } - - argv[argc] = nil; - - quotefmtinstall(); - if(debug['v']) { - Bprint(&bso, "host link:"); - for(i=0; i<argc; i++) - Bprint(&bso, " %q", argv[i]); - Bprint(&bso, "\n"); - Bflush(&bso); - } - - if(runcmd(argv) < 0) { - ctxt->cursym = nil; - diag("%s: running %s failed: %r", argv0, argv[0]); - errorexit(); - } -} - -void -ldobj(Biobuf *f, char *pkg, int64 length, char *pn, char *file, int whence) -{ - char *line; - int n, c1, c2, c3, c4; - uint32 magic; - vlong import0, import1, eof, start; - char *t; - - eof = Boffset(f) + length; - - pn = estrdup(pn); - - start = Boffset(f); - c1 = BGETC(f); - c2 = BGETC(f); - c3 = BGETC(f); - c4 = BGETC(f); - Bseek(f, start, 0); - - magic = c1<<24 | c2<<16 | c3<<8 | c4; - if(magic == 0x7f454c46) { // \x7F E L F - ldhostobj(ldelf, f, pkg, length, pn, file); - return; - } - if((magic&~1) == 0xfeedface || (magic&~0x01000000) == 0xcefaedfe) { - ldhostobj(ldmacho, f, pkg, length, pn, file); - return; - } - if(c1 == 0x4c && c2 == 0x01 || c1 == 0x64 && c2 == 0x86) { - ldhostobj(ldpe, f, pkg, length, pn, file); - return; - } - - /* check the header */ - line = Brdline(f, '\n'); - if(line == nil) { - if(Blinelen(f) > 0) { - diag("%s: not an object file", pn); - return; - } - goto eof; - } - n = Blinelen(f) - 1; - line[n] = '\x00'; - if(strncmp(line, "go object ", 10) != 0) { - if(strlen(pn) > 3 && strcmp(pn+strlen(pn)-3, ".go") == 0) { - print("%cl: input %s is not .%c file (use %cg to compile .go files)\n", thearch.thechar, pn, thearch.thechar, thearch.thechar); - errorexit(); - } - if(strcmp(line, thestring) == 0) { - // old header format: just $GOOS - diag("%s: stale object file", pn); - return; - } - diag("%s: not an object file", pn); - free(pn); - return; - } - - // 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] = '\x00'; - diag("%s: object is [%s] expected [%s]", pn, line+10, t); - free(t); - free(pn); - return; - } - - // Second, check that longer lines match each other exactly, - // so that the Go compiler and write additional information - // that must be the same from run to run. - line[n] = '\x00'; - if(n-10 > strlen(t)) { - if(theline == nil) - theline = estrdup(line+10); - else if(strcmp(theline, line+10) != 0) { - line[n] = '\x00'; - diag("%s: object is [%s] expected [%s]", pn, line+10, theline); - free(t); - free(pn); - return; - } - } - free(t); - line[n] = '\n'; - - /* skip over exports and other info -- ends with \n!\n */ - import0 = Boffset(f); - c1 = '\n'; // the last line ended in \n - c2 = BGETC(f); - c3 = BGETC(f); - while(c1 != '\n' || c2 != '!' || c3 != '\n') { - c1 = c2; - c2 = c3; - c3 = BGETC(f); - if(c3 == Beof) - goto eof; - } - import1 = Boffset(f); - - Bseek(f, import0, 0); - ldpkg(f, pkg, import1 - import0 - 2, pn, whence); // -2 for !\n - Bseek(f, import1, 0); - - ldobjfile(ctxt, f, pkg, eof - Boffset(f), pn); - free(pn); - return; - -eof: - diag("truncated object file: %s", pn); - free(pn); -} - -void -zerosig(char *sp) -{ - LSym *s; - - s = linklookup(ctxt, sp, 0); - s->sig = 0; -} - -void -mywhatsys(void) -{ - goroot = getgoroot(); - goos = getgoos(); - goarch = getgoarch(); - - if(strncmp(goarch, thestring, strlen(thestring)) != 0) - sysfatal("cannot use %cc with GOARCH=%s", thearch.thechar, goarch); -} - -int -pathchar(void) -{ - return '/'; -} - -static uchar* hunk; -static uint32 nhunk; -enum { - NHUNK = 10<<20, -}; - -void* -mal(uint32 n) -{ - void *v; - - n = (n+7)&~7; - if(n > NHUNK) { - v = malloc(n); - if(v == nil) { - diag("out of memory"); - errorexit(); - } - memset(v, 0, n); - return v; - } - if(n > nhunk) { - hunk = malloc(NHUNK); - if(hunk == nil) { - diag("out of memory"); - errorexit(); - } - nhunk = NHUNK; - } - - v = hunk; - nhunk -= n; - hunk += n; - - memset(v, 0, n); - return v; -} - -void -unmal(void *v, uint32 n) -{ - n = (n+7)&~7; - if(hunk - n == v) { - hunk -= n; - nhunk += n; - } -} - -// Copied from ../gc/subr.c:/^pathtoprefix; must stay in sync. -/* - * Convert raw string to the prefix that will be used in the symbol table. - * Invalid bytes turn into %xx. Right now the only bytes that need - * escaping are %, ., and ", but we escape all control characters too. - * - * If you edit this, edit ../gc/subr.c:/^pathtoprefix too. - * If you edit this, edit ../../debug/goobj/read.go:/importPathToPrefix too. - */ -static char* -pathtoprefix(char *s) -{ - static char hex[] = "0123456789abcdef"; - char *p, *r, *w, *l; - int n; - - // find first character past the last slash, if any. - l = s; - for(r=s; *r; r++) - if(*r == '/') - l = r+1; - - // check for chars that need escaping - n = 0; - for(r=s; *r; r++) - if(*r <= ' ' || (*r == '.' && r >= l) || *r == '%' || *r == '"' || *r >= 0x7f) - n++; - - // quick exit - if(n == 0) - return s; - - // escape - p = mal((r-s)+1+2*n); - for(r=s, w=p; *r; r++) { - if(*r <= ' ' || (*r == '.' && r >= l) || *r == '%' || *r == '"' || *r >= 0x7f) { - *w++ = '%'; - *w++ = hex[(*r>>4)&0xF]; - *w++ = hex[*r&0xF]; - } else - *w++ = *r; - } - *w = '\x00'; - return p; -} - -int -iconv(Fmt *fp) -{ - char *p; - - p = va_arg(fp->args, char*); - if(p == nil) { - fmtstrcpy(fp, "<nil>"); - return 0; - } - p = pathtoprefix(p); - fmtstrcpy(fp, p); - return 0; -} - -Section* -addsection(Segment *seg, char *name, int rwx) -{ - Section **l; - Section *sect; - - for(l=&seg->sect; *l; l=&(*l)->next) - ; - sect = mal(sizeof *sect); - sect->rwx = rwx; - sect->name = name; - sect->seg = seg; - sect->align = thearch.ptrsize; // everything is at least pointer-aligned - *l = sect; - return sect; -} - -uint16 -le16(uchar *b) -{ - return b[0] | b[1]<<8; -} - -uint32 -le32(uchar *b) -{ - return b[0] | b[1]<<8 | b[2]<<16 | (uint32)b[3]<<24; -} - -uint64 -le64(uchar *b) -{ - return le32(b) | (uint64)le32(b+4)<<32; -} - -uint16 -be16(uchar *b) -{ - return b[0]<<8 | b[1]; -} - -uint32 -be32(uchar *b) -{ - return (uint32)b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3]; -} - -uint64 -be64(uchar *b) -{ - return (uvlong)be32(b)<<32 | be32(b+4); -} - -Endian be = { be16, be32, be64 }; -Endian le = { le16, le32, le64 }; - -typedef struct Chain Chain; -struct Chain -{ - LSym *sym; - Chain *up; - int limit; // limit on entry to sym -}; - -static int stkcheck(Chain*, int); -static void stkprint(Chain*, int); -static void stkbroke(Chain*, int); -static LSym *morestack; -static LSym *newstack; - -// TODO: Record enough information in new object files to -// allow stack checks here. - -static int -haslinkregister(void) -{ - return thearch.thechar == '5' || thearch.thechar == '9'; -} - -static int -callsize(void) -{ - if(haslinkregister()) - return 0; - return thearch.regsize; -} - -void -dostkcheck(void) -{ - Chain ch; - LSym *s; - - 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; - - if(s->nosplit) { - ctxt->cursym = s; - ch.sym = s; - stkcheck(&ch, 0); - } - } - for(s = ctxt->textp; s != nil; s = s->next) { - if(!s->nosplit) { - ctxt->cursym = s; - ch.sym = s; - stkcheck(&ch, 0); - } - } -} - -static int -stkcheck(Chain *up, int depth) -{ - Chain ch, ch1; - LSym *s; - int limit; - Reloc *r; - int ri, endr; - Pciter pcsp; - - limit = up->limit; - s = up->sym; - - // 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"); - stkbroke(up, 0); - return -1; - } - - if(s->external || s->pcln == nil) { - // external function. - // should never be called directly. - // only diagnose the direct caller. - if(depth == 1 && s->type != SXREF) - diag("call to external function %s", s->name); - return -1; - } - - if(limit < 0) { - stkbroke(up, limit); - return -1; - } - - // morestack looks like it calls functions, - // but it switches the stack pointer first. - if(s == morestack) - return 0; - - ch.up = up; - - // Walk through sp adjustments in function, consuming relocs. - ri = 0; - endr = 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; - } - - // Process calls in this span. - for(; ri < endr && s->r[ri].off < pcsp.nextpc; ri++) { - r = &s->r[ri]; - switch(r->type) { - case R_CALL: - case R_CALLARM: - case R_CALLPOWER: - // Direct call. - ch.limit = limit - pcsp.value - callsize(); - ch.sym = r->sym; - if(stkcheck(&ch, depth+1) < 0) - return -1; - - // 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(haslinkregister()) - limit += thearch.regsize; - } - 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. - // 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 = ch.limit - callsize(); // for morestack in called prologue - ch1.up = &ch; - ch1.sym = morestack; - if(stkcheck(&ch1, depth+2) < 0) - return -1; - break; - } - } - } - - return 0; -} - -static void -stkbroke(Chain *ch, int limit) -{ - diag("nosplit stack overflow"); - stkprint(ch, limit); -} - -static void -stkprint(Chain *ch, int limit) -{ - char *name; - - if(ch->sym) - name = ch->sym->name; - else - name = "function pointer"; - - if(ch->up == nil) { - // top of chain. ch->sym != nil. - 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); - } else { - stkprint(ch->up, ch->limit + callsize()); - if(!haslinkregister()) - print("\t%d\ton entry to %s\n", ch->limit, name); - } - if(ch->limit != limit) - print("\t%d\tafter %s uses %d\n", limit, name, ch->limit - limit); -} - -int -Yconv(Fmt *fp) -{ - LSym *s; - Fmt fmt; - int i; - char *str; - - s = va_arg(fp->args, LSym*); - if (s == nil) { - fmtprint(fp, "<nil>"); - } else { - fmtstrinit(&fmt); - fmtprint(&fmt, "%s @0x%08llx [%lld]", s->name, (vlong)s->value, (vlong)s->size); - for (i = 0; i < s->size; i++) { - if (!(i%8)) fmtprint(&fmt, "\n\t0x%04x ", i); - fmtprint(&fmt, "%02x ", s->p[i]); - } - fmtprint(&fmt, "\n"); - for (i = 0; i < s->nr; i++) { - fmtprint(&fmt, "\t0x%04x[%x] %d %s[%llx]\n", - s->r[i].off, - s->r[i].siz, - s->r[i].type, - s->r[i].sym->name, - (vlong)s->r[i].add); - } - str = fmtstrflush(&fmt); - fmtstrcpy(fp, str); - free(str); - } - - return 0; -} - -void -cflush(void) -{ - Bflush(&coutbuf); -} - -vlong -cpos(void) -{ - return Boffset(&coutbuf); -} - -void -cseek(vlong p) -{ - Bseek(&coutbuf, p, 0); -} - -void -cwrite(void *buf, int n) -{ - Bflush(&coutbuf); // TODO: Remove if safe. - Bwrite(&coutbuf, buf, n); -} - -void -cput(uint8 c) -{ - Bputc(&coutbuf, c); -} - -void -usage(void) -{ - fprint(2, "usage: %cl [options] main.%c\n", thearch.thechar, thearch.thechar); - flagprint(2); - exits("usage"); -} - -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); -} - -void -setinterp(char *s) -{ - debug['I'] = 1; // denote cmdline interpreter override - interpreter = s; -} - -void -doversion(void) -{ - print("%cl version %s\n", thearch.thechar, getgoversion()); - errorexit(); -} - -void -genasmsym(void (*put)(LSym*, char*, int, vlong, vlong, int, LSym*)) -{ - Auto *a; - 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 = linklookup(ctxt, "runtime.text", 0); - if(s->type == STEXT) - put(s, s->name, 'T', s->value, s->size, s->version, 0); - s = linklookup(ctxt, "runtime.etext", 0); - if(s->type == STEXT) - put(s, s->name, 'T', s->value, s->size, s->version, 0); - - for(s=ctxt->allsym; s!=nil; s=s->allsym) { - if(s->hide || (s->name[0] == '.' && s->version == 0 && strcmp(s->name, ".rathole") != 0)) - continue; - switch(s->type&SMASK) { - case SCONST: - case SRODATA: - case SSYMTAB: - case SPCLNTAB: - case SDATA: - case SNOPTRDATA: - case SELFROSECT: - case SMACHOGOT: - case STYPE: - case SSTRING: - case SGOSTRING: - case SWINDOWS: - if(!s->reachable) - continue; - put(s, s->name, 'D', symaddr(s), s->size, s->version, s->gotype); - continue; - - case SBSS: - case SNOPTRBSS: - if(!s->reachable) - continue; - if(s->np > 0) - diag("%s should not be bss (size=%d type=%d special=%d)", s->name, (int)s->np, s->type, s->special); - put(s, s->name, 'B', symaddr(s), s->size, s->version, s->gotype); - continue; - - case SFILE: - put(nil, s->name, 'f', s->value, 0, s->version, 0); - 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+thearch.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->name != A_AUTO && a->name != A_PARAM) - continue; - - // compute offset relative to FP - if(a->name == A_PARAM) - off = a->aoffset; - else - off = a->aoffset - thearch.ptrsize; - - // FP - if(off >= 0) { - put(nil, a->asym->name, 'p', off, 0, 0, a->gotype); - continue; - } - - // SP - if(off <= -thearch.ptrsize) { - put(nil, a->asym->name, 'a', -(off+thearch.ptrsize), 0, 0, a->gotype); - continue; - } - - // Otherwise, off is addressing the saved program counter. - // Something underhanded is going on. Say nothing. - } - } - if(debug['v'] || debug['n']) - Bprint(&bso, "%5.2f symsize = %ud\n", cputime(), symsize); - Bflush(&bso); -} - -vlong -symaddr(LSym *s) -{ - if(!s->reachable) - diag("unreachable symbol in symaddr - %s", s->name); - return s->value; -} - -void -xdefine(char *p, int t, vlong v) -{ - LSym *s; - - s = linklookup(ctxt, p, 0); - s->type = t; - s->value = v; - s->reachable = 1; - s->special = 1; -} - -vlong -datoff(vlong addr) -{ - 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; -} - -vlong -entryvalue(void) -{ - char *a; - LSym *s; - - 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; -} - -static void -undefsym(LSym *s) -{ - int i; - Reloc *r; - - 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); - } -} - -void -undef(void) -{ - LSym *s; - - 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(); -} - -void -callgraph(void) -{ - LSym *s; - Reloc *r; - int i; - - if(!debug['c']) - return; - - 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; - if((r->type == R_CALL || r->type == R_CALLARM || r->type == R_CALLPOWER) && r->sym->type == STEXT) - Bprint(&bso, "%s calls %s\n", s->name, r->sym->name); - } - } -} - -void -diag(char *fmt, ...) -{ - char buf[1024], *tn, *sep; - va_list arg; - - tn = ""; - sep = ""; - if(ctxt->cursym != nil) { - 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(); - } -} - -void -checkgo(void) -{ - LSym *s; - Reloc *r; - int i; - int changed; - - if(!debug['C']) - return; - - // TODO(rsc,khr): Eventually we want to get to no Go-called C functions at all, - // which would simplify this logic quite a bit. - - // Mark every Go-called C function with cfunc=2, recursively. - do { - changed = 0; - for(s = ctxt->textp; s != nil; s = s->next) { - if(s->cfunc == 0 || (s->cfunc == 2 && s->nosplit)) { - for(i=0; i<s->nr; i++) { - r = &s->r[i]; - if(r->sym == nil) - continue; - if((r->type == R_CALL || r->type == R_CALLARM) && r->sym->type == STEXT) { - if(r->sym->cfunc == 1) { - changed = 1; - r->sym->cfunc = 2; - } - } - } - } - } - }while(changed); - - // Complain about Go-called C functions that can split the stack - // (that can be preempted for garbage collection or trigger a stack copy). - for(s = ctxt->textp; s != nil; s = s->next) { - if(s->cfunc == 0 || (s->cfunc == 2 && s->nosplit)) { - for(i=0; i<s->nr; i++) { - r = &s->r[i]; - if(r->sym == nil) - continue; - if((r->type == R_CALL || r->type == R_CALLARM) && r->sym->type == STEXT) { - if(s->cfunc == 0 && r->sym->cfunc == 2 && !r->sym->nosplit) - print("Go %s calls C %s\n", s->name, r->sym->name); - else if(s->cfunc == 2 && s->nosplit && !r->sym->nosplit) - print("Go calls C %s calls %s\n", s->name, r->sym->name); - } - } - } - } -} - -vlong -rnd(vlong v, vlong r) -{ - vlong c; - - if(r <= 0) - return v; - v += r - 1; - c = v % r; - if(c < 0) - c += r; - v -= c; - return v; -} |
