diff options
Diffstat (limited to 'src/cmd/8l/obj.c')
-rw-r--r-- | src/cmd/8l/obj.c | 739 |
1 files changed, 739 insertions, 0 deletions
diff --git a/src/cmd/8l/obj.c b/src/cmd/8l/obj.c new file mode 100644 index 000000000..a8e1c34a5 --- /dev/null +++ b/src/cmd/8l/obj.c @@ -0,0 +1,739 @@ +// Inferno utils/8l/obj.c +// http://code.google.com/p/inferno-os/source/browse/utils/8l/obj.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. + +// Reading object files. + +#define EXTERN +#include "l.h" +#include "../ld/lib.h" +#include "../ld/elf.h" +#include "../ld/macho.h" +#include "../ld/dwarf.h" +#include "../ld/pe.h" +#include <ar.h> + +#ifndef DEFAULT +#define DEFAULT '9' +#endif + +char *noname = "<none>"; +char *thestring = "386"; + +Header headers[] = { + "garbunix", Hgarbunix, + "unixcoff", Hunixcoff, + "plan9", Hplan9x32, + "msdoscom", Hmsdoscom, + "msdosexe", Hmsdosexe, + "darwin", Hdarwin, + "linux", Hlinux, + "freebsd", Hfreebsd, + "windows", Hwindows, + "windowsgui", Hwindows, + 0, 0 +}; + +/* + * -Hgarbunix -T0x40004C -D0x10000000 is garbage unix + * -Hunixcoff -T0xd0 -R4 is unix coff + * -Hplan9 -T4128 -R4096 is plan9 format + * -Hmsdoscom -Tx -Rx is MS-DOS .COM + * -Hmsdosexe -Tx -Rx is fake MS-DOS .EXE + * -Hdarwin -Tx -Rx is Apple Mach-O + * -Hlinux -Tx -Rx is Linux ELF32 + * -Hfreebsd -Tx -Rx is FreeBSD ELF32 + * -Hwindows -Tx -Rx is MS Windows PE32 + */ + +void +usage(void) +{ + fprint(2, "usage: 8l [-options] [-E entry] [-H head] [-I interpreter] [-L dir] [-T text] [-R rnd] [-r path] [-o out] main.8\n"); + exits("usage"); +} + +void +main(int argc, char *argv[]) +{ + int c; + + Binit(&bso, 1, OWRITE); + listinit(); + memset(debug, 0, sizeof(debug)); + nerrors = 0; + outfile = nil; + HEADTYPE = -1; + INITTEXT = -1; + INITDAT = -1; + INITRND = -1; + INITENTRY = 0; + + ARGBEGIN { + default: + c = ARGC(); + if(c == 'l') + usage(); + if(c >= 0 && c < sizeof(debug)) + debug[c]++; + break; + case 'o': /* output to (next arg) */ + outfile = EARGF(usage()); + break; + case 'E': + INITENTRY = EARGF(usage()); + break; + case 'H': + HEADTYPE = headtype(EARGF(usage())); + break; + case 'I': + interpreter = EARGF(usage()); + break; + case 'L': + Lflag(EARGF(usage())); + break; + case 'T': + INITTEXT = atolwhex(EARGF(usage())); + break; + case 'D': + INITDAT = atolwhex(EARGF(usage())); + break; + case 'R': + INITRND = atolwhex(EARGF(usage())); + break; + case 'r': + rpath = EARGF(usage()); + break; + case 'V': + print("%cl version %s\n", thechar, getgoversion()); + errorexit(); + } ARGEND + + if(argc != 1) + usage(); + + mywhatsys(); // get goos + + if(HEADTYPE == -1) + HEADTYPE = headtype(goos); + + if(outfile == nil) { + if(HEADTYPE == Hwindows) + outfile = "8.out.exe"; + else + outfile = "8.out"; + } + + libinit(); + + switch(HEADTYPE) { + default: + diag("unknown -H option"); + errorexit(); + + case Hgarbunix: /* this is garbage */ + HEADR = 20L+56L; + if(INITTEXT == -1) + INITTEXT = 0x40004CL; + if(INITDAT == -1) + INITDAT = 0x10000000L; + if(INITRND == -1) + INITRND = 0; + break; + case Hunixcoff: /* is unix coff */ + HEADR = 0xd0L; + if(INITTEXT == -1) + INITTEXT = 0xd0; + if(INITDAT == -1) + INITDAT = 0x400000; + if(INITRND == -1) + INITRND = 0; + break; + case Hplan9x32: /* plan 9 */ + tlsoffset = -8; + HEADR = 32L; + if(INITTEXT == -1) + INITTEXT = 4096+32; + if(INITDAT == -1) + INITDAT = 0; + if(INITRND == -1) + INITRND = 4096; + break; + case Hmsdoscom: /* MS-DOS .COM */ + HEADR = 0; + if(INITTEXT == -1) + INITTEXT = 0x0100; + if(INITDAT == -1) + INITDAT = 0; + if(INITRND == -1) + INITRND = 4; + break; + case Hmsdosexe: /* fake MS-DOS .EXE */ + HEADR = 0x200; + if(INITTEXT == -1) + INITTEXT = 0x0100; + if(INITDAT == -1) + INITDAT = 0; + if(INITRND == -1) + INITRND = 4; + HEADR += (INITTEXT & 0xFFFF); + if(debug['v']) + Bprint(&bso, "HEADR = 0x%d\n", HEADR); + break; + case Hdarwin: /* apple MACH */ + /* + * OS X system constant - offset from %gs to our TLS. + * Explained in ../../libcgo/darwin_386.c. + */ + tlsoffset = 0x468; + machoinit(); + HEADR = INITIAL_MACHO_HEADR; + if(INITTEXT == -1) + INITTEXT = 4096+HEADR; + if(INITDAT == -1) + INITDAT = 0; + if(INITRND == -1) + INITRND = 4096; + break; + case Hlinux: /* elf32 executable */ + case Hfreebsd: + /* + * ELF uses TLS offsets negative from %gs. + * Translate 0(GS) and 4(GS) into -8(GS) and -4(GS). + * Also known to ../../pkg/runtime/linux/386/sys.s + * and ../../libcgo/linux_386.c. + */ + tlsoffset = -8; + elfinit(); + HEADR = ELFRESERVE; + if(INITTEXT == -1) + INITTEXT = 0x08048000+HEADR; + if(INITDAT == -1) + INITDAT = 0; + if(INITRND == -1) + INITRND = 4096; + break; + case Hwindows: /* PE executable */ + peinit(); + HEADR = PEFILEHEADR; + if(INITTEXT == -1) + INITTEXT = PEBASE+PESECTHEADR; + if(INITDAT == -1) + INITDAT = 0; + if(INITRND == -1) + INITRND = PESECTALIGN; + break; + } + if(INITDAT != 0 && INITRND != 0) + print("warning: -D0x%ux is ignored because of -R0x%ux\n", + INITDAT, INITRND); + if(debug['v']) + Bprint(&bso, "HEADER = -H0x%d -T0x%ux -D0x%ux -R0x%ux\n", + HEADTYPE, INITTEXT, INITDAT, INITRND); + Bflush(&bso); + + instinit(); + zprg.link = P; + zprg.pcond = P; + zprg.back = 2; + zprg.as = AGOK; + zprg.from.type = D_NONE; + zprg.from.index = D_NONE; + zprg.from.scale = 1; + zprg.to = zprg.from; + + pcstr = "%.6ux "; + nuxiinit(); + histgen = 0; + pc = 0; + dtype = 4; + version = 0; + cbp = buf.cbuf; + cbc = sizeof(buf.cbuf); + + addlibpath("command line", "command line", argv[0], "main"); + loadlib(); + deadcode(); + patch(); + follow(); + doelf(); + if(HEADTYPE == Hdarwin) + domacho(); + if(HEADTYPE == Hwindows) + dope(); + dostkoff(); + if(debug['p']) + if(debug['1']) + doprof1(); + else + doprof2(); + span(); + addexport(); + textaddress(); + pclntab(); + symtab(); + dodata(); + address(); + doweak(); + reloc(); + asmb(); + undef(); + if(debug['v']) { + Bprint(&bso, "%5.2f cpu time\n", cputime()); + Bprint(&bso, "%d symbols\n", nsymbol); + Bprint(&bso, "%d sizeof adr\n", sizeof(Adr)); + Bprint(&bso, "%d sizeof prog\n", sizeof(Prog)); + } + Bflush(&bso); + + errorexit(); +} + +static Sym* +zsym(char *pn, Biobuf *f, Sym *h[]) +{ + int o; + + o = Bgetc(f); + if(o < 0 || o >= NSYM || h[o] == nil) + mangle(pn); + return h[o]; +} + +static void +zaddr(char *pn, Biobuf *f, Adr *a, Sym *h[]) +{ + int t; + int32 l; + Sym *s; + Auto *u; + + t = Bgetc(f); + a->index = D_NONE; + a->scale = 0; + if(t & T_INDEX) { + a->index = Bgetc(f); + a->scale = Bgetc(f); + } + a->type = D_NONE; + a->offset = 0; + if(t & T_OFFSET) + a->offset = Bget4(f); + a->offset2 = 0; + if(t & T_OFFSET2) { + a->offset2 = Bget4(f); + a->type = D_CONST2; + } + a->sym = S; + if(t & T_SYM) + a->sym = zsym(pn, f, h); + if(t & T_FCONST) { + a->ieee.l = Bget4(f); + a->ieee.h = Bget4(f); + a->type = D_FCONST; + } else + if(t & T_SCONST) { + Bread(f, a->scon, NSNAME); + a->type = D_SCONST; + } + if(t & T_TYPE) + a->type = Bgetc(f); + adrgotype = S; + if(t & T_GOTYPE) + adrgotype = zsym(pn, f, h); + + t = a->type; + if(t == D_INDIR+D_GS) + a->offset += tlsoffset; + + s = a->sym; + if(s == S) + return; + if(t != D_AUTO && t != D_PARAM) { + if(adrgotype) + s->gotype = adrgotype; + return; + } + l = a->offset; + for(u=curauto; u; u=u->link) { + if(u->asym == s) + if(u->type == t) { + if(u->aoffset > l) + u->aoffset = l; + if(adrgotype) + u->gotype = adrgotype; + return; + } + } + + u = mal(sizeof(*u)); + u->link = curauto; + curauto = u; + u->asym = s; + u->aoffset = l; + u->type = t; + u->gotype = adrgotype; +} + +void +nopout(Prog *p) +{ + p->as = ANOP; + p->from.type = D_NONE; + p->to.type = D_NONE; +} + +void +ldobj1(Biobuf *f, char *pkg, int64 len, char *pn) +{ + int32 ipc; + Prog *p; + int v, o, r, skip; + Sym *h[NSYM], *s; + uint32 sig; + int ntext; + int32 eof; + char *name, *x; + char src[1024]; + Prog *lastp; + + lastp = nil; + ntext = 0; + eof = Boffset(f) + len; + src[0] = 0; + + +newloop: + memset(h, 0, sizeof(h)); + version++; + histfrogp = 0; + ipc = pc; + skip = 0; + +loop: + if(f->state == Bracteof || Boffset(f) >= eof) + goto eof; + o = Bgetc(f); + if(o == Beof) + goto eof; + o |= Bgetc(f) << 8; + if(o <= AXXX || o >= ALAST) { + if(o < 0) + goto eof; + diag("%s:#%lld: opcode out of range: %#ux", pn, Boffset(f), o); + print(" probably not a .%c file\n", thechar); + errorexit(); + } + + if(o == ANAME || o == ASIGNAME) { + sig = 0; + if(o == ASIGNAME) + sig = Bget4(f); + v = Bgetc(f); /* type */ + o = Bgetc(f); /* sym */ + r = 0; + if(v == D_STATIC) + r = version; + name = Brdline(f, '\0'); + if(name == nil) { + if(Blinelen(f) > 0) { + fprint(2, "%s: name too long\n", pn); + errorexit(); + } + goto eof; + } + x = expandpkg(name, pkg); + s = lookup(x, r); + if(x != name) + free(x); + + if(debug['S'] && r == 0) + sig = 1729; + if(sig != 0){ + if(s->sig != 0 && s->sig != sig) + diag("incompatible type signatures" + "%ux(%s) and %ux(%s) for %s", + s->sig, s->file, sig, pn, s->name); + s->sig = sig; + s->file = pn; + } + + if(debug['W']) + print(" ANAME %s\n", s->name); + if(o < 0 || o >= nelem(h)) + mangle(pn); + h[o] = s; + if((v == D_EXTERN || v == D_STATIC) && s->type == 0) + s->type = SXREF; + if(v == D_FILE) { + if(s->type != SFILE) { + histgen++; + s->type = SFILE; + s->value = histgen; + } + if(histfrogp < MAXHIST) { + histfrog[histfrogp] = s; + histfrogp++; + } else + collapsefrog(s); + dwarfaddfrag(s->value, s->name); + } + goto loop; + } + + p = mal(sizeof(*p)); + p->as = o; + p->line = Bget4(f); + p->back = 2; + p->ft = 0; + p->tt = 0; + zaddr(pn, f, &p->from, h); + fromgotype = adrgotype; + zaddr(pn, f, &p->to, h); + + if(debug['W']) + print("%P\n", p); + + switch(p->as) { + case AHISTORY: + if(p->to.offset == -1) { + addlib(src, pn); + histfrogp = 0; + goto loop; + } + if(src[0] == '\0') + copyhistfrog(src, sizeof src); + addhist(p->line, D_FILE); /* 'z' */ + if(p->to.offset) + addhist(p->to.offset, D_FILE1); /* 'Z' */ + histfrogp = 0; + goto loop; + + case AEND: + histtoauto(); + if(cursym != nil && cursym->text) + cursym->autom = curauto; + curauto = 0; + cursym = nil; + if(Boffset(f) == eof) + return; + goto newloop; + + case AGLOBL: + s = p->from.sym; + if(s->type == 0 || s->type == SXREF) { + s->type = SBSS; + s->size = 0; + } + if(s->type != SBSS && !s->dupok) { + diag("%s: redefinition: %s in %s", + pn, s->name, TNAME); + s->type = SBSS; + s->size = 0; + } + if(p->to.offset > s->size) + s->size = p->to.offset; + if(p->from.scale & DUPOK) + s->dupok = 1; + if(p->from.scale & RODATA) + s->type = SRODATA; + goto loop; + + case ADATA: + // Assume that AGLOBL comes after ADATA. + // If we've seen an AGLOBL that said this sym was DUPOK, + // ignore any more ADATA we see, which must be + // redefinitions. + s = p->from.sym; + if(s->dupok) { +// if(debug['v']) +// Bprint(&bso, "skipping %s in %s: dupok\n", s->name, pn); + goto loop; + } + if(s->file == nil) + s->file = pn; + else if(s->file != pn) { + diag("multiple initialization for %s: in both %s and %s", s->name, s->file, pn); + errorexit(); + } + savedata(s, p, pn); + unmal(p, sizeof *p); + goto loop; + + case AGOK: + diag("%s: GOK opcode in %s", pn, TNAME); + pc++; + goto loop; + + case ATEXT: + s = p->from.sym; + if(s->text != nil) { + diag("%s: %s: redefinition", pn, s->name); + return; + } + if(ntext++ == 0 && s->type != 0 && s->type != SXREF) { + /* redefinition, so file has probably been seen before */ + if(debug['v']) + diag("skipping: %s: redefinition: %s", pn, s->name); + return; + } + if(cursym != nil && cursym->text) { + histtoauto(); + cursym->autom = curauto; + curauto = 0; + } + skip = 0; + if(etextp) + etextp->next = s; + else + textp = s; + etextp = s; + s->text = p; + cursym = s; + if(s->type != 0 && s->type != SXREF) { + if(p->from.scale & DUPOK) { + skip = 1; + goto casdef; + } + diag("%s: redefinition: %s\n%P", pn, s->name, p); + } + s->type = STEXT; + s->value = pc; + lastp = p; + p->pc = pc++; + goto loop; + + case AFMOVF: + case AFADDF: + case AFSUBF: + case AFSUBRF: + case AFMULF: + case AFDIVF: + case AFDIVRF: + case AFCOMF: + case AFCOMFP: + if(skip) + goto casdef; + if(p->from.type == D_FCONST) { + /* size sb 9 max */ + sprint(literal, "$%ux", ieeedtof(&p->from.ieee)); + s = lookup(literal, 0); + if(s->type == 0) { + s->type = SDATA; + adduint32(s, ieeedtof(&p->from.ieee)); + s->reachable = 0; + } + p->from.type = D_EXTERN; + p->from.sym = s; + p->from.offset = 0; + } + goto casdef; + + case AFMOVD: + case AFADDD: + case AFSUBD: + case AFSUBRD: + case AFMULD: + case AFDIVD: + case AFDIVRD: + case AFCOMD: + case AFCOMDP: + if(skip) + goto casdef; + if(p->from.type == D_FCONST) { + /* size sb 18 max */ + sprint(literal, "$%ux.%ux", + p->from.ieee.l, p->from.ieee.h); + s = lookup(literal, 0); + if(s->type == 0) { + s->type = SDATA; + adduint32(s, p->from.ieee.l); + adduint32(s, p->from.ieee.h); + s->reachable = 0; + } + p->from.type = D_EXTERN; + p->from.sym = s; + p->from.offset = 0; + } + goto casdef; + + casdef: + default: + if(skip) + nopout(p); + p->pc = pc; + pc++; + + if(p->to.type == D_BRANCH) + p->to.offset += ipc; + if(lastp == nil) { + if(p->as != ANOP) + diag("unexpected instruction: %P", p); + goto loop; + } + lastp->link = p; + lastp = p; + goto loop; + } + +eof: + diag("truncated object file: %s", pn); +} + +Prog* +prg(void) +{ + Prog *p; + + p = mal(sizeof(Prog)); + *p = zprg; + return p; +} + +Prog* +copyp(Prog *q) +{ + Prog *p; + + p = prg(); + *p = *q; + return p; +} + +Prog* +appendp(Prog *q) +{ + Prog *p; + + p = prg(); + p->link = q->link; + q->link = p; + p->line = q->line; + return p; +} |