diff options
author | Ondřej Surý <ondrej@sury.org> | 2011-09-13 13:13:40 +0200 |
---|---|---|
committer | Ondřej Surý <ondrej@sury.org> | 2011-09-13 13:13:40 +0200 |
commit | 5ff4c17907d5b19510a62e08fd8d3b11e62b431d (patch) | |
tree | c0650497e988f47be9c6f2324fa692a52dea82e1 /src/cmd/ld | |
parent | 80f18fc933cf3f3e829c5455a1023d69f7b86e52 (diff) | |
download | golang-upstream/60.tar.gz |
Imported Upstream version 60upstream/60
Diffstat (limited to 'src/cmd/ld')
-rw-r--r-- | src/cmd/ld/data.c | 1026 | ||||
-rw-r--r-- | src/cmd/ld/doc.go | 11 | ||||
-rw-r--r-- | src/cmd/ld/dwarf.c | 2588 | ||||
-rw-r--r-- | src/cmd/ld/dwarf.h | 30 | ||||
-rw-r--r-- | src/cmd/ld/dwarf_defs.h | 503 | ||||
-rw-r--r-- | src/cmd/ld/elf.c | 564 | ||||
-rw-r--r-- | src/cmd/ld/elf.h | 989 | ||||
-rw-r--r-- | src/cmd/ld/go.c | 869 | ||||
-rw-r--r-- | src/cmd/ld/ldelf.c | 816 | ||||
-rw-r--r-- | src/cmd/ld/ldmacho.c | 821 | ||||
-rw-r--r-- | src/cmd/ld/ldpe.c | 451 | ||||
-rw-r--r-- | src/cmd/ld/lib.c | 1417 | ||||
-rw-r--r-- | src/cmd/ld/lib.h | 308 | ||||
-rw-r--r-- | src/cmd/ld/macho.c | 518 | ||||
-rw-r--r-- | src/cmd/ld/macho.h | 94 | ||||
-rw-r--r-- | src/cmd/ld/pe.c | 593 | ||||
-rw-r--r-- | src/cmd/ld/pe.h | 179 | ||||
-rw-r--r-- | src/cmd/ld/symtab.c | 378 |
18 files changed, 12155 insertions, 0 deletions
diff --git a/src/cmd/ld/data.c b/src/cmd/ld/data.c new file mode 100644 index 000000000..e7269169e --- /dev/null +++ b/src/cmd/ld/data.c @@ -0,0 +1,1026 @@ +// Inferno utils/8l/asm.c +// http://code.google.com/p/inferno-os/source/browse/utils/8l/asm.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. + +// Data layout and relocation. + +#include "l.h" +#include "../ld/lib.h" +#include "../ld/elf.h" +#include "../ld/pe.h" + +void dynreloc(void); +static vlong addaddrplus4(Sym *s, Sym *t, int32 add); + +/* + * divide-and-conquer list-link + * sort of Sym* structures. + * Used for the data block. + */ +int +datcmp(Sym *s1, Sym *s2) +{ + if(s1->type != s2->type) + return (int)s1->type - (int)s2->type; + if(s1->size != s2->size) { + if(s1->size < s2->size) + return -1; + return +1; + } + return strcmp(s1->name, s2->name); +} + +Sym* +datsort(Sym *l) +{ + Sym *l1, *l2, *le; + + if(l == 0 || l->next == 0) + return l; + + l1 = l; + l2 = l; + for(;;) { + l2 = l2->next; + if(l2 == 0) + break; + l2 = l2->next; + if(l2 == 0) + break; + l1 = l1->next; + } + + l2 = l1->next; + l1->next = 0; + l1 = datsort(l); + l2 = datsort(l2); + + /* set up lead element */ + if(datcmp(l1, l2) < 0) { + l = l1; + l1 = l1->next; + } else { + l = l2; + l2 = l2->next; + } + le = l; + + for(;;) { + if(l1 == 0) { + while(l2) { + le->next = l2; + le = l2; + l2 = l2->next; + } + le->next = 0; + break; + } + if(l2 == 0) { + while(l1) { + le->next = l1; + le = l1; + l1 = l1->next; + } + break; + } + if(datcmp(l1, l2) < 0) { + le->next = l1; + le = l1; + l1 = l1->next; + } else { + le->next = l2; + le = l2; + l2 = l2->next; + } + } + le->next = 0; + return l; +} + +Reloc* +addrel(Sym *s) +{ + if(s->nr >= s->maxr) { + if(s->maxr == 0) + s->maxr = 4; + else + s->maxr <<= 1; + s->r = realloc(s->r, s->maxr*sizeof s->r[0]); + if(s->r == 0) { + diag("out of memory"); + errorexit(); + } + memset(s->r+s->nr, 0, (s->maxr-s->nr)*sizeof s->r[0]); + } + return &s->r[s->nr++]; +} + +void +relocsym(Sym *s) +{ + Reloc *r; + Prog p; + int32 i, off, siz, fl; + vlong o; + uchar *cast; + + cursym = s; + memset(&p, 0, sizeof p); + for(r=s->r; r<s->r+s->nr; r++) { + off = r->off; + siz = r->siz; + if(off < 0 || off+(siz&~Rbig) > s->np) { + diag("%s: invalid relocation %d+%d not in [%d,%d)", s->name, off, siz&~Rbig, 0, s->np); + continue; + } + if(r->sym != S && (r->sym->type == 0 || r->sym->type == SXREF)) { + diag("%s: not defined", r->sym->name); + continue; + } + if(r->type >= 256) + continue; + + if(r->sym != S && r->sym->type == SDYNIMPORT) + diag("unhandled relocation for %s (type %d rtype %d)", r->sym->name, r->sym->type, r->type); + + if(r->sym != S && !r->sym->reachable) + diag("unreachable sym in relocation: %s %s", s->name, r->sym->name); + + switch(r->type) { + default: + o = 0; + if(archreloc(r, s, &o) < 0) + diag("unknown reloc %d", r->type); + break; + case D_ADDR: + o = symaddr(r->sym) + r->add; + break; + case D_PCREL: + o = symaddr(r->sym) + r->add - (s->value + r->off + r->siz); + break; + case D_SIZE: + o = r->sym->size + r->add; + break; + } +//print("relocate %s %p %s => %p %p %p %p [%p]\n", s->name, s->value+off, r->sym ? r->sym->name : "<nil>", (void*)symaddr(r->sym), (void*)s->value, (void*)r->off, (void*)r->siz, (void*)o); + switch(siz) { + default: + cursym = s; + diag("bad reloc size %#ux for %s", siz, r->sym->name); + case 4 + Rbig: + fl = o; + s->p[off] = fl>>24; + s->p[off+1] = fl>>16; + s->p[off+2] = fl>>8; + s->p[off+3] = fl; + break; + case 4 + Rlittle: + fl = o; + s->p[off] = fl; + s->p[off+1] = fl>>8; + s->p[off+2] = fl>>16; + s->p[off+3] = fl>>24; + break; + case 4: + fl = o; + cast = (uchar*)&fl; + for(i=0; i<4; i++) + s->p[off+i] = cast[inuxi4[i]]; + break; + case 8: + cast = (uchar*)&o; + for(i=0; i<8; i++) + s->p[off+i] = cast[inuxi8[i]]; + break; + } + } +} + +void +reloc(void) +{ + Sym *s; + + if(debug['v']) + Bprint(&bso, "%5.2f reloc\n", cputime()); + Bflush(&bso); + + for(s=textp; s!=S; s=s->next) + relocsym(s); + for(s=datap; s!=S; s=s->next) + relocsym(s); +} + +void +dynrelocsym(Sym *s) +{ + Reloc *r; + + if(HEADTYPE == Hwindows) { + Sym *rel, *targ; + + rel = lookup(".rel", 0); + if(s == rel) + return; + for(r=s->r; r<s->r+s->nr; r++) { + targ = r->sym; + if(r->sym->plt == -2 && r->sym->got != -2) { // make dynimport JMP table for PE object files. + targ->plt = rel->size; + r->sym = rel; + r->add = targ->plt; + + // jmp *addr + if(thechar == '8') { + adduint8(rel, 0xff); + adduint8(rel, 0x25); + addaddr(rel, targ); + adduint8(rel, 0x90); + adduint8(rel, 0x90); + } else { + adduint8(rel, 0xff); + adduint8(rel, 0x24); + adduint8(rel, 0x25); + addaddrplus4(rel, targ, 0); + adduint8(rel, 0x90); + } + } else if(r->sym->plt >= 0) { + r->sym = rel; + r->add = targ->plt; + } + } + return; + } + + for(r=s->r; r<s->r+s->nr; r++) + if(r->sym->type == SDYNIMPORT || r->type >= 256) + adddynrel(s, r); +} + +void +dynreloc(void) +{ + Sym *s; + + // -d supresses dynamic loader format, so we may as well not + // compute these sections or mark their symbols as reachable. + if(debug['d'] && HEADTYPE != Hwindows) + return; + if(debug['v']) + Bprint(&bso, "%5.2f reloc\n", cputime()); + Bflush(&bso); + + for(s=textp; s!=S; s=s->next) + dynrelocsym(s); + for(s=datap; s!=S; s=s->next) + dynrelocsym(s); + if(iself) + elfdynhash(); +} + +void +symgrow(Sym *s, int32 siz) +{ + if(s->np >= siz) + return; + + if(s->maxp < siz) { + if(s->maxp == 0) + s->maxp = 8; + while(s->maxp < siz) + s->maxp <<= 1; + s->p = realloc(s->p, s->maxp); + if(s->p == nil) { + diag("out of memory"); + errorexit(); + } + memset(s->p+s->np, 0, s->maxp-s->np); + } + s->np = siz; +} + +void +savedata(Sym *s, Prog *p, char *pn) +{ + int32 off, siz, i, fl; + uchar *cast; + vlong o; + Reloc *r; + + off = p->from.offset; + siz = p->datasize; + if(off < 0 || siz < 0 || off >= 1<<30 || siz >= 100) + mangle(pn); + symgrow(s, off+siz); + + switch(p->to.type) { + default: + diag("bad data: %P", p); + break; + + case D_FCONST: + switch(siz) { + default: + case 4: + fl = ieeedtof(&p->to.ieee); + cast = (uchar*)&fl; + for(i=0; i<4; i++) + s->p[off+i] = cast[fnuxi4[i]]; + break; + case 8: + cast = (uchar*)&p->to.ieee; + for(i=0; i<8; i++) + s->p[off+i] = cast[fnuxi8[i]]; + break; + } + break; + + case D_SCONST: + for(i=0; i<siz; i++) + s->p[off+i] = p->to.scon[i]; + break; + + case D_CONST: + if(p->to.sym) + goto Addr; + o = p->to.offset; + fl = o; + cast = (uchar*)&fl; + switch(siz) { + default: + diag("bad nuxi %d\n%P", siz, p); + break; + case 1: + s->p[off] = cast[inuxi1[0]]; + break; + case 2: + for(i=0; i<2; i++) + s->p[off+i] = cast[inuxi2[i]]; + break; + case 4: + for(i=0; i<4; i++) + s->p[off+i] = cast[inuxi4[i]]; + break; + case 8: + cast = (uchar*)&o; + for(i=0; i<8; i++) + s->p[off+i] = cast[inuxi8[i]]; + break; + } + break; + + case D_ADDR: + case D_SIZE: + Addr: + r = addrel(s); + r->off = off; + r->siz = siz; + r->sym = p->to.sym; + r->type = p->to.type; + if(r->type != D_SIZE) + r->type = D_ADDR; + r->add = p->to.offset; + break; + } +} + +static void +blk(Sym *allsym, int32 addr, int32 size) +{ + Sym *sym; + int32 eaddr; + uchar *p, *ep; + + for(sym = allsym; sym != nil; sym = sym->next) + if(!(sym->type&SSUB) && sym->value >= addr) + break; + + eaddr = addr+size; + for(; sym != nil; sym = sym->next) { + if(sym->type&SSUB) + continue; + if(sym->value >= eaddr) + break; + if(sym->value < addr) { + diag("phase error: addr=%#llx but sym=%#llx type=%d", (vlong)addr, (vlong)sym->value, sym->type); + errorexit(); + } + cursym = sym; + for(; addr < sym->value; addr++) + cput(0); + p = sym->p; + ep = p + sym->np; + while(p < ep) + cput(*p++); + addr += sym->np; + for(; addr < sym->value+sym->size; addr++) + cput(0); + if(addr != sym->value+sym->size) { + diag("phase error: addr=%#llx value+size=%#llx", (vlong)addr, (vlong)sym->value+sym->size); + errorexit(); + } + } + + for(; addr < eaddr; addr++) + cput(0); + cflush(); +} + +void +codeblk(int32 addr, int32 size) +{ + Sym *sym; + int32 eaddr, n, epc; + Prog *p; + uchar *q; + + if(debug['a']) + Bprint(&bso, "codeblk [%#x,%#x) at offset %#llx\n", addr, addr+size, cpos()); + + blk(textp, addr, size); + + /* again for printing */ + if(!debug['a']) + return; + + for(sym = textp; sym != nil; sym = sym->next) { + if(!sym->reachable) + continue; + if(sym->value >= addr) + break; + } + + eaddr = addr + size; + for(; sym != nil; sym = sym->next) { + if(!sym->reachable) + continue; + if(sym->value >= eaddr) + break; + + if(addr < sym->value) { + Bprint(&bso, "%-20s %.8llux|", "_", (vlong)addr); + for(; addr < sym->value; addr++) + Bprint(&bso, " %.2ux", 0); + Bprint(&bso, "\n"); + } + p = sym->text; + if(p == nil) { + Bprint(&bso, "%.6llux\t%-20s | foreign text\n", (vlong)addr, sym->name); + n = sym->size; + q = sym->p; + + while(n >= 16) { + Bprint(&bso, "%.6ux\t%-20.16I\n", addr, q); + addr += 16; + q += 16; + n -= 16; + } + if(n > 0) + Bprint(&bso, "%.6ux\t%-20.*I\n", addr, (int)n, q); + addr += n; + continue; + } + + Bprint(&bso, "%.6llux\t%-20s | %P\n", (vlong)sym->value, sym->name, p); + for(p = p->link; p != P; p = p->link) { + if(p->link != P) + epc = p->link->pc; + else + epc = sym->value + sym->size; + Bprint(&bso, "%.6llux\t", (uvlong)p->pc); + q = sym->p + p->pc - sym->value; + n = epc - p->pc; + Bprint(&bso, "%-20.*I | %P\n", (int)n, q, p); + addr += n; + } + } + + if(addr < eaddr) { + Bprint(&bso, "%-20s %.8llux|", "_", (vlong)addr); + for(; addr < eaddr; addr++) + Bprint(&bso, " %.2ux", 0); + } + Bflush(&bso); +} + +void +datblk(int32 addr, int32 size) +{ + Sym *sym; + int32 eaddr; + uchar *p, *ep; + + if(debug['a']) + Bprint(&bso, "datblk [%#x,%#x) at offset %#llx\n", addr, addr+size, cpos()); + + blk(datap, addr, size); + + /* again for printing */ + if(!debug['a']) + return; + + for(sym = datap; sym != nil; sym = sym->next) + if(sym->value >= addr) + break; + + eaddr = addr + size; + for(; sym != nil; sym = sym->next) { + if(sym->value >= eaddr) + break; + if(addr < sym->value) { + Bprint(&bso, "%-20s %.8ux| 00 ...\n", "(pre-pad)", addr); + addr = sym->value; + } + Bprint(&bso, "%-20s %.8ux|", sym->name, (uint)addr); + p = sym->p; + ep = p + sym->np; + while(p < ep) + Bprint(&bso, " %.2ux", *p++); + addr += sym->np; + for(; addr < sym->value+sym->size; addr++) + Bprint(&bso, " %.2ux", 0); + Bprint(&bso, "\n"); + } + + if(addr < eaddr) + Bprint(&bso, "%-20s %.8ux| 00 ...\n", "(post-pad)", (uint)addr); + Bprint(&bso, "%-20s %.8ux|\n", "", (uint)eaddr); +} + +void +strnput(char *s, int n) +{ + for(; *s && n > 0; s++) { + cput(*s); + n--; + } + while(n > 0) { + cput(0); + n--; + } +} + +vlong +addstring(Sym *s, char *str) +{ + int n; + int32 r; + + if(s->type == 0) + s->type = SDATA; + s->reachable = 1; + r = s->size; + n = strlen(str)+1; + if(strcmp(s->name, ".shstrtab") == 0) + elfsetstring(str, r); + symgrow(s, r+n); + memmove(s->p+r, str, n); + s->size += n; + return r; +} + +vlong +adduintxx(Sym *s, uint64 v, int wid) +{ + int32 i, r, fl; + vlong o; + uchar *cast; + + if(s->type == 0) + s->type = SDATA; + s->reachable = 1; + r = s->size; + s->size += wid; + symgrow(s, s->size); + assert(r+wid <= s->size); + fl = v; + cast = (uchar*)&fl; + switch(wid) { + case 1: + s->p[r] = cast[inuxi1[0]]; + break; + case 2: + for(i=0; i<2; i++) + s->p[r+i] = cast[inuxi2[i]]; + break; + case 4: + for(i=0; i<4; i++) + s->p[r+i] = cast[inuxi4[i]]; + break; + case 8: + o = v; + cast = (uchar*)&o; + for(i=0; i<8; i++) + s->p[r+i] = cast[inuxi8[i]]; + break; + } + return r; +} + +vlong +adduint8(Sym *s, uint8 v) +{ + return adduintxx(s, v, 1); +} + +vlong +adduint16(Sym *s, uint16 v) +{ + return adduintxx(s, v, 2); +} + +vlong +adduint32(Sym *s, uint32 v) +{ + return adduintxx(s, v, 4); +} + +vlong +adduint64(Sym *s, uint64 v) +{ + return adduintxx(s, v, 8); +} + +vlong +addaddrplus(Sym *s, Sym *t, int32 add) +{ + vlong i; + Reloc *r; + + if(s->type == 0) + s->type = SDATA; + s->reachable = 1; + i = s->size; + s->size += PtrSize; + symgrow(s, s->size); + r = addrel(s); + r->sym = t; + r->off = i; + r->siz = PtrSize; + r->type = D_ADDR; + r->add = add; + return i; +} + +static vlong +addaddrplus4(Sym *s, Sym *t, int32 add) +{ + vlong i; + Reloc *r; + + if(s->type == 0) + s->type = SDATA; + s->reachable = 1; + i = s->size; + s->size += 4; + symgrow(s, s->size); + r = addrel(s); + r->sym = t; + r->off = i; + r->siz = 4; + r->type = D_ADDR; + r->add = add; + return i; +} + +vlong +addpcrelplus(Sym *s, Sym *t, int32 add) +{ + vlong i; + Reloc *r; + + if(s->type == 0) + s->type = SDATA; + s->reachable = 1; + i = s->size; + s->size += 4; + symgrow(s, s->size); + r = addrel(s); + r->sym = t; + r->off = i; + r->add = add; + r->type = D_PCREL; + r->siz = 4; + return i; +} + +vlong +addaddr(Sym *s, Sym *t) +{ + return addaddrplus(s, t, 0); +} + +vlong +addsize(Sym *s, Sym *t) +{ + vlong i; + Reloc *r; + + if(s->type == 0) + s->type = SDATA; + s->reachable = 1; + i = s->size; + s->size += PtrSize; + symgrow(s, s->size); + r = addrel(s); + r->sym = t; + r->off = i; + r->siz = PtrSize; + r->type = D_SIZE; + return i; +} + +void +dodata(void) +{ + int32 t, datsize; + Section *sect; + Sym *s, *last, **l; + + if(debug['v']) + Bprint(&bso, "%5.2f dodata\n", cputime()); + Bflush(&bso); + + last = nil; + datap = nil; + + for(s=allsym; s!=S; s=s->allsym) { + if(!s->reachable || s->special) + continue; + if(STEXT < s->type && s->type < SXREF) { + if(last == nil) + datap = s; + else + last->next = s; + s->next = nil; + last = s; + } + } + + for(s = datap; s != nil; s = s->next) { + if(s->np > 0 && s->type == SBSS) + s->type = SDATA; + if(s->np > s->size) + diag("%s: initialize bounds (%lld < %d)", + s->name, (vlong)s->size, s->np); + } + + /* + * now that we have the datap list, but before we start + * to assign addresses, record all the necessary + * dynamic relocations. these will grow the relocation + * symbol, which is itself data. + */ + dynreloc(); + + /* some symbols may no longer belong in datap (Mach-O) */ + for(l=&datap; (s=*l) != nil; ) { + if(s->type <= STEXT || SXREF <= s->type) + *l = s->next; + else + l = &s->next; + } + *l = nil; + + datap = datsort(datap); + + /* + * allocate data sections. list is sorted by type, + * so we can just walk it for each piece we want to emit. + */ + + /* read-only data */ + sect = addsection(&segtext, ".rodata", 04); + sect->vaddr = 0; + datsize = 0; + s = datap; + for(; s != nil && s->type < SSYMTAB; s = s->next) { + s->type = SRODATA; + s->value = datsize; + datsize += rnd(s->size, PtrSize); + } + sect->len = datsize - sect->vaddr; + + /* gosymtab */ + sect = addsection(&segtext, ".gosymtab", 04); + sect->vaddr = datsize; + for(; s != nil && s->type < SPCLNTAB; s = s->next) { + s->type = SRODATA; + s->value = datsize; + datsize += s->size; + } + sect->len = datsize - sect->vaddr; + datsize = rnd(datsize, PtrSize); + + /* gopclntab */ + sect = addsection(&segtext, ".gopclntab", 04); + sect->vaddr = datsize; + for(; s != nil && s->type < SELFROSECT; s = s->next) { + s->type = SRODATA; + s->value = datsize; + datsize += s->size; + } + sect->len = datsize - sect->vaddr; + datsize = rnd(datsize, PtrSize); + + /* read-only ELF sections */ + for(; s != nil && s->type < SELFSECT; s = s->next) { + sect = addsection(&segtext, s->name, 04); + sect->vaddr = datsize; + s->type = SRODATA; + s->value = datsize; + datsize += rnd(s->size, PtrSize); + sect->len = datsize - sect->vaddr; + } + + /* writable ELF sections */ + datsize = 0; + for(; s != nil && s->type < SDATA; s = s->next) { + sect = addsection(&segdata, s->name, 06); + sect->vaddr = datsize; + s->type = SDATA; + s->value = datsize; + datsize += rnd(s->size, PtrSize); + sect->len = datsize - sect->vaddr; + } + + /* data */ + sect = addsection(&segdata, ".data", 06); + sect->vaddr = datsize; + for(; s != nil && s->type < SBSS; s = s->next) { + s->type = SDATA; + t = s->size; + if(t == 0 && s->name[0] != '.') { + diag("%s: no size", s->name); + t = 1; + } + if(t >= PtrSize) + t = rnd(t, PtrSize); + else if(t > 2) + t = rnd(t, 4); + if(t & 1) { + ; + } else if(t & 2) + datsize = rnd(datsize, 2); + else if(t & 4) + datsize = rnd(datsize, 4); + else + datsize = rnd(datsize, 8); + s->value = datsize; + datsize += t; + } + sect->len = datsize - sect->vaddr; + + /* bss */ + sect = addsection(&segdata, ".bss", 06); + sect->vaddr = datsize; + for(; s != nil; s = s->next) { + if(s->type != SBSS) { + cursym = s; + diag("unexpected symbol type %d", s->type); + } + t = s->size; + if(t >= PtrSize) + t = rnd(t, PtrSize); + else if(t > 2) + t = rnd(t, 4); + if(t & 1) { + ; + } else if(t & 2) + datsize = rnd(datsize, 2); + else if(t & 4) + datsize = rnd(datsize, 4); + else + datsize = rnd(datsize, 8); + s->value = datsize; + datsize += t; + } + sect->len = datsize - sect->vaddr; +} + +// assign addresses to text +void +textaddress(void) +{ + uvlong va; + Prog *p; + Section *sect; + Sym *sym, *sub; + + addsection(&segtext, ".text", 05); + + // Assign PCs in text segment. + // Could parallelize, by assigning to text + // and then letting threads copy down, but probably not worth it. + sect = segtext.sect; + va = INITTEXT; + sect->vaddr = va; + for(sym = textp; sym != nil; sym = sym->next) { + if(sym->type & SSUB) + continue; + sym->value = 0; + for(sub = sym; sub != S; sub = sub->sub) { + sub->value += va; + for(p = sub->text; p != P; p = p->link) + p->pc += sub->value; + } + if(sym->size == 0 && sym->sub != S) { + cursym = sym; + } + va += sym->size; + } + sect->len = va - sect->vaddr; +} + +// assign addresses +void +address(void) +{ + Section *s, *text, *data, *rodata, *symtab, *pclntab; + Sym *sym, *sub; + uvlong va; + + va = INITTEXT; + segtext.rwx = 05; + segtext.vaddr = va; + segtext.fileoff = HEADR; + for(s=segtext.sect; s != nil; s=s->next) { + s->vaddr = va; + va += rnd(s->len, PtrSize); + } + segtext.len = va - INITTEXT; + segtext.filelen = segtext.len; + + va = rnd(va, INITRND); + + segdata.rwx = 06; + segdata.vaddr = va; + segdata.fileoff = va - segtext.vaddr + segtext.fileoff; + segdata.filelen = 0; + if(HEADTYPE == Hwindows) + segdata.fileoff = segtext.fileoff + rnd(segtext.len, PEFILEALIGN); + if(HEADTYPE == Hplan9x32) + segdata.fileoff = segtext.fileoff + segtext.filelen; + data = nil; + for(s=segdata.sect; s != nil; s=s->next) { + s->vaddr = va; + va += s->len; + segdata.filelen += s->len; + segdata.len = va - segdata.vaddr; + if(strcmp(s->name, ".data") == 0) + data = s; + } + segdata.filelen -= data->next->len; // deduct .bss + + text = segtext.sect; + rodata = text->next; + symtab = rodata->next; + pclntab = symtab->next; + + for(sym = datap; sym != nil; sym = sym->next) { + cursym = sym; + if(sym->type < SDATA) + sym->value += rodata->vaddr; + else + sym->value += segdata.sect->vaddr; + for(sub = sym->sub; sub != nil; sub = sub->sub) + sub->value += sym->value; + } + + xdefine("text", STEXT, text->vaddr); + xdefine("etext", STEXT, text->vaddr + text->len); + xdefine("rodata", SRODATA, rodata->vaddr); + xdefine("erodata", SRODATA, rodata->vaddr + rodata->len); + xdefine("symtab", SRODATA, symtab->vaddr); + xdefine("esymtab", SRODATA, symtab->vaddr + symtab->len); + xdefine("pclntab", SRODATA, pclntab->vaddr); + xdefine("epclntab", SRODATA, pclntab->vaddr + pclntab->len); + xdefine("data", SBSS, data->vaddr); + xdefine("edata", SBSS, data->vaddr + data->len); + xdefine("end", SBSS, segdata.vaddr + segdata.len); +} diff --git a/src/cmd/ld/doc.go b/src/cmd/ld/doc.go new file mode 100644 index 000000000..972e2a32c --- /dev/null +++ b/src/cmd/ld/doc.go @@ -0,0 +1,11 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* + +This directory contains the portable section of the Plan 9 C linkers. +See ../6l, ../8l, and ../5l for more information. + +*/ +package documentation diff --git a/src/cmd/ld/dwarf.c b/src/cmd/ld/dwarf.c new file mode 100644 index 000000000..d8ca27ace --- /dev/null +++ b/src/cmd/ld/dwarf.c @@ -0,0 +1,2588 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// TODO/NICETOHAVE: +// - eliminate DW_CLS_ if not used +// - package info in compilation units +// - assign global variables and types to their packages +// - gdb uses c syntax, meaning clumsy quoting is needed for go identifiers. eg +// ptype struct '[]uint8' and qualifiers need to be quoted away +// - lexical scoping is lost, so gdb gets confused as to which 'main.i' you mean. +// - file:line info for variables +// - make strings a typedef so prettyprinters can see the underlying string type +// +#include "l.h" +#include "lib.h" +#include "../ld/dwarf.h" +#include "../ld/dwarf_defs.h" +#include "../ld/elf.h" +#include "../ld/macho.h" +#include "../ld/pe.h" + +/* + * Offsets and sizes of the debug_* sections in the cout file. + */ + +static vlong abbrevo; +static vlong abbrevsize; +static vlong lineo; +static vlong linesize; +static vlong infoo; // also the base for DWDie->offs and reference attributes. +static vlong infosize; +static vlong frameo; +static vlong framesize; +static vlong pubnameso; +static vlong pubnamessize; +static vlong pubtypeso; +static vlong pubtypessize; +static vlong arangeso; +static vlong arangessize; +static vlong gdbscripto; +static vlong gdbscriptsize; + +static char gdbscript[1024]; + +/* + * Basic I/O + */ + +static void +addrput(vlong addr) +{ + switch(PtrSize) { + case 4: + LPUT(addr); + break; + case 8: + VPUT(addr); + break; + } +} + +static int +uleb128enc(uvlong v, char* dst) +{ + uint8 c, len; + + len = 0; + do { + c = v & 0x7f; + v >>= 7; + if (v) + c |= 0x80; + if (dst) + *dst++ = c; + len++; + } while (c & 0x80); + return len; +}; + +static int +sleb128enc(vlong v, char *dst) +{ + uint8 c, s, len; + + len = 0; + do { + c = v & 0x7f; + s = v & 0x40; + v >>= 7; + if ((v != -1 || !s) && (v != 0 || s)) + c |= 0x80; + if (dst) + *dst++ = c; + len++; + } while(c & 0x80); + return len; +} + +static void +uleb128put(vlong v) +{ + char buf[10]; + strnput(buf, uleb128enc(v, buf)); +} + +static void +sleb128put(vlong v) +{ + char buf[10]; + strnput(buf, sleb128enc(v, buf)); +} + +/* + * Defining Abbrevs. This is hardcoded, and there will be + * only a handful of them. The DWARF spec places no restriction on + * the ordering of atributes in the Abbrevs and DIEs, and we will + * always write them out in the order of declaration in the abbrev. + * This implementation relies on tag, attr < 127, so they serialize as + * a char. Higher numbered user-defined tags or attributes can be used + * for storing internal data but won't be serialized. + */ +typedef struct DWAttrForm DWAttrForm; +struct DWAttrForm { + uint8 attr; + uint8 form; +}; + +// Index into the abbrevs table below. +// Keep in sync with ispubname() and ispubtype() below. +// ispubtype considers >= NULLTYPE public +enum +{ + DW_ABRV_NULL, + DW_ABRV_COMPUNIT, + DW_ABRV_FUNCTION, + DW_ABRV_VARIABLE, + DW_ABRV_AUTO, + DW_ABRV_PARAM, + DW_ABRV_STRUCTFIELD, + DW_ABRV_FUNCTYPEPARAM, + DW_ABRV_DOTDOTDOT, + DW_ABRV_ARRAYRANGE, + DW_ABRV_NULLTYPE, + DW_ABRV_BASETYPE, + DW_ABRV_ARRAYTYPE, + DW_ABRV_CHANTYPE, + DW_ABRV_FUNCTYPE, + DW_ABRV_IFACETYPE, + DW_ABRV_MAPTYPE, + DW_ABRV_PTRTYPE, + DW_ABRV_SLICETYPE, + DW_ABRV_STRINGTYPE, + DW_ABRV_STRUCTTYPE, + DW_ABRV_TYPEDECL, + DW_NABRV +}; + +typedef struct DWAbbrev DWAbbrev; +static struct DWAbbrev { + uint8 tag; + uint8 children; + DWAttrForm attr[30]; +} abbrevs[DW_NABRV] = { + /* The mandatory DW_ABRV_NULL entry. */ + { 0 }, + /* COMPUNIT */ + { + DW_TAG_compile_unit, DW_CHILDREN_yes, + DW_AT_name, DW_FORM_string, + DW_AT_language, DW_FORM_data1, + DW_AT_low_pc, DW_FORM_addr, + DW_AT_high_pc, DW_FORM_addr, + DW_AT_stmt_list, DW_FORM_data4, + 0, 0 + }, + /* FUNCTION */ + { + DW_TAG_subprogram, DW_CHILDREN_yes, + DW_AT_name, DW_FORM_string, + DW_AT_low_pc, DW_FORM_addr, + DW_AT_high_pc, DW_FORM_addr, + DW_AT_external, DW_FORM_flag, + 0, 0 + }, + /* VARIABLE */ + { + DW_TAG_variable, DW_CHILDREN_no, + DW_AT_name, DW_FORM_string, + DW_AT_location, DW_FORM_block1, + DW_AT_type, DW_FORM_ref_addr, + DW_AT_external, DW_FORM_flag, + 0, 0 + }, + /* AUTO */ + { + DW_TAG_variable, DW_CHILDREN_no, + DW_AT_name, DW_FORM_string, + DW_AT_location, DW_FORM_block1, + DW_AT_type, DW_FORM_ref_addr, + 0, 0 + }, + /* PARAM */ + { + DW_TAG_formal_parameter, DW_CHILDREN_no, + DW_AT_name, DW_FORM_string, + DW_AT_location, DW_FORM_block1, + DW_AT_type, DW_FORM_ref_addr, + 0, 0 + }, + /* STRUCTFIELD */ + { + DW_TAG_member, DW_CHILDREN_no, + DW_AT_name, DW_FORM_string, + DW_AT_data_member_location, DW_FORM_block1, + DW_AT_type, DW_FORM_ref_addr, + 0, 0 + }, + /* FUNCTYPEPARAM */ + { + DW_TAG_formal_parameter, DW_CHILDREN_no, + // No name! + DW_AT_type, DW_FORM_ref_addr, + 0, 0 + }, + + /* DOTDOTDOT */ + { + DW_TAG_unspecified_parameters, DW_CHILDREN_no, + 0, 0 + }, + /* ARRAYRANGE */ + { + DW_TAG_subrange_type, DW_CHILDREN_no, + // No name! + DW_AT_type, DW_FORM_ref_addr, + DW_AT_upper_bound, DW_FORM_data1, + 0, 0 + }, + + // Below here are the types considered public by ispubtype + /* NULLTYPE */ + { + DW_TAG_unspecified_type, DW_CHILDREN_no, + DW_AT_name, DW_FORM_string, + 0, 0 + }, + /* BASETYPE */ + { + DW_TAG_base_type, DW_CHILDREN_no, + DW_AT_name, DW_FORM_string, + DW_AT_encoding, DW_FORM_data1, + DW_AT_byte_size, DW_FORM_data1, + 0, 0 + }, + /* ARRAYTYPE */ + // child is subrange with upper bound + { + DW_TAG_array_type, DW_CHILDREN_yes, + DW_AT_name, DW_FORM_string, + DW_AT_type, DW_FORM_ref_addr, + DW_AT_byte_size, DW_FORM_udata, + 0, 0 + }, + + /* CHANTYPE */ + { + DW_TAG_typedef, DW_CHILDREN_no, + DW_AT_name, DW_FORM_string, + DW_AT_type, DW_FORM_ref_addr, + 0, 0 + }, + + /* FUNCTYPE */ + { + DW_TAG_subroutine_type, DW_CHILDREN_yes, + DW_AT_name, DW_FORM_string, +// DW_AT_type, DW_FORM_ref_addr, + 0, 0 + }, + + /* IFACETYPE */ + { + DW_TAG_typedef, DW_CHILDREN_yes, + DW_AT_name, DW_FORM_string, + DW_AT_type, DW_FORM_ref_addr, + 0, 0 + }, + + /* MAPTYPE */ + { + DW_TAG_typedef, DW_CHILDREN_no, + DW_AT_name, DW_FORM_string, + DW_AT_type, DW_FORM_ref_addr, + 0, 0 + }, + + /* PTRTYPE */ + { + DW_TAG_pointer_type, DW_CHILDREN_no, + DW_AT_name, DW_FORM_string, + DW_AT_type, DW_FORM_ref_addr, + 0, 0 + }, + + /* SLICETYPE */ + { + DW_TAG_structure_type, DW_CHILDREN_yes, + DW_AT_name, DW_FORM_string, + DW_AT_byte_size, DW_FORM_udata, + 0, 0 + }, + + /* STRINGTYPE */ + { + DW_TAG_structure_type, DW_CHILDREN_yes, + DW_AT_name, DW_FORM_string, + DW_AT_byte_size, DW_FORM_udata, + 0, 0 + }, + + /* STRUCTTYPE */ + { + DW_TAG_structure_type, DW_CHILDREN_yes, + DW_AT_name, DW_FORM_string, + DW_AT_byte_size, DW_FORM_udata, + 0, 0 + }, + + /* TYPEDECL */ + { + DW_TAG_typedef, DW_CHILDREN_no, + DW_AT_name, DW_FORM_string, + DW_AT_type, DW_FORM_ref_addr, + 0, 0 + }, +}; + +static void +writeabbrev(void) +{ + int i, n; + + abbrevo = cpos(); + for (i = 1; i < DW_NABRV; i++) { + // See section 7.5.3 + uleb128put(i); + uleb128put(abbrevs[i].tag); + cput(abbrevs[i].children); + // 0 is not a valid attr or form, and DWAbbrev.attr is + // 0-terminated, so we can treat it as a string + n = strlen((char*)abbrevs[i].attr) / 2; + strnput((char*)abbrevs[i].attr, + (n+1) * sizeof(DWAttrForm)); + } + cput(0); + abbrevsize = cpos() - abbrevo; +} + +/* + * Debugging Information Entries and their attributes. + */ + +enum +{ + HASHSIZE = 107 +}; + +static uint32 +hashstr(char* s) +{ + uint32 h; + + h = 0; + while (*s) + h = h+h+h + *s++; + return h % HASHSIZE; +} + +// For DW_CLS_string and _block, value should contain the length, and +// data the data, for _reference, value is 0 and data is a DWDie* to +// the referenced instance, for all others, value is the whole thing +// and data is null. + +typedef struct DWAttr DWAttr; +struct DWAttr { + DWAttr *link; + uint8 atr; // DW_AT_ + uint8 cls; // DW_CLS_ + vlong value; + char *data; +}; + +typedef struct DWDie DWDie; +struct DWDie { + int abbrev; + DWDie *link; + DWDie *child; + DWAttr *attr; + // offset into .debug_info section, i.e relative to + // infoo. only valid after call to putdie() + vlong offs; + DWDie **hash; // optional index of children by name, enabled by mkindex() + DWDie *hlink; // bucket chain in parent's index +}; + +/* + * Root DIEs for compilation units, types and global variables. + */ + +static DWDie dwroot; +static DWDie dwtypes; +static DWDie dwglobals; + +static DWAttr* +newattr(DWDie *die, uint8 attr, int cls, vlong value, char *data) +{ + DWAttr *a; + + a = mal(sizeof *a); + a->link = die->attr; + die->attr = a; + a->atr = attr; + a->cls = cls; + a->value = value; + a->data = data; + return a; +} + +// Each DIE (except the root ones) has at least 1 attribute: its +// name. getattr moves the desired one to the front so +// frequently searched ones are found faster. +static DWAttr* +getattr(DWDie *die, uint8 attr) +{ + DWAttr *a, *b; + + if (die->attr->atr == attr) + return die->attr; + + a = die->attr; + b = a->link; + while (b != nil) { + if (b->atr == attr) { + a->link = b->link; + b->link = die->attr; + die->attr = b; + return b; + } + a = b; + b = b->link; + } + return nil; +} + +// Every DIE has at least a DW_AT_name attribute (but it will only be +// written out if it is listed in the abbrev). If its parent is +// keeping an index, the new DIE will be inserted there. +static DWDie* +newdie(DWDie *parent, int abbrev, char *name) +{ + DWDie *die; + int h; + + die = mal(sizeof *die); + die->abbrev = abbrev; + die->link = parent->child; + parent->child = die; + + newattr(die, DW_AT_name, DW_CLS_STRING, strlen(name), name); + + if (parent->hash) { + h = hashstr(name); + die->hlink = parent->hash[h]; + parent->hash[h] = die; + } + + return die; +} + +static void +mkindex(DWDie *die) +{ + die->hash = mal(HASHSIZE * sizeof(DWDie*)); +} + +// Find child by AT_name using hashtable if available or linear scan +// if not. +static DWDie* +find(DWDie *die, char* name) +{ + DWDie *a, *b; + int h; + + if (die->hash == nil) { + for (a = die->child; a != nil; a = a->link) + if (strcmp(name, getattr(a, DW_AT_name)->data) == 0) + return a; + return nil; + } + + h = hashstr(name); + a = die->hash[h]; + + if (a == nil) + return nil; + + + if (strcmp(name, getattr(a, DW_AT_name)->data) == 0) + return a; + + // Move found ones to head of the list. + b = a->hlink; + while (b != nil) { + if (strcmp(name, getattr(b, DW_AT_name)->data) == 0) { + a->hlink = b->hlink; + b->hlink = die->hash[h]; + die->hash[h] = b; + return b; + } + a = b; + b = b->hlink; + } + return nil; +} + +static DWDie* +find_or_diag(DWDie *die, char* name) +{ + DWDie *r; + r = find(die, name); + if (r == nil) { + diag("dwarf find: %s has no %s", getattr(die, DW_AT_name)->data, name); + errorexit(); + } + return r; +} + +static DWAttr* +newrefattr(DWDie *die, uint8 attr, DWDie* ref) +{ + if (ref == nil) + return nil; + return newattr(die, attr, DW_CLS_REFERENCE, 0, (char*)ref); +} + +static int fwdcount; + +static void +putattr(int form, int cls, vlong value, char *data) +{ + switch(form) { + case DW_FORM_addr: // address + addrput(value); + break; + + case DW_FORM_block1: // block + value &= 0xff; + cput(value); + while(value--) + cput(*data++); + break; + + case DW_FORM_block2: // block + value &= 0xffff; + WPUT(value); + while(value--) + cput(*data++); + break; + + case DW_FORM_block4: // block + value &= 0xffffffff; + LPUT(value); + while(value--) + cput(*data++); + break; + + case DW_FORM_block: // block + uleb128put(value); + while(value--) + cput(*data++); + break; + + case DW_FORM_data1: // constant + cput(value); + break; + + case DW_FORM_data2: // constant + WPUT(value); + break; + + case DW_FORM_data4: // constant, {line,loclist,mac,rangelist}ptr + LPUT(value); + break; + + case DW_FORM_data8: // constant, {line,loclist,mac,rangelist}ptr + VPUT(value); + break; + + case DW_FORM_sdata: // constant + sleb128put(value); + break; + + case DW_FORM_udata: // constant + uleb128put(value); + break; + + case DW_FORM_string: // string + strnput(data, value+1); + break; + + case DW_FORM_flag: // flag + cput(value?1:0); + break; + + case DW_FORM_ref_addr: // reference to a DIE in the .info section + if (data == nil) { + diag("dwarf: null reference"); + LPUT(0); // invalid dwarf, gdb will complain. + } else { + if (((DWDie*)data)->offs == 0) + fwdcount++; + LPUT(((DWDie*)data)->offs); + } + break; + + case DW_FORM_ref1: // reference within the compilation unit + case DW_FORM_ref2: // reference + case DW_FORM_ref4: // reference + case DW_FORM_ref8: // reference + case DW_FORM_ref_udata: // reference + + case DW_FORM_strp: // string + case DW_FORM_indirect: // (see Section 7.5.3) + default: + diag("dwarf: unsupported attribute form %d / class %d", form, cls); + errorexit(); + } +} + +// Note that we can (and do) add arbitrary attributes to a DIE, but +// only the ones actually listed in the Abbrev will be written out. +static void +putattrs(int abbrev, DWAttr* attr) +{ + DWAttr *attrs[DW_AT_recursive + 1]; + DWAttrForm* af; + + memset(attrs, 0, sizeof attrs); + for( ; attr; attr = attr->link) + if (attr->atr < nelem(attrs)) + attrs[attr->atr] = attr; + + for(af = abbrevs[abbrev].attr; af->attr; af++) + if (attrs[af->attr]) + putattr(af->form, + attrs[af->attr]->cls, + attrs[af->attr]->value, + attrs[af->attr]->data); + else + putattr(af->form, 0, 0, 0); +} + +static void putdie(DWDie* die); + +static void +putdies(DWDie* die) +{ + for(; die; die = die->link) + putdie(die); +} + +static void +putdie(DWDie* die) +{ + die->offs = cpos() - infoo; + uleb128put(die->abbrev); + putattrs(die->abbrev, die->attr); + if (abbrevs[die->abbrev].children) { + putdies(die->child); + cput(0); + } +} + +static void +reverselist(DWDie** list) +{ + DWDie *curr, *prev; + + curr = *list; + prev = nil; + while(curr != nil) { + DWDie* next = curr->link; + curr->link = prev; + prev = curr; + curr = next; + } + *list = prev; +} + +static void +reversetree(DWDie** list) +{ + DWDie *die; + + reverselist(list); + for (die = *list; die != nil; die = die->link) + if (abbrevs[die->abbrev].children) + reversetree(&die->child); +} + +static void +newmemberoffsetattr(DWDie *die, int32 offs) +{ + char block[10]; + int i; + + i = 0; + if (offs != 0) { + block[i++] = DW_OP_consts; + i += sleb128enc(offs, block+i); + block[i++] = DW_OP_plus; + } + newattr(die, DW_AT_data_member_location, DW_CLS_BLOCK, i, mal(i)); + memmove(die->attr->data, block, i); +} + +// GDB doesn't like DW_FORM_addr for DW_AT_location, so emit a +// location expression that evals to a const. +static void +newabslocexprattr(DWDie *die, vlong addr) +{ + char block[10]; + int i; + + i = 0; + block[i++] = DW_OP_constu; + i += uleb128enc(addr, block+i); + newattr(die, DW_AT_location, DW_CLS_BLOCK, i, mal(i)); + memmove(die->attr->data, block, i); +} + +// Decoding the type.* symbols. This has to be in sync with +// ../../pkg/runtime/type.go, or more specificaly, with what +// ../gc/reflect.c stuffs in these. + +enum { + KindBool = 1, + KindInt, + KindInt8, + KindInt16, + KindInt32, + KindInt64, + KindUint, + KindUint8, + KindUint16, + KindUint32, + KindUint64, + KindUintptr, + KindFloat32, + KindFloat64, + KindComplex64, + KindComplex128, + KindArray, + KindChan, + KindFunc, + KindInterface, + KindMap, + KindPtr, + KindSlice, + KindString, + KindStruct, + KindUnsafePointer, + + KindNoPointers = 1<<7, + + // size of Type interface header + CommonType structure. + CommonSize = 2*PtrSize+ 4*PtrSize + 8, +}; + +static Reloc* +decode_reloc(Sym *s, int32 off) +{ + int i; + + for (i = 0; i < s->nr; i++) + if (s->r[i].off == off) + return s->r + i; + return nil; +} + +static Sym* +decode_reloc_sym(Sym *s, int32 off) +{ + Reloc *r; + + r = decode_reloc(s,off); + if (r == nil) + return nil; + return r->sym; +} + +static uvlong +decode_inuxi(uchar* p, int sz) +{ + uint64 v; + uint32 l; + uchar *cast, *inuxi; + int i; + + v = l = 0; + cast = nil; + inuxi = nil; + switch (sz) { + case 2: + cast = (uchar*)&l; + inuxi = inuxi2; + break; + case 4: + cast = (uchar*)&l; + inuxi = inuxi4; + break; + case 8: + cast = (uchar*)&v; + inuxi = inuxi8; + break; + default: + diag("dwarf: decode inuxi %d", sz); + errorexit(); + } + for (i = 0; i < sz; i++) + cast[inuxi[i]] = p[i]; + if (sz == 8) + return v; + return l; +} + +// Type.commonType.kind +static uint8 +decodetype_kind(Sym *s) +{ + return s->p[3*PtrSize + 7] & ~KindNoPointers; // 0x13 / 0x1f +} + +// Type.commonType.size +static vlong +decodetype_size(Sym *s) +{ + return decode_inuxi(s->p + 2*PtrSize, PtrSize); // 0x8 / 0x10 +} + +// Type.ArrayType.elem and Type.SliceType.Elem +static Sym* +decodetype_arrayelem(Sym *s) +{ + return decode_reloc_sym(s, CommonSize); // 0x1c / 0x30 +} + +static vlong +decodetype_arraylen(Sym *s) +{ + return decode_inuxi(s->p + CommonSize+PtrSize, PtrSize); +} + +// Type.PtrType.elem +static Sym* +decodetype_ptrelem(Sym *s) +{ + return decode_reloc_sym(s, CommonSize); // 0x1c / 0x30 +} + +// Type.MapType.key, elem +static Sym* +decodetype_mapkey(Sym *s) +{ + return decode_reloc_sym(s, CommonSize); // 0x1c / 0x30 +} +static Sym* +decodetype_mapvalue(Sym *s) +{ + return decode_reloc_sym(s, CommonSize+PtrSize); // 0x20 / 0x38 +} + +// Type.ChanType.elem +static Sym* +decodetype_chanelem(Sym *s) +{ + return decode_reloc_sym(s, CommonSize); // 0x1c / 0x30 +} + +// Type.FuncType.dotdotdot +static int +decodetype_funcdotdotdot(Sym *s) +{ + return s->p[CommonSize]; +} + +// Type.FuncType.in.len +static int +decodetype_funcincount(Sym *s) +{ + return decode_inuxi(s->p + CommonSize+2*PtrSize, 4); +} + +static int +decodetype_funcoutcount(Sym *s) +{ + return decode_inuxi(s->p + CommonSize+3*PtrSize + 2*4, 4); +} + +static Sym* +decodetype_funcintype(Sym *s, int i) +{ + Reloc *r; + + r = decode_reloc(s, CommonSize + PtrSize); + if (r == nil) + return nil; + return decode_reloc_sym(r->sym, r->add + i * PtrSize); +} + +static Sym* +decodetype_funcouttype(Sym *s, int i) +{ + Reloc *r; + + r = decode_reloc(s, CommonSize + 2*PtrSize + 2*4); + if (r == nil) + return nil; + return decode_reloc_sym(r->sym, r->add + i * PtrSize); +} + +// Type.StructType.fields.Slice::len +static int +decodetype_structfieldcount(Sym *s) +{ + return decode_inuxi(s->p + CommonSize + PtrSize, 4); +} + +enum { + StructFieldSize = 5*PtrSize +}; +// Type.StructType.fields[]-> name, typ and offset. +static char* +decodetype_structfieldname(Sym *s, int i) +{ + Reloc *r; + + // go.string."foo" 0x28 / 0x40 + s = decode_reloc_sym(s, CommonSize + PtrSize + 2*4 + i*StructFieldSize); + if (s == nil) // embedded structs have a nil name. + return nil; + r = decode_reloc(s, 0); // s has a pointer to the string data at offset 0 + if (r == nil) // shouldn't happen. + return nil; + return (char*) r->sym->p + r->add; // the c-string +} + +static Sym* +decodetype_structfieldtype(Sym *s, int i) +{ + return decode_reloc_sym(s, CommonSize + PtrSize + 2*4 + i*StructFieldSize + 2*PtrSize); +} + +static vlong +decodetype_structfieldoffs(Sym *s, int i) +{ + return decode_inuxi(s->p + CommonSize + PtrSize + 2*4 + i*StructFieldSize + 4*PtrSize, 4); +} + +// InterfaceTYpe.methods.len +static vlong +decodetype_ifacemethodcount(Sym *s) +{ + return decode_inuxi(s->p + CommonSize + PtrSize, 4); +} + + +// Fake attributes for slices, maps and channel +enum { + DW_AT_internal_elem_type = 250, // channels and slices + DW_AT_internal_key_type = 251, // maps + DW_AT_internal_val_type = 252, // maps + DW_AT_internal_location = 253, // params and locals +}; + +static DWDie* defptrto(DWDie *dwtype); // below + +// Lookup predefined types +static Sym* +lookup_or_diag(char *n) +{ + Sym *s; + + s = rlookup(n, 0); + if (s == nil || s->size == 0) { + diag("dwarf: missing type: %s", n); + errorexit(); + } + return s; +} + +// Define gotype, for composite ones recurse into constituents. +static DWDie* +defgotype(Sym *gotype) +{ + DWDie *die, *fld; + Sym *s; + char *name, *f; + uint8 kind; + vlong bytesize; + int i, nfields; + + if (gotype == nil) + return find_or_diag(&dwtypes, "<unspecified>"); + + if (strncmp("type.", gotype->name, 5) != 0) { + diag("dwarf: type name doesn't start with \".type\": %s", gotype->name); + return find_or_diag(&dwtypes, "<unspecified>"); + } + name = gotype->name + 5; // could also decode from Type.string + + die = find(&dwtypes, name); + if (die != nil) + return die; + + if (0 && debug['v'] > 2) + print("new type: %Y\n", gotype); + + kind = decodetype_kind(gotype); + bytesize = decodetype_size(gotype); + + switch (kind) { + case KindBool: + die = newdie(&dwtypes, DW_ABRV_BASETYPE, name); + newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_boolean, 0); + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); + break; + + case KindInt: + case KindInt8: + case KindInt16: + case KindInt32: + case KindInt64: + die = newdie(&dwtypes, DW_ABRV_BASETYPE, name); + newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_signed, 0); + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); + break; + + case KindUint: + case KindUint8: + case KindUint16: + case KindUint32: + case KindUint64: + case KindUintptr: + die = newdie(&dwtypes, DW_ABRV_BASETYPE, name); + newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_unsigned, 0); + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); + break; + + case KindFloat32: + case KindFloat64: + die = newdie(&dwtypes, DW_ABRV_BASETYPE, name); + newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_float, 0); + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); + break; + + case KindComplex64: + case KindComplex128: + die = newdie(&dwtypes, DW_ABRV_BASETYPE, name); + newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_complex_float, 0); + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); + break; + + case KindArray: + die = newdie(&dwtypes, DW_ABRV_ARRAYTYPE, name); + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); + s = decodetype_arrayelem(gotype); + newrefattr(die, DW_AT_type, defgotype(s)); + fld = newdie(die, DW_ABRV_ARRAYRANGE, "range"); + newattr(fld, DW_AT_upper_bound, DW_CLS_CONSTANT, decodetype_arraylen(gotype), 0); + newrefattr(fld, DW_AT_type, find_or_diag(&dwtypes, "uintptr")); + break; + + case KindChan: + die = newdie(&dwtypes, DW_ABRV_CHANTYPE, name); + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); + s = decodetype_chanelem(gotype); + newrefattr(die, DW_AT_internal_elem_type, defgotype(s)); + break; + + case KindFunc: + die = newdie(&dwtypes, DW_ABRV_FUNCTYPE, name); + newrefattr(die, DW_AT_type, find_or_diag(&dwtypes, "void")); + nfields = decodetype_funcincount(gotype); + for (i = 0; i < nfields; i++) { + s = decodetype_funcintype(gotype, i); + fld = newdie(die, DW_ABRV_FUNCTYPEPARAM, s->name+5); + newrefattr(fld, DW_AT_type, defgotype(s)); + } + if (decodetype_funcdotdotdot(gotype)) + newdie(die, DW_ABRV_DOTDOTDOT, "..."); + nfields = decodetype_funcoutcount(gotype); + for (i = 0; i < nfields; i++) { + s = decodetype_funcouttype(gotype, i); + fld = newdie(die, DW_ABRV_FUNCTYPEPARAM, s->name+5); + newrefattr(fld, DW_AT_type, defptrto(defgotype(s))); + } + break; + + case KindInterface: + die = newdie(&dwtypes, DW_ABRV_IFACETYPE, name); + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); + nfields = decodetype_ifacemethodcount(gotype); + if (nfields == 0) + s = lookup_or_diag("type.runtime.eface"); + else + s = lookup_or_diag("type.runtime.iface"); + newrefattr(die, DW_AT_type, defgotype(s)); + break; + + case KindMap: + die = newdie(&dwtypes, DW_ABRV_MAPTYPE, name); + s = decodetype_mapkey(gotype); + newrefattr(die, DW_AT_internal_key_type, defgotype(s)); + s = decodetype_mapvalue(gotype); + newrefattr(die, DW_AT_internal_val_type, defgotype(s)); + break; + + case KindPtr: + die = newdie(&dwtypes, DW_ABRV_PTRTYPE, name); + s = decodetype_ptrelem(gotype); + newrefattr(die, DW_AT_type, defgotype(s)); + break; + + case KindSlice: + die = newdie(&dwtypes, DW_ABRV_SLICETYPE, name); + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); + s = decodetype_arrayelem(gotype); + newrefattr(die, DW_AT_internal_elem_type, defgotype(s)); + break; + + case KindString: + die = newdie(&dwtypes, DW_ABRV_STRINGTYPE, name); + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); + break; + + case KindStruct: + die = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, name); + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); + nfields = decodetype_structfieldcount(gotype); + for (i = 0; i < nfields; i++) { + f = decodetype_structfieldname(gotype, i); + s = decodetype_structfieldtype(gotype, i); + if (f == nil) + f = s->name + 5; // skip "type." + fld = newdie(die, DW_ABRV_STRUCTFIELD, f); + newrefattr(fld, DW_AT_type, defgotype(s)); + newmemberoffsetattr(fld, decodetype_structfieldoffs(gotype, i)); + } + break; + + case KindUnsafePointer: + die = newdie(&dwtypes, DW_ABRV_PTRTYPE, name); + newrefattr(die, DW_AT_type, find(&dwtypes, "void")); + break; + + default: + diag("dwarf: definition of unknown kind %d: %s", kind, gotype->name); + die = newdie(&dwtypes, DW_ABRV_TYPEDECL, name); + newrefattr(die, DW_AT_type, find_or_diag(&dwtypes, "<unspecified>")); + } + + return die; +} + +// Find or construct *T given T. +static DWDie* +defptrto(DWDie *dwtype) +{ + char ptrname[1024]; + DWDie *die; + + snprint(ptrname, sizeof ptrname, "*%s", getattr(dwtype, DW_AT_name)->data); + die = find(&dwtypes, ptrname); + if (die == nil) { + die = newdie(&dwtypes, DW_ABRV_PTRTYPE, + strcpy(mal(strlen(ptrname)+1), ptrname)); + newrefattr(die, DW_AT_type, dwtype); + } + return die; +} + +// Copies src's children into dst. Copies attributes by value. +// DWAttr.data is copied as pointer only. +static void +copychildren(DWDie *dst, DWDie *src) +{ + DWDie *c; + DWAttr *a; + + for (src = src->child; src != nil; src = src->link) { + c = newdie(dst, src->abbrev, getattr(src, DW_AT_name)->data); + for (a = src->attr; a != nil; a = a->link) + newattr(c, a->atr, a->cls, a->value, a->data); + copychildren(c, src); + } + reverselist(&dst->child); +} + +// Search children (assumed to have DW_TAG_member) for the one named +// field and set its DW_AT_type to dwtype +static void +substitutetype(DWDie *structdie, char *field, DWDie* dwtype) +{ + DWDie *child; + DWAttr *a; + + child = find_or_diag(structdie, field); + if (child == nil) + return; + + a = getattr(child, DW_AT_type); + if (a != nil) + a->data = (char*) dwtype; + else + newrefattr(child, DW_AT_type, dwtype); +} + +static void +synthesizestringtypes(DWDie* die) +{ + DWDie *prototype; + + prototype = defgotype(lookup_or_diag("type.runtime._string")); + if (prototype == nil) + return; + + for (; die != nil; die = die->link) { + if (die->abbrev != DW_ABRV_STRINGTYPE) + continue; + copychildren(die, prototype); + } +} + +static void +synthesizeslicetypes(DWDie *die) +{ + DWDie *prototype, *elem; + + prototype = defgotype(lookup_or_diag("type.runtime.slice")); + if (prototype == nil) + return; + + for (; die != nil; die = die->link) { + if (die->abbrev != DW_ABRV_SLICETYPE) + continue; + copychildren(die, prototype); + elem = (DWDie*) getattr(die, DW_AT_internal_elem_type)->data; + substitutetype(die, "array", defptrto(elem)); + } +} + +static char* +mkinternaltypename(char *base, char *arg1, char *arg2) +{ + char buf[1024]; + char *n; + + if (arg2 == nil) + snprint(buf, sizeof buf, "%s<%s>", base, arg1); + else + snprint(buf, sizeof buf, "%s<%s,%s>", base, arg1, arg2); + n = mal(strlen(buf) + 1); + memmove(n, buf, strlen(buf)); + return n; +} + + +// synthesizemaptypes is way too closely married to runtime/hashmap.c +enum { + MaxValsize = 256 - 64 +}; + +static void +synthesizemaptypes(DWDie *die) +{ + + DWDie *hash, *hash_subtable, *hash_entry, + *dwh, *dwhs, *dwhe, *dwhash, *keytype, *valtype, *fld; + int hashsize, keysize, valsize, datsize, valsize_in_hash, datavo; + DWAttr *a; + + hash = defgotype(lookup_or_diag("type.runtime.hmap")); + hash_subtable = defgotype(lookup_or_diag("type.runtime.hash_subtable")); + hash_entry = defgotype(lookup_or_diag("type.runtime.hash_entry")); + + if (hash == nil || hash_subtable == nil || hash_entry == nil) + return; + + dwhash = (DWDie*)getattr(find_or_diag(hash_entry, "hash"), DW_AT_type)->data; + if (dwhash == nil) + return; + + hashsize = getattr(dwhash, DW_AT_byte_size)->value; + + for (; die != nil; die = die->link) { + if (die->abbrev != DW_ABRV_MAPTYPE) + continue; + + keytype = (DWDie*) getattr(die, DW_AT_internal_key_type)->data; + valtype = (DWDie*) getattr(die, DW_AT_internal_val_type)->data; + + a = getattr(keytype, DW_AT_byte_size); + keysize = a ? a->value : PtrSize; // We don't store size with Pointers + + a = getattr(valtype, DW_AT_byte_size); + valsize = a ? a->value : PtrSize; + + // This is what happens in hash_init and makemap_c + valsize_in_hash = valsize; + if (valsize > MaxValsize) + valsize_in_hash = PtrSize; + datavo = keysize; + if (valsize_in_hash >= PtrSize) + datavo = rnd(keysize, PtrSize); + datsize = datavo + valsize_in_hash; + if (datsize < PtrSize) + datsize = PtrSize; + datsize = rnd(datsize, PtrSize); + + // Construct struct hash_entry<K,V> + dwhe = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, + mkinternaltypename("hash_entry", + getattr(keytype, DW_AT_name)->data, + getattr(valtype, DW_AT_name)->data)); + + fld = newdie(dwhe, DW_ABRV_STRUCTFIELD, "hash"); + newrefattr(fld, DW_AT_type, dwhash); + newmemberoffsetattr(fld, 0); + + fld = newdie(dwhe, DW_ABRV_STRUCTFIELD, "key"); + newrefattr(fld, DW_AT_type, keytype); + newmemberoffsetattr(fld, hashsize); + + fld = newdie(dwhe, DW_ABRV_STRUCTFIELD, "val"); + if (valsize > MaxValsize) + valtype = defptrto(valtype); + newrefattr(fld, DW_AT_type, valtype); + newmemberoffsetattr(fld, hashsize + datavo); + newattr(dwhe, DW_AT_byte_size, DW_CLS_CONSTANT, hashsize + datsize, nil); + + // Construct hash_subtable<hash_entry<K,V>> + dwhs = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, + mkinternaltypename("hash_subtable", + getattr(keytype, DW_AT_name)->data, + getattr(valtype, DW_AT_name)->data)); + copychildren(dwhs, hash_subtable); + substitutetype(dwhs, "end", defptrto(dwhe)); + substitutetype(dwhs, "entry", dwhe); // todo: []hash_entry with dynamic size + newattr(dwhs, DW_AT_byte_size, DW_CLS_CONSTANT, + getattr(hash_subtable, DW_AT_byte_size)->value, nil); + + // Construct hash<K,V> + dwh = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, + mkinternaltypename("hash", + getattr(keytype, DW_AT_name)->data, + getattr(valtype, DW_AT_name)->data)); + copychildren(dwh, hash); + substitutetype(dwh, "st", defptrto(dwhs)); + newattr(dwh, DW_AT_byte_size, DW_CLS_CONSTANT, + getattr(hash, DW_AT_byte_size)->value, nil); + + newrefattr(die, DW_AT_type, defptrto(dwh)); + } +} + +static void +synthesizechantypes(DWDie *die) +{ + DWDie *sudog, *waitq, *hchan, + *dws, *dww, *dwh, *elemtype; + DWAttr *a; + int elemsize, sudogsize; + + sudog = defgotype(lookup_or_diag("type.runtime.sudog")); + waitq = defgotype(lookup_or_diag("type.runtime.waitq")); + hchan = defgotype(lookup_or_diag("type.runtime.hchan")); + if (sudog == nil || waitq == nil || hchan == nil) + return; + + sudogsize = getattr(sudog, DW_AT_byte_size)->value; + + for (; die != nil; die = die->link) { + if (die->abbrev != DW_ABRV_CHANTYPE) + continue; + elemtype = (DWDie*) getattr(die, DW_AT_internal_elem_type)->data; + a = getattr(elemtype, DW_AT_byte_size); + elemsize = a ? a->value : PtrSize; + + // sudog<T> + dws = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, + mkinternaltypename("sudog", + getattr(elemtype, DW_AT_name)->data, nil)); + copychildren(dws, sudog); + substitutetype(dws, "elem", elemtype); + newattr(dws, DW_AT_byte_size, DW_CLS_CONSTANT, + sudogsize + (elemsize > 8 ? elemsize - 8 : 0), nil); + + // waitq<T> + dww = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, + mkinternaltypename("waitq", getattr(elemtype, DW_AT_name)->data, nil)); + copychildren(dww, waitq); + substitutetype(dww, "first", defptrto(dws)); + substitutetype(dww, "last", defptrto(dws)); + newattr(dww, DW_AT_byte_size, DW_CLS_CONSTANT, + getattr(waitq, DW_AT_byte_size)->value, nil); + + // hchan<T> + dwh = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, + mkinternaltypename("hchan", getattr(elemtype, DW_AT_name)->data, nil)); + copychildren(dwh, hchan); + substitutetype(dwh, "recvq", dww); + substitutetype(dwh, "sendq", dww); + newattr(dwh, DW_AT_byte_size, DW_CLS_CONSTANT, + getattr(hchan, DW_AT_byte_size)->value, nil); + + newrefattr(die, DW_AT_type, defptrto(dwh)); + } +} + +// For use with pass.c::genasmsym +static void +defdwsymb(Sym* sym, char *s, int t, vlong v, vlong size, int ver, Sym *gotype) +{ + DWDie *dv, *dt; + + USED(size); + if (strncmp(s, "go.string.", 10) == 0) + return; + + if (strncmp(s, "type.", 5) == 0 && strcmp(s, "type.*") != 0) { + defgotype(sym); + return; + } + + dv = nil; + + switch (t) { + default: + return; + case 'd': + case 'b': + case 'D': + case 'B': + dv = newdie(&dwglobals, DW_ABRV_VARIABLE, s); + newabslocexprattr(dv, v); + if (ver == 0) + newattr(dv, DW_AT_external, DW_CLS_FLAG, 1, 0); + // fallthrough + case 'a': + case 'p': + dt = defgotype(gotype); + } + + if (dv != nil) + newrefattr(dv, DW_AT_type, dt); +} + +// TODO(lvd) For now, just append them all to the first compilation +// unit (that should be main), in the future distribute them to the +// appropriate compilation units. +static void +movetomodule(DWDie *parent) +{ + DWDie *die; + + for (die = dwroot.child->child; die->link != nil; die = die->link) /* nix */; + die->link = parent->child; +} + +/* + * Filename fragments for the line history stack. + */ + +static char **ftab; +static int ftabsize; + +void +dwarfaddfrag(int n, char *frag) +{ + int s; + + if (n >= ftabsize) { + s = ftabsize; + ftabsize = 1 + n + (n >> 2); + ftab = realloc(ftab, ftabsize * sizeof(ftab[0])); + memset(ftab + s, 0, (ftabsize - s) * sizeof(ftab[0])); + } + + if (*frag == '<') + frag++; + ftab[n] = frag; +} + +// Returns a malloc'ed string, piecewise copied from the ftab. +static char * +decodez(char *s) +{ + int len, o; + char *ss, *f; + char *r, *rb, *re; + + len = 0; + ss = s + 1; // first is 0 + while((o = ((uint8)ss[0] << 8) | (uint8)ss[1]) != 0) { + if (o < 0 || o >= ftabsize) { + diag("dwarf: corrupt z entry"); + return 0; + } + f = ftab[o]; + if (f == nil) { + diag("dwarf: corrupt z entry"); + return 0; + } + len += strlen(f) + 1; // for the '/' + ss += 2; + } + + if (len == 0) + return 0; + + r = malloc(len + 1); + rb = r; + re = rb + len + 1; + + s++; + while((o = ((uint8)s[0] << 8) | (uint8)s[1]) != 0) { + f = ftab[o]; + if (rb == r || rb[-1] == '/') + rb = seprint(rb, re, "%s", f); + else + rb = seprint(rb, re, "/%s", f); + s += 2; + } + return r; +} + +/* + * The line history itself + */ + +static char **histfile; // [0] holds "<eof>", DW_LNS_set_file arguments must be > 0. +static int histfilesize; +static int histfilecap; + +static void +clearhistfile(void) +{ + int i; + + // [0] holds "<eof>" + for (i = 1; i < histfilesize; i++) + free(histfile[i]); + histfilesize = 0; +} + +static int +addhistfile(char *zentry) +{ + char *fname; + + if (histfilesize == histfilecap) { + histfilecap = 2 * histfilecap + 2; + histfile = realloc(histfile, histfilecap * sizeof(char*)); + } + if (histfilesize == 0) + histfile[histfilesize++] = "<eof>"; + + fname = decodez(zentry); + if (fname == 0) + return -1; + // Don't fill with duplicates (check only top one). + if (strcmp(fname, histfile[histfilesize-1]) == 0) { + free(fname); + return histfilesize - 1; + } + histfile[histfilesize++] = fname; + return histfilesize - 1; +} + +// if the histfile stack contains ..../runtime/runtime_defs.go +// use that to set gdbscript +static void +finddebugruntimepath(void) +{ + int i, l; + char *c; + + for (i = 1; i < histfilesize; i++) { + if ((c = strstr(histfile[i], "runtime/runtime_defs.go")) != nil) { + l = c - histfile[i]; + memmove(gdbscript, histfile[i], l); + memmove(gdbscript + l, "runtime/runtime-gdb.py", strlen("runtime/runtime-gdb.py") + 1); + break; + } + } +} + +// Go's runtime C sources are sane, and Go sources nest only 1 level, +// so 16 should be plenty. +static struct { + int file; + vlong line; +} includestack[16]; +static int includetop; +static vlong absline; + +typedef struct Linehist Linehist; +struct Linehist { + Linehist *link; + vlong absline; + vlong line; + int file; +}; + +static Linehist *linehist; + +static void +checknesting(void) +{ + int i; + + if (includetop < 0) { + diag("dwarf: corrupt z stack"); + errorexit(); + } + if (includetop >= nelem(includestack)) { + diag("dwarf: nesting too deep"); + for (i = 0; i < nelem(includestack); i++) + diag("\t%s", histfile[includestack[i].file]); + errorexit(); + } +} + +/* + * Return false if the a->link chain contains no history, otherwise + * returns true and finds z and Z entries in the Auto list (of a + * Prog), and resets the history stack + */ +static int +inithist(Auto *a) +{ + Linehist *lh; + + for (; a; a = a->link) + if (a->type == D_FILE) + break; + if (a==nil) + return 0; + + // We have a new history. They are guaranteed to come completely + // at the beginning of the compilation unit. + if (a->aoffset != 1) { + diag("dwarf: stray 'z' with offset %d", a->aoffset); + return 0; + } + + // Clear the history. + clearhistfile(); + includetop = 0; + includestack[includetop].file = 0; + includestack[includetop].line = -1; + absline = 0; + while (linehist != nil) { + lh = linehist->link; + free(linehist); + linehist = lh; + } + + // Construct the new one. + for (; a; a = a->link) { + if (a->type == D_FILE) { // 'z' + int f = addhistfile(a->asym->name); + if (f < 0) { // pop file + includetop--; + checknesting(); + } else if(f != includestack[includetop].file) { // pushed a new file + includestack[includetop].line += a->aoffset - absline; + includetop++; + checknesting(); + includestack[includetop].file = f; + includestack[includetop].line = 1; + } + absline = a->aoffset; + } else if (a->type == D_FILE1) { // 'Z' + // We could just fixup the current + // linehist->line, but there doesn't appear to + // be a guarantee that every 'Z' is preceded + // by its own 'z', so do the safe thing and + // update the stack and push a new Linehist + // entry + includestack[includetop].line = a->aoffset; + } else + continue; + if (linehist == 0 || linehist->absline != absline) { + Linehist* lh = malloc(sizeof *lh); + lh->link = linehist; + lh->absline = absline; + linehist = lh; + } + linehist->file = includestack[includetop].file; + linehist->line = includestack[includetop].line; + } + return 1; +} + +static Linehist * +searchhist(vlong absline) +{ + Linehist *lh; + + for (lh = linehist; lh; lh = lh->link) + if (lh->absline <= absline) + break; + return lh; +} + +static int +guesslang(char *s) +{ + if(strlen(s) >= 3 && strcmp(s+strlen(s)-3, ".go") == 0) + return DW_LANG_Go; + + return DW_LANG_C; +} + +/* + * Generate short opcodes when possible, long ones when neccesary. + * See section 6.2.5 + */ + +enum { + LINE_BASE = -1, + LINE_RANGE = 4, + OPCODE_BASE = 5 +}; + +static void +putpclcdelta(vlong delta_pc, vlong delta_lc) +{ + if (LINE_BASE <= delta_lc && delta_lc < LINE_BASE+LINE_RANGE) { + vlong opcode = OPCODE_BASE + (delta_lc - LINE_BASE) + (LINE_RANGE * delta_pc); + if (OPCODE_BASE <= opcode && opcode < 256) { + cput(opcode); + return; + } + } + + if (delta_pc) { + cput(DW_LNS_advance_pc); + sleb128put(delta_pc); + } + + cput(DW_LNS_advance_line); + sleb128put(delta_lc); + cput(DW_LNS_copy); +} + +static void +newcfaoffsetattr(DWDie *die, int32 offs) +{ + char block[10]; + int i; + + i = 0; + + block[i++] = DW_OP_call_frame_cfa; + if (offs != 0) { + block[i++] = DW_OP_consts; + i += sleb128enc(offs, block+i); + block[i++] = DW_OP_plus; + } + newattr(die, DW_AT_location, DW_CLS_BLOCK, i, mal(i)); + memmove(die->attr->data, block, i); +} + +static char* +mkvarname(char* name, int da) +{ + char buf[1024]; + char *n; + + snprint(buf, sizeof buf, "%s#%d", name, da); + n = mal(strlen(buf) + 1); + memmove(n, buf, strlen(buf)); + return n; +} + +/* + * Walk prog table, emit line program and build DIE tree. + */ + +// flush previous compilation unit. +static void +flushunit(DWDie *dwinfo, vlong pc, vlong unitstart, int32 header_length) +{ + vlong here; + + if (dwinfo != nil && pc != 0) { + newattr(dwinfo, DW_AT_high_pc, DW_CLS_ADDRESS, pc+1, 0); + } + + if (unitstart >= 0) { + cput(0); // start extended opcode + uleb128put(1); + cput(DW_LNE_end_sequence); + + here = cpos(); + cseek(unitstart); + LPUT(here - unitstart - sizeof(int32)); // unit_length + WPUT(3); // dwarf version + LPUT(header_length); // header length starting here + cseek(here); + } +} + +static void +writelines(void) +{ + Prog *q; + Sym *s; + Auto *a; + vlong unitstart, headerend, offs; + vlong pc, epc, lc, llc, lline; + int currfile; + int i, lang, da, dt; + Linehist *lh; + DWDie *dwinfo, *dwfunc, *dwvar, **dws; + DWDie *varhash[HASHSIZE]; + char *n, *nn; + + unitstart = -1; + headerend = -1; + pc = 0; + epc = 0; + lc = 1; + llc = 1; + currfile = -1; + lineo = cpos(); + dwinfo = nil; + + for(cursym = textp; cursym != nil; cursym = cursym->next) { + s = cursym; + if(s->text == P) + continue; + + // Look for history stack. If we find one, + // we're entering a new compilation unit + + if (inithist(s->autom)) { + flushunit(dwinfo, epc, unitstart, headerend - unitstart - 10); + unitstart = cpos(); + + if(debug['v'] > 1) { + print("dwarf writelines found %s\n", histfile[1]); + Linehist* lh; + for (lh = linehist; lh; lh = lh->link) + print("\t%8lld: [%4lld]%s\n", + lh->absline, lh->line, histfile[lh->file]); + } + + lang = guesslang(histfile[1]); + finddebugruntimepath(); + + dwinfo = newdie(&dwroot, DW_ABRV_COMPUNIT, strdup(histfile[1])); + newattr(dwinfo, DW_AT_language, DW_CLS_CONSTANT,lang, 0); + newattr(dwinfo, DW_AT_stmt_list, DW_CLS_PTR, unitstart - lineo, 0); + newattr(dwinfo, DW_AT_low_pc, DW_CLS_ADDRESS, s->text->pc, 0); + + // Write .debug_line Line Number Program Header (sec 6.2.4) + // Fields marked with (*) must be changed for 64-bit dwarf + LPUT(0); // unit_length (*), will be filled in by flushunit. + WPUT(3); // dwarf version (appendix F) + LPUT(0); // header_length (*), filled in by flushunit. + // cpos == unitstart + 4 + 2 + 4 + cput(1); // minimum_instruction_length + cput(1); // default_is_stmt + cput(LINE_BASE); // line_base + cput(LINE_RANGE); // line_range + cput(OPCODE_BASE); // opcode_base (we only use 1..4) + cput(0); // standard_opcode_lengths[1] + cput(1); // standard_opcode_lengths[2] + cput(1); // standard_opcode_lengths[3] + cput(1); // standard_opcode_lengths[4] + cput(0); // include_directories (empty) + + for (i=1; i < histfilesize; i++) { + strnput(histfile[i], strlen(histfile[i]) + 4); + // 4 zeros: the string termination + 3 fields. + } + + cput(0); // terminate file_names. + headerend = cpos(); + + pc = s->text->pc; + epc = pc; + currfile = 1; + lc = 1; + llc = 1; + + cput(0); // start extended opcode + uleb128put(1 + PtrSize); + cput(DW_LNE_set_address); + addrput(pc); + } + if(s->text == nil) + continue; + + if (unitstart < 0) { + diag("dwarf: reachable code before seeing any history: %P", s->text); + continue; + } + + dwfunc = newdie(dwinfo, DW_ABRV_FUNCTION, s->name); + newattr(dwfunc, DW_AT_low_pc, DW_CLS_ADDRESS, s->value, 0); + epc = s->value + s->size; + newattr(dwfunc, DW_AT_high_pc, DW_CLS_ADDRESS, epc, 0); + if (s->version == 0) + newattr(dwfunc, DW_AT_external, DW_CLS_FLAG, 1, 0); + + if(s->text->link == nil) + continue; + + for(q = s->text; q != P; q = q->link) { + lh = searchhist(q->line); + if (lh == nil) { + diag("dwarf: corrupt history or bad absolute line: %P", q); + continue; + } + + if (lh->file < 1) { // 0 is the past-EOF entry. + // diag("instruction with line number past EOF in %s: %P", histfile[1], q); + continue; + } + + lline = lh->line + q->line - lh->absline; + if (debug['v'] > 1) + print("%6llux %s[%lld] %P\n", (vlong)q->pc, histfile[lh->file], lline, q); + + if (q->line == lc) + continue; + if (currfile != lh->file) { + currfile = lh->file; + cput(DW_LNS_set_file); + uleb128put(currfile); + } + putpclcdelta(q->pc - pc, lline - llc); + pc = q->pc; + lc = q->line; + llc = lline; + } + + da = 0; + dwfunc->hash = varhash; // enable indexing of children by name + memset(varhash, 0, sizeof varhash); + for(a = s->autom; a; a = a->link) { + switch (a->type) { + case D_AUTO: + dt = DW_ABRV_AUTO; + offs = a->aoffset - PtrSize; + break; + case D_PARAM: + dt = DW_ABRV_PARAM; + offs = a->aoffset; + break; + default: + continue; + } + if (strstr(a->asym->name, ".autotmp_")) + continue; + if (find(dwfunc, a->asym->name) != nil) + n = mkvarname(a->asym->name, da); + else + n = a->asym->name; + // Drop the package prefix from locals and arguments. + nn = strrchr(n, '.'); + if (nn) + n = nn + 1; + + dwvar = newdie(dwfunc, dt, n); + newcfaoffsetattr(dwvar, offs); + newrefattr(dwvar, DW_AT_type, defgotype(a->gotype)); + + // push dwvar down dwfunc->child to preserve order + newattr(dwvar, DW_AT_internal_location, DW_CLS_CONSTANT, offs, nil); + dwfunc->child = dwvar->link; // take dwvar out from the top of the list + for (dws = &dwfunc->child; *dws != nil; dws = &(*dws)->link) + if (offs > getattr(*dws, DW_AT_internal_location)->value) + break; + dwvar->link = *dws; + *dws = dwvar; + + da++; + } + + dwfunc->hash = nil; + } + + flushunit(dwinfo, epc, unitstart, headerend - unitstart - 10); + linesize = cpos() - lineo; +} + +/* + * Emit .debug_frame + */ +enum +{ + CIERESERVE = 16, + DATAALIGNMENTFACTOR = -4, // TODO -PtrSize? + FAKERETURNCOLUMN = 16 // TODO gdb6 doesnt like > 15? +}; + +static void +putpccfadelta(vlong deltapc, vlong cfa) +{ + if (deltapc < 0x40) { + cput(DW_CFA_advance_loc + deltapc); + } else if (deltapc < 0x100) { + cput(DW_CFA_advance_loc1); + cput(deltapc); + } else if (deltapc < 0x10000) { + cput(DW_CFA_advance_loc2); + WPUT(deltapc); + } else { + cput(DW_CFA_advance_loc4); + LPUT(deltapc); + } + + cput(DW_CFA_def_cfa_offset_sf); + sleb128put(cfa / DATAALIGNMENTFACTOR); +} + +static void +writeframes(void) +{ + Prog *p, *q; + Sym *s; + vlong fdeo, fdesize, pad, cfa, pc; + + frameo = cpos(); + + // Emit the CIE, Section 6.4.1 + LPUT(CIERESERVE); // initial length, must be multiple of PtrSize + LPUT(0xffffffff); // cid. + cput(3); // dwarf version (appendix F) + cput(0); // augmentation "" + uleb128put(1); // code_alignment_factor + sleb128put(DATAALIGNMENTFACTOR); // guess + uleb128put(FAKERETURNCOLUMN); // return_address_register + + cput(DW_CFA_def_cfa); + uleb128put(DWARFREGSP); // register SP (**ABI-dependent, defined in l.h) + uleb128put(PtrSize); // offset + + cput(DW_CFA_offset + FAKERETURNCOLUMN); // return address + uleb128put(-PtrSize / DATAALIGNMENTFACTOR); // at cfa - x*4 + + // 4 is to exclude the length field. + pad = CIERESERVE + frameo + 4 - cpos(); + if (pad < 0) { + diag("dwarf: CIERESERVE too small by %lld bytes.", -pad); + errorexit(); + } + strnput("", pad); + + for(cursym = textp; cursym != nil; cursym = cursym->next) { + s = cursym; + if(s->text == nil) + continue; + + fdeo = cpos(); + // Emit a FDE, Section 6.4.1, starting wit a placeholder. + LPUT(0); // length, must be multiple of PtrSize + LPUT(0); // Pointer to the CIE above, at offset 0 + addrput(0); // initial location + addrput(0); // address range + + cfa = PtrSize; // CFA starts at sp+PtrSize + p = s->text; + pc = p->pc; + + for(q = p; q->link != P; q = q->link) { + if (q->spadj == 0) + continue; + cfa += q->spadj; + putpccfadelta(q->link->pc - pc, cfa); + pc = q->link->pc; + } + + fdesize = cpos() - fdeo - 4; // exclude the length field. + pad = rnd(fdesize, PtrSize) - fdesize; + strnput("", pad); + fdesize += pad; + + // Emit the FDE header for real, Section 6.4.1. + cseek(fdeo); + LPUT(fdesize); + LPUT(0); + addrput(p->pc); + addrput(s->size); + cseek(fdeo + 4 + fdesize); + } + + cflush(); + framesize = cpos() - frameo; +} + +/* + * Walk DWarfDebugInfoEntries, and emit .debug_info + */ +enum +{ + COMPUNITHEADERSIZE = 4+2+4+1 +}; + +static void +writeinfo(void) +{ + DWDie *compunit; + vlong unitstart, here; + + fwdcount = 0; + + for (compunit = dwroot.child; compunit; compunit = compunit->link) { + unitstart = cpos(); + + // Write .debug_info Compilation Unit Header (sec 7.5.1) + // Fields marked with (*) must be changed for 64-bit dwarf + // This must match COMPUNITHEADERSIZE above. + LPUT(0); // unit_length (*), will be filled in later. + WPUT(3); // dwarf version (appendix F) + LPUT(0); // debug_abbrev_offset (*) + cput(PtrSize); // address_size + + putdie(compunit); + + here = cpos(); + cseek(unitstart); + LPUT(here - unitstart - 4); // exclude the length field. + cseek(here); + } + cflush(); +} + +/* + * Emit .debug_pubnames/_types. _info must have been written before, + * because we need die->offs and infoo/infosize; + */ +static int +ispubname(DWDie *die) { + DWAttr *a; + + switch(die->abbrev) { + case DW_ABRV_FUNCTION: + case DW_ABRV_VARIABLE: + a = getattr(die, DW_AT_external); + return a && a->value; + } + return 0; +} + +static int +ispubtype(DWDie *die) { + return die->abbrev >= DW_ABRV_NULLTYPE; +} + +static vlong +writepub(int (*ispub)(DWDie*)) +{ + DWDie *compunit, *die; + DWAttr *dwa; + vlong unitstart, unitend, sectionstart, here; + + sectionstart = cpos(); + + for (compunit = dwroot.child; compunit != nil; compunit = compunit->link) { + unitstart = compunit->offs - COMPUNITHEADERSIZE; + if (compunit->link != nil) + unitend = compunit->link->offs - COMPUNITHEADERSIZE; + else + unitend = infoo + infosize; + + // Write .debug_pubnames/types Header (sec 6.1.1) + LPUT(0); // unit_length (*), will be filled in later. + WPUT(2); // dwarf version (appendix F) + LPUT(unitstart); // debug_info_offset (of the Comp unit Header) + LPUT(unitend - unitstart); // debug_info_length + + for (die = compunit->child; die != nil; die = die->link) { + if (!ispub(die)) continue; + LPUT(die->offs - unitstart); + dwa = getattr(die, DW_AT_name); + strnput(dwa->data, dwa->value + 1); + } + LPUT(0); + + here = cpos(); + cseek(sectionstart); + LPUT(here - sectionstart - 4); // exclude the length field. + cseek(here); + + } + + return sectionstart; +} + +/* + * emit .debug_aranges. _info must have been written before, + * because we need die->offs of dw_globals. + */ +static vlong +writearanges(void) +{ + DWDie *compunit; + DWAttr *b, *e; + int headersize; + vlong sectionstart; + + sectionstart = cpos(); + headersize = rnd(4+2+4+1+1, PtrSize); // don't count unit_length field itself + + for (compunit = dwroot.child; compunit != nil; compunit = compunit->link) { + b = getattr(compunit, DW_AT_low_pc); + if (b == nil) + continue; + e = getattr(compunit, DW_AT_high_pc); + if (e == nil) + continue; + + // Write .debug_aranges Header + entry (sec 6.1.2) + LPUT(headersize + 4*PtrSize - 4); // unit_length (*) + WPUT(2); // dwarf version (appendix F) + LPUT(compunit->offs - COMPUNITHEADERSIZE); // debug_info_offset + cput(PtrSize); // address_size + cput(0); // segment_size + strnput("", headersize - (4+2+4+1+1)); // align to PtrSize + + addrput(b->value); + addrput(e->value - b->value); + addrput(0); + addrput(0); + } + cflush(); + return sectionstart; +} + +static vlong +writegdbscript(void) +{ + vlong sectionstart; + + sectionstart = cpos(); + + if (gdbscript[0]) { + cput(1); // magic 1 byte? + strnput(gdbscript, strlen(gdbscript)+1); + cflush(); + } + return sectionstart; +} + +static void +align(vlong size) +{ + if(HEADTYPE == Hwindows) // Only Windows PE need section align. + strnput("", rnd(size, PEFILEALIGN) - size); +} + +/* + * This is the main entry point for generating dwarf. After emitting + * the mandatory debug_abbrev section, it calls writelines() to set up + * the per-compilation unit part of the DIE tree, while simultaneously + * emitting the debug_line section. When the final tree contains + * forward references, it will write the debug_info section in 2 + * passes. + * + */ +void +dwarfemitdebugsections(void) +{ + vlong infoe; + DWDie* die; + + if(debug['w']) // disable dwarf + return; + + // For diagnostic messages. + newattr(&dwtypes, DW_AT_name, DW_CLS_STRING, strlen("dwtypes"), "dwtypes"); + + mkindex(&dwroot); + mkindex(&dwtypes); + mkindex(&dwglobals); + + // Some types that must exist to define other ones. + newdie(&dwtypes, DW_ABRV_NULLTYPE, "<unspecified>"); + newdie(&dwtypes, DW_ABRV_NULLTYPE, "void"); + newrefattr(newdie(&dwtypes, DW_ABRV_PTRTYPE, "unsafe.Pointer"), + DW_AT_type, find(&dwtypes, "void")); + die = newdie(&dwtypes, DW_ABRV_BASETYPE, "uintptr"); // needed for array size + newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_unsigned, 0); + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, PtrSize, 0); + + // Needed by the prettyprinter code for interface inspection. + defgotype(lookup_or_diag("type.runtime.commonType")); + defgotype(lookup_or_diag("type.runtime.InterfaceType")); + defgotype(lookup_or_diag("type.runtime.itab")); + + genasmsym(defdwsymb); + + writeabbrev(); + align(abbrevsize); + writelines(); + align(linesize); + writeframes(); + align(framesize); + + synthesizestringtypes(dwtypes.child); + synthesizeslicetypes(dwtypes.child); + synthesizemaptypes(dwtypes.child); + synthesizechantypes(dwtypes.child); + + reversetree(&dwroot.child); + reversetree(&dwtypes.child); + reversetree(&dwglobals.child); + + movetomodule(&dwtypes); + movetomodule(&dwglobals); + + infoo = cpos(); + writeinfo(); + infoe = cpos(); + pubnameso = infoe; + pubtypeso = infoe; + arangeso = infoe; + gdbscripto = infoe; + + if (fwdcount > 0) { + if (debug['v']) + Bprint(&bso, "%5.2f dwarf pass 2.\n", cputime()); + cseek(infoo); + writeinfo(); + if (fwdcount > 0) { + diag("dwarf: unresolved references after first dwarf info pass"); + errorexit(); + } + if (infoe != cpos()) { + diag("dwarf: inconsistent second dwarf info pass"); + errorexit(); + } + } + infosize = infoe - infoo; + align(infosize); + + pubnameso = writepub(ispubname); + pubnamessize = cpos() - pubnameso; + align(pubnamessize); + + pubtypeso = writepub(ispubtype); + pubtypessize = cpos() - pubtypeso; + align(pubtypessize); + + arangeso = writearanges(); + arangessize = cpos() - arangeso; + align(arangessize); + + gdbscripto = writegdbscript(); + gdbscriptsize = cpos() - gdbscripto; + align(gdbscriptsize); +} + +/* + * Elf. + */ +enum +{ + ElfStrDebugAbbrev, + ElfStrDebugAranges, + ElfStrDebugFrame, + ElfStrDebugInfo, + ElfStrDebugLine, + ElfStrDebugLoc, + ElfStrDebugMacinfo, + ElfStrDebugPubNames, + ElfStrDebugPubTypes, + ElfStrDebugRanges, + ElfStrDebugStr, + ElfStrGDBScripts, + NElfStrDbg +}; + +vlong elfstrdbg[NElfStrDbg]; + +void +dwarfaddshstrings(Sym *shstrtab) +{ + if(debug['w']) // disable dwarf + return; + + elfstrdbg[ElfStrDebugAbbrev] = addstring(shstrtab, ".debug_abbrev"); + elfstrdbg[ElfStrDebugAranges] = addstring(shstrtab, ".debug_aranges"); + elfstrdbg[ElfStrDebugFrame] = addstring(shstrtab, ".debug_frame"); + elfstrdbg[ElfStrDebugInfo] = addstring(shstrtab, ".debug_info"); + elfstrdbg[ElfStrDebugLine] = addstring(shstrtab, ".debug_line"); + elfstrdbg[ElfStrDebugLoc] = addstring(shstrtab, ".debug_loc"); + elfstrdbg[ElfStrDebugMacinfo] = addstring(shstrtab, ".debug_macinfo"); + elfstrdbg[ElfStrDebugPubNames] = addstring(shstrtab, ".debug_pubnames"); + elfstrdbg[ElfStrDebugPubTypes] = addstring(shstrtab, ".debug_pubtypes"); + elfstrdbg[ElfStrDebugRanges] = addstring(shstrtab, ".debug_ranges"); + elfstrdbg[ElfStrDebugStr] = addstring(shstrtab, ".debug_str"); + elfstrdbg[ElfStrGDBScripts] = addstring(shstrtab, ".debug_gdb_scripts"); +} + +void +dwarfaddelfheaders(void) +{ + ElfShdr *sh; + + if(debug['w']) // disable dwarf + return; + + sh = newElfShdr(elfstrdbg[ElfStrDebugAbbrev]); + sh->type = SHT_PROGBITS; + sh->off = abbrevo; + sh->size = abbrevsize; + sh->addralign = 1; + + sh = newElfShdr(elfstrdbg[ElfStrDebugLine]); + sh->type = SHT_PROGBITS; + sh->off = lineo; + sh->size = linesize; + sh->addralign = 1; + + sh = newElfShdr(elfstrdbg[ElfStrDebugFrame]); + sh->type = SHT_PROGBITS; + sh->off = frameo; + sh->size = framesize; + sh->addralign = 1; + + sh = newElfShdr(elfstrdbg[ElfStrDebugInfo]); + sh->type = SHT_PROGBITS; + sh->off = infoo; + sh->size = infosize; + sh->addralign = 1; + + if (pubnamessize > 0) { + sh = newElfShdr(elfstrdbg[ElfStrDebugPubNames]); + sh->type = SHT_PROGBITS; + sh->off = pubnameso; + sh->size = pubnamessize; + sh->addralign = 1; + } + + if (pubtypessize > 0) { + sh = newElfShdr(elfstrdbg[ElfStrDebugPubTypes]); + sh->type = SHT_PROGBITS; + sh->off = pubtypeso; + sh->size = pubtypessize; + sh->addralign = 1; + } + + if (arangessize) { + sh = newElfShdr(elfstrdbg[ElfStrDebugAranges]); + sh->type = SHT_PROGBITS; + sh->off = arangeso; + sh->size = arangessize; + sh->addralign = 1; + } + + if (gdbscriptsize) { + sh = newElfShdr(elfstrdbg[ElfStrGDBScripts]); + sh->type = SHT_PROGBITS; + sh->off = gdbscripto; + sh->size = gdbscriptsize; + sh->addralign = 1; + } +} + +/* + * Macho + */ +void +dwarfaddmachoheaders(void) +{ + MachoSect *msect; + MachoSeg *ms; + vlong fakestart; + int nsect; + + if(debug['w']) // disable dwarf + return; + + // Zero vsize segments won't be loaded in memory, even so they + // have to be page aligned in the file. + fakestart = abbrevo & ~0xfff; + + nsect = 4; + if (pubnamessize > 0) + nsect++; + if (pubtypessize > 0) + nsect++; + if (arangessize > 0) + nsect++; + if (gdbscriptsize > 0) + nsect++; + + ms = newMachoSeg("__DWARF", nsect); + ms->fileoffset = fakestart; + ms->filesize = abbrevo-fakestart; + + msect = newMachoSect(ms, "__debug_abbrev"); + msect->off = abbrevo; + msect->size = abbrevsize; + ms->filesize += msect->size; + + msect = newMachoSect(ms, "__debug_line"); + msect->off = lineo; + msect->size = linesize; + ms->filesize += msect->size; + + msect = newMachoSect(ms, "__debug_frame"); + msect->off = frameo; + msect->size = framesize; + ms->filesize += msect->size; + + msect = newMachoSect(ms, "__debug_info"); + msect->off = infoo; + msect->size = infosize; + ms->filesize += msect->size; + + if (pubnamessize > 0) { + msect = newMachoSect(ms, "__debug_pubnames"); + msect->off = pubnameso; + msect->size = pubnamessize; + ms->filesize += msect->size; + } + + if (pubtypessize > 0) { + msect = newMachoSect(ms, "__debug_pubtypes"); + msect->off = pubtypeso; + msect->size = pubtypessize; + ms->filesize += msect->size; + } + + if (arangessize > 0) { + msect = newMachoSect(ms, "__debug_aranges"); + msect->off = arangeso; + msect->size = arangessize; + ms->filesize += msect->size; + } + + // TODO(lvd) fix gdb/python to load MachO (16 char section name limit) + if (gdbscriptsize > 0) { + msect = newMachoSect(ms, "__debug_gdb_scripts"); + msect->off = gdbscripto; + msect->size = gdbscriptsize; + ms->filesize += msect->size; + } +} + +/* + * Windows PE + */ +void +dwarfaddpeheaders(void) +{ + if(debug['w']) // disable dwarf + return; + + newPEDWARFSection(".debug_abbrev", abbrevsize); + newPEDWARFSection(".debug_line", linesize); + newPEDWARFSection(".debug_frame", framesize); + newPEDWARFSection(".debug_info", infosize); + newPEDWARFSection(".debug_pubnames", pubnamessize); + newPEDWARFSection(".debug_pubtypes", pubtypessize); + newPEDWARFSection(".debug_aranges", arangessize); + newPEDWARFSection(".debug_gdb_scripts", gdbscriptsize); +} diff --git a/src/cmd/ld/dwarf.h b/src/cmd/ld/dwarf.h new file mode 100644 index 000000000..f0df2f9b1 --- /dev/null +++ b/src/cmd/ld/dwarf.h @@ -0,0 +1,30 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* + * Register 'f' symbol file fragments. Doing this while parsing the + * .6 input saves a pass over the symbol table later. + */ +void dwarfaddfrag(int n, char* frag); + +/* + * Emit debug_abbrevs, debug_info and debug_line sections to current + * offset in cout. + */ +void dwarfemitdebugsections(void); + +/* + * Add the dwarf section names to the ELF + * s[ection]h[eader]str[ing]tab. Prerequisite for + * dwarfaddelfheaders(). + */ +void dwarfaddshstrings(Sym *shstrtab); + +/* + * Add section headers pointing to the sections emitted in + * dwarfemitdebugsections. + */ +void dwarfaddelfheaders(void); +void dwarfaddmachoheaders(void); +void dwarfaddpeheaders(void); diff --git a/src/cmd/ld/dwarf_defs.h b/src/cmd/ld/dwarf_defs.h new file mode 100644 index 000000000..eed143dff --- /dev/null +++ b/src/cmd/ld/dwarf_defs.h @@ -0,0 +1,503 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Cut, pasted, tr-and-awk'ed from tables in +// http://dwarfstd.org/doc/Dwarf3.pdf + +// Table 18 +enum +{ + DW_TAG_array_type = 0x01, + DW_TAG_class_type = 0x02, + DW_TAG_entry_point = 0x03, + DW_TAG_enumeration_type = 0x04, + DW_TAG_formal_parameter = 0x05, + DW_TAG_imported_declaration = 0x08, + DW_TAG_label = 0x0a, + DW_TAG_lexical_block = 0x0b, + DW_TAG_member = 0x0d, + DW_TAG_pointer_type = 0x0f, + DW_TAG_reference_type = 0x10, + DW_TAG_compile_unit = 0x11, + DW_TAG_string_type = 0x12, + DW_TAG_structure_type = 0x13, + DW_TAG_subroutine_type = 0x15, + DW_TAG_typedef = 0x16, + DW_TAG_union_type = 0x17, + DW_TAG_unspecified_parameters = 0x18, + DW_TAG_variant = 0x19, + DW_TAG_common_block = 0x1a, + DW_TAG_common_inclusion = 0x1b, + DW_TAG_inheritance = 0x1c, + DW_TAG_inlined_subroutine = 0x1d, + DW_TAG_module = 0x1e, + DW_TAG_ptr_to_member_type = 0x1f, + DW_TAG_set_type = 0x20, + DW_TAG_subrange_type = 0x21, + DW_TAG_with_stmt = 0x22, + DW_TAG_access_declaration = 0x23, + DW_TAG_base_type = 0x24, + DW_TAG_catch_block = 0x25, + DW_TAG_const_type = 0x26, + DW_TAG_constant = 0x27, + DW_TAG_enumerator = 0x28, + DW_TAG_file_type = 0x29, + DW_TAG_friend = 0x2a, + DW_TAG_namelist = 0x2b, + DW_TAG_namelist_item = 0x2c, + DW_TAG_packed_type = 0x2d, + DW_TAG_subprogram = 0x2e, + DW_TAG_template_type_parameter = 0x2f, + DW_TAG_template_value_parameter = 0x30, + DW_TAG_thrown_type = 0x31, + DW_TAG_try_block = 0x32, + DW_TAG_variant_part = 0x33, + DW_TAG_variable = 0x34, + DW_TAG_volatile_type = 0x35, + // Dwarf3 + DW_TAG_dwarf_procedure = 0x36, + DW_TAG_restrict_type = 0x37, + DW_TAG_interface_type = 0x38, + DW_TAG_namespace = 0x39, + DW_TAG_imported_module = 0x3a, + DW_TAG_unspecified_type = 0x3b, + DW_TAG_partial_unit = 0x3c, + DW_TAG_imported_unit = 0x3d, + DW_TAG_condition = 0x3f, + DW_TAG_shared_type = 0x40, + // Dwarf4 + DW_TAG_type_unit = 0x41, + DW_TAG_rvalue_reference_type = 0x42, + DW_TAG_template_alias = 0x43, + + // User defined + DW_TAG_lo_user = 0x4080, + DW_TAG_hi_user = 0xffff, + +}; + +// Table 19 +enum +{ + DW_CHILDREN_no = 0x00, + DW_CHILDREN_yes = 0x01, +}; + +// Not from the spec, but logicaly belongs here +enum +{ + DW_CLS_ADDRESS = 0x01, + DW_CLS_BLOCK, + DW_CLS_CONSTANT, + DW_CLS_FLAG, + DW_CLS_PTR, // lineptr, loclistptr, macptr, rangelistptr + DW_CLS_REFERENCE, + DW_CLS_STRING +}; + +// Table 20 +enum +{ + DW_AT_sibling = 0x01, // reference + DW_AT_location = 0x02, // block, loclistptr + DW_AT_name = 0x03, // string + DW_AT_ordering = 0x09, // constant + DW_AT_byte_size = 0x0b, // block, constant, reference + DW_AT_bit_offset = 0x0c, // block, constant, reference + DW_AT_bit_size = 0x0d, // block, constant, reference + DW_AT_stmt_list = 0x10, // lineptr + DW_AT_low_pc = 0x11, // address + DW_AT_high_pc = 0x12, // address + DW_AT_language = 0x13, // constant + DW_AT_discr = 0x15, // reference + DW_AT_discr_value = 0x16, // constant + DW_AT_visibility = 0x17, // constant + DW_AT_import = 0x18, // reference + DW_AT_string_length = 0x19, // block, loclistptr + DW_AT_common_reference = 0x1a, // reference + DW_AT_comp_dir = 0x1b, // string + DW_AT_const_value = 0x1c, // block, constant, string + DW_AT_containing_type = 0x1d, // reference + DW_AT_default_value = 0x1e, // reference + DW_AT_inline = 0x20, // constant + DW_AT_is_optional = 0x21, // flag + DW_AT_lower_bound = 0x22, // block, constant, reference + DW_AT_producer = 0x25, // string + DW_AT_prototyped = 0x27, // flag + DW_AT_return_addr = 0x2a, // block, loclistptr + DW_AT_start_scope = 0x2c, // constant + DW_AT_bit_stride = 0x2e, // constant + DW_AT_upper_bound = 0x2f, // block, constant, reference + DW_AT_abstract_origin = 0x31, // reference + DW_AT_accessibility = 0x32, // constant + DW_AT_address_class = 0x33, // constant + DW_AT_artificial = 0x34, // flag + DW_AT_base_types = 0x35, // reference + DW_AT_calling_convention = 0x36, // constant + DW_AT_count = 0x37, // block, constant, reference + DW_AT_data_member_location = 0x38, // block, constant, loclistptr + DW_AT_decl_column = 0x39, // constant + DW_AT_decl_file = 0x3a, // constant + DW_AT_decl_line = 0x3b, // constant + DW_AT_declaration = 0x3c, // flag + DW_AT_discr_list = 0x3d, // block + DW_AT_encoding = 0x3e, // constant + DW_AT_external = 0x3f, // flag + DW_AT_frame_base = 0x40, // block, loclistptr + DW_AT_friend = 0x41, // reference + DW_AT_identifier_case = 0x42, // constant + DW_AT_macro_info = 0x43, // macptr + DW_AT_namelist_item = 0x44, // block + DW_AT_priority = 0x45, // reference + DW_AT_segment = 0x46, // block, loclistptr + DW_AT_specification = 0x47, // reference + DW_AT_static_link = 0x48, // block, loclistptr + DW_AT_type = 0x49, // reference + DW_AT_use_location = 0x4a, // block, loclistptr + DW_AT_variable_parameter = 0x4b, // flag + DW_AT_virtuality = 0x4c, // constant + DW_AT_vtable_elem_location = 0x4d, // block, loclistptr + // Dwarf3 + DW_AT_allocated = 0x4e, // block, constant, reference + DW_AT_associated = 0x4f, // block, constant, reference + DW_AT_data_location = 0x50, // block + DW_AT_byte_stride = 0x51, // block, constant, reference + DW_AT_entry_pc = 0x52, // address + DW_AT_use_UTF8 = 0x53, // flag + DW_AT_extension = 0x54, // reference + DW_AT_ranges = 0x55, // rangelistptr + DW_AT_trampoline = 0x56, // address, flag, reference, string + DW_AT_call_column = 0x57, // constant + DW_AT_call_file = 0x58, // constant + DW_AT_call_line = 0x59, // constant + DW_AT_description = 0x5a, // string + DW_AT_binary_scale = 0x5b, // constant + DW_AT_decimal_scale = 0x5c, // constant + DW_AT_small = 0x5d, // reference + DW_AT_decimal_sign = 0x5e, // constant + DW_AT_digit_count = 0x5f, // constant + DW_AT_picture_string = 0x60, // string + DW_AT_mutable = 0x61, // flag + DW_AT_threads_scaled = 0x62, // flag + DW_AT_explicit = 0x63, // flag + DW_AT_object_pointer = 0x64, // reference + DW_AT_endianity = 0x65, // constant + DW_AT_elemental = 0x66, // flag + DW_AT_pure = 0x67, // flag + DW_AT_recursive = 0x68, // flag + + DW_AT_lo_user = 0x2000, // --- + DW_AT_hi_user = 0x3fff, // --- + +}; + +// Table 21 +enum +{ + DW_FORM_addr = 0x01, // address + DW_FORM_block2 = 0x03, // block + DW_FORM_block4 = 0x04, // block + DW_FORM_data2 = 0x05, // constant + DW_FORM_data4 = 0x06, // constant, lineptr, loclistptr, macptr, rangelistptr + DW_FORM_data8 = 0x07, // constant, lineptr, loclistptr, macptr, rangelistptr + DW_FORM_string = 0x08, // string + DW_FORM_block = 0x09, // block + DW_FORM_block1 = 0x0a, // block + DW_FORM_data1 = 0x0b, // constant + DW_FORM_flag = 0x0c, // flag + DW_FORM_sdata = 0x0d, // constant + DW_FORM_strp = 0x0e, // string + DW_FORM_udata = 0x0f, // constant + DW_FORM_ref_addr = 0x10, // reference + DW_FORM_ref1 = 0x11, // reference + DW_FORM_ref2 = 0x12, // reference + DW_FORM_ref4 = 0x13, // reference + DW_FORM_ref8 = 0x14, // reference + DW_FORM_ref_udata = 0x15, // reference + DW_FORM_indirect = 0x16, // (see Section 7.5.3) +}; + +// Table 24 (#operands, notes) +enum +{ + DW_OP_addr = 0x03, // 1 constant address (size target specific) + DW_OP_deref = 0x06, // 0 + DW_OP_const1u = 0x08, // 1 1-byte constant + DW_OP_const1s = 0x09, // 1 1-byte constant + DW_OP_const2u = 0x0a, // 1 2-byte constant + DW_OP_const2s = 0x0b, // 1 2-byte constant + DW_OP_const4u = 0x0c, // 1 4-byte constant + DW_OP_const4s = 0x0d, // 1 4-byte constant + DW_OP_const8u = 0x0e, // 1 8-byte constant + DW_OP_const8s = 0x0f, // 1 8-byte constant + DW_OP_constu = 0x10, // 1 ULEB128 constant + DW_OP_consts = 0x11, // 1 SLEB128 constant + DW_OP_dup = 0x12, // 0 + DW_OP_drop = 0x13, // 0 + DW_OP_over = 0x14, // 0 + DW_OP_pick = 0x15, // 1 1-byte stack index + DW_OP_swap = 0x16, // 0 + DW_OP_rot = 0x17, // 0 + DW_OP_xderef = 0x18, // 0 + DW_OP_abs = 0x19, // 0 + DW_OP_and = 0x1a, // 0 + DW_OP_div = 0x1b, // 0 + DW_OP_minus = 0x1c, // 0 + DW_OP_mod = 0x1d, // 0 + DW_OP_mul = 0x1e, // 0 + DW_OP_neg = 0x1f, // 0 + DW_OP_not = 0x20, // 0 + DW_OP_or = 0x21, // 0 + DW_OP_plus = 0x22, // 0 + DW_OP_plus_uconst = 0x23, // 1 ULEB128 addend + DW_OP_shl = 0x24, // 0 + DW_OP_shr = 0x25, // 0 + DW_OP_shra = 0x26, // 0 + DW_OP_xor = 0x27, // 0 + DW_OP_skip = 0x2f, // 1 signed 2-byte constant + DW_OP_bra = 0x28, // 1 signed 2-byte constant + DW_OP_eq = 0x29, // 0 + DW_OP_ge = 0x2a, // 0 + DW_OP_gt = 0x2b, // 0 + DW_OP_le = 0x2c, // 0 + DW_OP_lt = 0x2d, // 0 + DW_OP_ne = 0x2e, // 0 + DW_OP_lit0 = 0x30, // 0 ... + DW_OP_lit31 = 0x4f, // 0 literals 0..31 = (DW_OP_lit0 + + // literal) + DW_OP_reg0 = 0x50, // 0 .. + DW_OP_reg31 = 0x6f, // 0 reg 0..31 = (DW_OP_reg0 + regnum) + DW_OP_breg0 = 0x70, // 1 ... + DW_OP_breg31 = 0x8f, // 1 SLEB128 offset base register 0..31 = (DW_OP_breg0 + regnum) + DW_OP_regx = 0x90, // 1 ULEB128 register + DW_OP_fbreg = 0x91, // 1 SLEB128 offset + DW_OP_bregx = 0x92, // 2 ULEB128 register followed by SLEB128 offset + DW_OP_piece = 0x93, // 1 ULEB128 size of piece addressed + DW_OP_deref_size = 0x94, // 1 1-byte size of data retrieved + DW_OP_xderef_size = 0x95, // 1 1-byte size of data retrieved + DW_OP_nop = 0x96, // 0 + DW_OP_push_object_address = 0x97, // 0 + DW_OP_call2 = 0x98, // 1 2-byte offset of DIE + DW_OP_call4 = 0x99, // 1 4-byte offset of DIE + DW_OP_call_ref = 0x9a, // 1 4- or 8-byte offset of DIE + DW_OP_form_tls_address = 0x9b, // 0 + DW_OP_call_frame_cfa = 0x9c, // 0 + DW_OP_bit_piece = 0x9d, // 2 + DW_OP_lo_user = 0xe0, + DW_OP_hi_user = 0xff, +}; + +// Table 25 +enum +{ + DW_ATE_address = 0x01, + DW_ATE_boolean = 0x02, + DW_ATE_complex_float = 0x03, + DW_ATE_float = 0x04, + DW_ATE_signed = 0x05, + DW_ATE_signed_char = 0x06, + DW_ATE_unsigned = 0x07, + DW_ATE_unsigned_char = 0x08, + DW_ATE_imaginary_float = 0x09, + DW_ATE_packed_decimal = 0x0a, + DW_ATE_numeric_string = 0x0b, + DW_ATE_edited = 0x0c, + DW_ATE_signed_fixed = 0x0d, + DW_ATE_unsigned_fixed = 0x0e, + DW_ATE_decimal_float = 0x0f, + DW_ATE_lo_user = 0x80, + DW_ATE_hi_user = 0xff, +}; + +// Table 26 +enum +{ + DW_DS_unsigned = 0x01, + DW_DS_leading_overpunch = 0x02, + DW_DS_trailing_overpunch = 0x03, + DW_DS_leading_separate = 0x04, + DW_DS_trailing_separate = 0x05, +}; + +// Table 27 +enum +{ + DW_END_default = 0x00, + DW_END_big = 0x01, + DW_END_little = 0x02, + DW_END_lo_user = 0x40, + DW_END_hi_user = 0xff, +}; + +// Table 28 +enum +{ + DW_ACCESS_public = 0x01, + DW_ACCESS_protected = 0x02, + DW_ACCESS_private = 0x03, +}; + +// Table 29 +enum +{ + DW_VIS_local = 0x01, + DW_VIS_exported = 0x02, + DW_VIS_qualified = 0x03, +}; + +// Table 30 +enum +{ + DW_VIRTUALITY_none = 0x00, + DW_VIRTUALITY_virtual = 0x01, + DW_VIRTUALITY_pure_virtual = 0x02, +}; + +// Table 31 +enum +{ + DW_LANG_C89 = 0x0001, + DW_LANG_C = 0x0002, + DW_LANG_Ada83 = 0x0003, + DW_LANG_C_plus_plus = 0x0004, + DW_LANG_Cobol74 = 0x0005, + DW_LANG_Cobol85 = 0x0006, + DW_LANG_Fortran77 = 0x0007, + DW_LANG_Fortran90 = 0x0008, + DW_LANG_Pascal83 = 0x0009, + DW_LANG_Modula2 = 0x000a, + // Dwarf3 + DW_LANG_Java = 0x000b, + DW_LANG_C99 = 0x000c, + DW_LANG_Ada95 = 0x000d, + DW_LANG_Fortran95 = 0x000e, + DW_LANG_PLI = 0x000f, + DW_LANG_ObjC = 0x0010, + DW_LANG_ObjC_plus_plus = 0x0011, + DW_LANG_UPC = 0x0012, + DW_LANG_D = 0x0013, + // Dwarf4 + DW_LANG_Python = 0x0014, + // Dwarf5 + DW_LANG_Go = 0x0016, + + DW_LANG_lo_user = 0x8000, + DW_LANG_hi_user = 0xffff, +}; + +// Table 32 +enum +{ + DW_ID_case_sensitive = 0x00, + DW_ID_up_case = 0x01, + DW_ID_down_case = 0x02, + DW_ID_case_insensitive = 0x03, +}; + +// Table 33 +enum +{ + DW_CC_normal = 0x01, + DW_CC_program = 0x02, + DW_CC_nocall = 0x03, + DW_CC_lo_user = 0x40, + DW_CC_hi_user = 0xff, +}; + +// Table 34 +enum +{ + DW_INL_not_inlined = 0x00, + DW_INL_inlined = 0x01, + DW_INL_declared_not_inlined = 0x02, + DW_INL_declared_inlined = 0x03, +}; + +// Table 35 +enum +{ + DW_ORD_row_major = 0x00, + DW_ORD_col_major = 0x01, +}; + +// Table 36 +enum +{ + DW_DSC_label = 0x00, + DW_DSC_range = 0x01, +}; + +// Table 37 +enum +{ + DW_LNS_copy = 0x01, + DW_LNS_advance_pc = 0x02, + DW_LNS_advance_line = 0x03, + DW_LNS_set_file = 0x04, + DW_LNS_set_column = 0x05, + DW_LNS_negate_stmt = 0x06, + DW_LNS_set_basic_block = 0x07, + DW_LNS_const_add_pc = 0x08, + DW_LNS_fixed_advance_pc = 0x09, + // Dwarf3 + DW_LNS_set_prologue_end = 0x0a, + DW_LNS_set_epilogue_begin = 0x0b, + DW_LNS_set_isa = 0x0c, +}; + +// Table 38 +enum +{ + DW_LNE_end_sequence = 0x01, + DW_LNE_set_address = 0x02, + DW_LNE_define_file = 0x03, + DW_LNE_lo_user = 0x80, + DW_LNE_hi_user = 0xff, +}; + +// Table 39 +enum +{ + DW_MACINFO_define = 0x01, + DW_MACINFO_undef = 0x02, + DW_MACINFO_start_file = 0x03, + DW_MACINFO_end_file = 0x04, + DW_MACINFO_vendor_ext = 0xff, +}; + +// Table 40. +enum +{ // operand,... + DW_CFA_nop = 0x00, + DW_CFA_set_loc = 0x01, // address + DW_CFA_advance_loc1 = 0x02, // 1-byte delta + DW_CFA_advance_loc2 = 0x03, // 2-byte delta + DW_CFA_advance_loc4 = 0x04, // 4-byte delta + DW_CFA_offset_extended = 0x05, // ULEB128 register, ULEB128 offset + DW_CFA_restore_extended = 0x06, // ULEB128 register + DW_CFA_undefined = 0x07, // ULEB128 register + DW_CFA_same_value = 0x08, // ULEB128 register + DW_CFA_register = 0x09, // ULEB128 register, ULEB128 register + DW_CFA_remember_state = 0x0a, + DW_CFA_restore_state = 0x0b, + DW_CFA_def_cfa = 0x0c, // ULEB128 register, ULEB128 offset + DW_CFA_def_cfa_register = 0x0d, // ULEB128 register + DW_CFA_def_cfa_offset = 0x0e, // ULEB128 offset + DW_CFA_def_cfa_expression = 0x0f, // BLOCK + DW_CFA_expression = 0x10, // ULEB128 register, BLOCK + DW_CFA_offset_extended_sf = 0x11, // ULEB128 register, SLEB128 offset + DW_CFA_def_cfa_sf = 0x12, // ULEB128 register, SLEB128 offset + DW_CFA_def_cfa_offset_sf = 0x13, // SLEB128 offset + DW_CFA_val_offset = 0x14, // ULEB128, ULEB128 + DW_CFA_val_offset_sf = 0x15, // ULEB128, SLEB128 + DW_CFA_val_expression = 0x16, // ULEB128, BLOCK + + DW_CFA_lo_user = 0x1c, + DW_CFA_hi_user = 0x3f, + + // Opcodes that take an addend operand. + DW_CFA_advance_loc = 0x1<<6, // +delta + DW_CFA_offset = 0x2<<6, // +register (ULEB128 offset) + DW_CFA_restore = 0x3<<6, // +register +}; diff --git a/src/cmd/ld/elf.c b/src/cmd/ld/elf.c new file mode 100644 index 000000000..f9f9ef6b2 --- /dev/null +++ b/src/cmd/ld/elf.c @@ -0,0 +1,564 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "l.h" +#include "lib.h" +#include "../ld/elf.h" + +/* + * We use the 64-bit data structures on both 32- and 64-bit machines + * in order to write the code just once. The 64-bit data structure is + * written in the 32-bit format on the 32-bit machines. + */ +#define NSECT 32 + +int iself; + +static int elf64; +static ElfEhdr hdr; +static ElfPhdr *phdr[NSECT]; +static ElfShdr *shdr[NSECT]; +static char *interp; + +typedef struct Elfstring Elfstring; +struct Elfstring +{ + char *s; + int off; +}; + +static Elfstring elfstr[100]; +static int nelfstr; + +/* + Initialize the global variable that describes the ELF header. It will be updated as + we write section and prog headers. + */ +void +elfinit(void) +{ + iself = 1; + + switch(thechar) { + // 64-bit architectures + case '6': + elf64 = 1; + hdr.phoff = ELF64HDRSIZE; /* Must be be ELF64HDRSIZE: first PHdr must follow ELF header */ + hdr.shoff = ELF64HDRSIZE; /* Will move as we add PHeaders */ + hdr.ehsize = ELF64HDRSIZE; /* Must be ELF64HDRSIZE */ + hdr.phentsize = ELF64PHDRSIZE; /* Must be ELF64PHDRSIZE */ + hdr.shentsize = ELF64SHDRSIZE; /* Must be ELF64SHDRSIZE */ + break; + + // 32-bit architectures + default: + hdr.phoff = ELF32HDRSIZE; /* Must be be ELF32HDRSIZE: first PHdr must follow ELF header */ + hdr.shoff = ELF32HDRSIZE; /* Will move as we add PHeaders */ + hdr.ehsize = ELF32HDRSIZE; /* Must be ELF32HDRSIZE */ + hdr.phentsize = ELF32PHDRSIZE; /* Must be ELF32PHDRSIZE */ + hdr.shentsize = ELF32SHDRSIZE; /* Must be ELF32SHDRSIZE */ + } +} + +void +elf64phdr(ElfPhdr *e) +{ + LPUT(e->type); + LPUT(e->flags); + VPUT(e->off); + VPUT(e->vaddr); + VPUT(e->paddr); + VPUT(e->filesz); + VPUT(e->memsz); + VPUT(e->align); +} + +void +elf32phdr(ElfPhdr *e) +{ + LPUT(e->type); + LPUT(e->off); + LPUT(e->vaddr); + LPUT(e->paddr); + LPUT(e->filesz); + LPUT(e->memsz); + LPUT(e->flags); + LPUT(e->align); +} + +void +elf64shdr(ElfShdr *e) +{ + LPUT(e->name); + LPUT(e->type); + VPUT(e->flags); + VPUT(e->addr); + VPUT(e->off); + VPUT(e->size); + LPUT(e->link); + LPUT(e->info); + VPUT(e->addralign); + VPUT(e->entsize); +} + +void +elf32shdr(ElfShdr *e) +{ + LPUT(e->name); + LPUT(e->type); + LPUT(e->flags); + LPUT(e->addr); + LPUT(e->off); + LPUT(e->size); + LPUT(e->link); + LPUT(e->info); + LPUT(e->addralign); + LPUT(e->entsize); +} + +uint32 +elfwriteshdrs(void) +{ + int i; + + if (elf64) { + for (i = 0; i < hdr.shnum; i++) + elf64shdr(shdr[i]); + return hdr.shnum * ELF64SHDRSIZE; + } + for (i = 0; i < hdr.shnum; i++) + elf32shdr(shdr[i]); + return hdr.shnum * ELF32SHDRSIZE; +} + +void +elfsetstring(char *s, int off) +{ + if(nelfstr >= nelem(elfstr)) { + diag("too many elf strings"); + errorexit(); + } + elfstr[nelfstr].s = s; + elfstr[nelfstr].off = off; + nelfstr++; +} + +uint32 +elfwritephdrs(void) +{ + int i; + + if (elf64) { + for (i = 0; i < hdr.phnum; i++) + elf64phdr(phdr[i]); + return hdr.phnum * ELF64PHDRSIZE; + } + for (i = 0; i < hdr.phnum; i++) + elf32phdr(phdr[i]); + return hdr.phnum * ELF32PHDRSIZE; +} + +ElfPhdr* +newElfPhdr(void) +{ + ElfPhdr *e; + + e = mal(sizeof *e); + if (hdr.phnum >= NSECT) + diag("too many phdrs"); + else + phdr[hdr.phnum++] = e; + if (elf64) + hdr.shoff += ELF64PHDRSIZE; + else + hdr.shoff += ELF32PHDRSIZE; + return e; +} + +ElfShdr* +newElfShstrtab(vlong name) +{ + hdr.shstrndx = hdr.shnum; + return newElfShdr(name); +} + +ElfShdr* +newElfShdr(vlong name) +{ + ElfShdr *e; + + e = mal(sizeof *e); + e->name = name; + if (hdr.shnum >= NSECT) { + diag("too many shdrs"); + } else { + shdr[hdr.shnum++] = e; + } + return e; +} + +ElfEhdr* +getElfEhdr(void) +{ + return &hdr; +} + +uint32 +elf64writehdr(void) +{ + int i; + + for (i = 0; i < EI_NIDENT; i++) + cput(hdr.ident[i]); + WPUT(hdr.type); + WPUT(hdr.machine); + LPUT(hdr.version); + VPUT(hdr.entry); + VPUT(hdr.phoff); + VPUT(hdr.shoff); + LPUT(hdr.flags); + WPUT(hdr.ehsize); + WPUT(hdr.phentsize); + WPUT(hdr.phnum); + WPUT(hdr.shentsize); + WPUT(hdr.shnum); + WPUT(hdr.shstrndx); + return ELF64HDRSIZE; +} + +uint32 +elf32writehdr(void) +{ + int i; + + for (i = 0; i < EI_NIDENT; i++) + cput(hdr.ident[i]); + WPUT(hdr.type); + WPUT(hdr.machine); + LPUT(hdr.version); + LPUT(hdr.entry); + LPUT(hdr.phoff); + LPUT(hdr.shoff); + LPUT(hdr.flags); + WPUT(hdr.ehsize); + WPUT(hdr.phentsize); + WPUT(hdr.phnum); + WPUT(hdr.shentsize); + WPUT(hdr.shnum); + WPUT(hdr.shstrndx); + return ELF32HDRSIZE; +} + +uint32 +elfwritehdr(void) +{ + if(elf64) + return elf64writehdr(); + return elf32writehdr(); +} + +/* Taken directly from the definition document for ELF64 */ +uint32 +elfhash(uchar *name) +{ + uint32 h = 0, g; + while (*name) { + h = (h << 4) + *name++; + if (g = h & 0xf0000000) + h ^= g >> 24; + h &= 0x0fffffff; + } + return h; +} + +void +elfwritedynent(Sym *s, int tag, uint64 val) +{ + if(elf64) { + adduint64(s, tag); + adduint64(s, val); + } else { + adduint32(s, tag); + adduint32(s, val); + } +} + +void +elfwritedynentsym(Sym *s, int tag, Sym *t) +{ + if(elf64) + adduint64(s, tag); + else + adduint32(s, tag); + addaddr(s, t); +} + +void +elfwritedynentsymsize(Sym *s, int tag, Sym *t) +{ + if(elf64) + adduint64(s, tag); + else + adduint32(s, tag); + addsize(s, t); +} + +int +elfwriteinterp(void) +{ + int n; + + if(interp == nil) + return 0; + + n = strlen(interp)+1; + cseek(ELFRESERVE-n); + cwrite(interp, n); + return n; +} + +void +elfinterp(ElfShdr *sh, uint64 startva, char *p) +{ + int n; + + interp = p; + n = strlen(interp)+1; + sh->addr = startva + ELFRESERVE - n; + sh->off = ELFRESERVE - n; + sh->size = n; +} + +extern int nelfsym; +int elfverneed; + +typedef struct Elfaux Elfaux; +typedef struct Elflib Elflib; + +struct Elflib +{ + Elflib *next; + Elfaux *aux; + char *file; +}; + +struct Elfaux +{ + Elfaux *next; + int num; + char *vers; +}; + +Elfaux* +addelflib(Elflib **list, char *file, char *vers) +{ + Elflib *lib; + Elfaux *aux; + + for(lib=*list; lib; lib=lib->next) + if(strcmp(lib->file, file) == 0) + goto havelib; + lib = mal(sizeof *lib); + lib->next = *list; + lib->file = file; + *list = lib; +havelib: + for(aux=lib->aux; aux; aux=aux->next) + if(strcmp(aux->vers, vers) == 0) + goto haveaux; + aux = mal(sizeof *aux); + aux->next = lib->aux; + aux->vers = vers; + lib->aux = aux; +haveaux: + return aux; +} + +void +elfdynhash(void) +{ + Sym *s, *sy, *dynstr; + int i, j, nbucket, b, nfile; + uint32 hc, *chain, *buckets; + int nsym; + char *name; + Elfaux **need; + Elflib *needlib; + Elflib *l; + Elfaux *x; + + if(!iself) + return; + + nsym = nelfsym; + s = lookup(".hash", 0); + s->type = SELFROSECT; + s->reachable = 1; + + i = nsym; + nbucket = 1; + while(i > 0) { + ++nbucket; + i >>= 1; + } + + needlib = nil; + need = malloc(nsym * sizeof need[0]); + chain = malloc(nsym * sizeof chain[0]); + buckets = malloc(nbucket * sizeof buckets[0]); + if(need == nil || chain == nil || buckets == nil) { + cursym = nil; + diag("out of memory"); + errorexit(); + } + memset(need, 0, nsym * sizeof need[0]); + memset(chain, 0, nsym * sizeof chain[0]); + memset(buckets, 0, nbucket * sizeof buckets[0]); + for(sy=allsym; sy!=S; sy=sy->allsym) { + if (sy->dynid <= 0) + continue; + + if(sy->dynimpvers) + need[sy->dynid] = addelflib(&needlib, sy->dynimplib, sy->dynimpvers); + + name = sy->dynimpname; + if(name == nil) + name = sy->name; + hc = elfhash((uchar*)name); + + b = hc % nbucket; + chain[sy->dynid] = buckets[b]; + buckets[b] = sy->dynid; + } + + adduint32(s, nbucket); + adduint32(s, nsym); + for(i = 0; i<nbucket; i++) + adduint32(s, buckets[i]); + for(i = 0; i<nsym; i++) + adduint32(s, chain[i]); + + free(chain); + free(buckets); + + // version symbols + dynstr = lookup(".dynstr", 0); + s = lookup(".gnu.version_r", 0); + i = 2; + nfile = 0; + for(l=needlib; l; l=l->next) { + nfile++; + // header + adduint16(s, 1); // table version + j = 0; + for(x=l->aux; x; x=x->next) + j++; + adduint16(s, j); // aux count + adduint32(s, addstring(dynstr, l->file)); // file string offset + adduint32(s, 16); // offset from header to first aux + if(l->next) + adduint32(s, 16+j*16); // offset from this header to next + else + adduint32(s, 0); + + for(x=l->aux; x; x=x->next) { + x->num = i++; + // aux struct + adduint32(s, elfhash((uchar*)x->vers)); // hash + adduint16(s, 0); // flags + adduint16(s, x->num); // other - index we refer to this by + adduint32(s, addstring(dynstr, x->vers)); // version string offset + if(x->next) + adduint32(s, 16); // offset from this aux to next + else + adduint32(s, 0); + } + } + + // version references + s = lookup(".gnu.version", 0); + for(i=0; i<nsym; i++) { + if(i == 0) + adduint16(s, 0); // first entry - no symbol + else if(need[i] == nil) + adduint16(s, 1); // global + else + adduint16(s, need[i]->num); + } + + free(need); + + s = lookup(".dynamic", 0); + elfverneed = nfile; + if(elfverneed) { + elfwritedynentsym(s, DT_VERNEED, lookup(".gnu.version_r", 0)); + elfwritedynent(s, DT_VERNEEDNUM, nfile); + elfwritedynentsym(s, DT_VERSYM, lookup(".gnu.version", 0)); + } + elfwritedynent(s, DT_NULL, 0); +} + +ElfPhdr* +elfphload(Segment *seg) +{ + ElfPhdr *ph; + + ph = newElfPhdr(); + ph->type = PT_LOAD; + if(seg->rwx & 4) + ph->flags |= PF_R; + if(seg->rwx & 2) + ph->flags |= PF_W; + if(seg->rwx & 1) + ph->flags |= PF_X; + ph->vaddr = seg->vaddr; + ph->paddr = seg->vaddr; + ph->memsz = seg->len; + ph->off = seg->fileoff; + ph->filesz = seg->filelen; + ph->align = INITRND; + + return ph; +} + +ElfShdr* +elfshbits(Section *sect) +{ + int i, off; + ElfShdr *sh; + + for(i=0; i<nelfstr; i++) { + if(strcmp(sect->name, elfstr[i].s) == 0) { + off = elfstr[i].off; + goto found; + } + } + diag("cannot find elf name %s", sect->name); + errorexit(); + return nil; + +found: + for(i=0; i<hdr.shnum; i++) { + sh = shdr[i]; + if(sh->name == off) + return sh; + } + + sh = newElfShdr(off); + if(sect->vaddr < sect->seg->vaddr + sect->seg->filelen) + sh->type = SHT_PROGBITS; + else + sh->type = SHT_NOBITS; + sh->flags = SHF_ALLOC; + if(sect->rwx & 1) + sh->flags |= SHF_EXECINSTR; + if(sect->rwx & 2) + sh->flags |= SHF_WRITE; + sh->addr = sect->vaddr; + sh->addralign = PtrSize; + sh->size = sect->len; + sh->off = sect->seg->fileoff + sect->vaddr - sect->seg->vaddr; + + return sh; +} diff --git a/src/cmd/ld/elf.h b/src/cmd/ld/elf.h new file mode 100644 index 000000000..c63df2241 --- /dev/null +++ b/src/cmd/ld/elf.h @@ -0,0 +1,989 @@ +/* + * Derived from: + * $FreeBSD: src/sys/sys/elf32.h,v 1.8.14.1 2005/12/30 22:13:58 marcel Exp $ + * $FreeBSD: src/sys/sys/elf64.h,v 1.10.14.1 2005/12/30 22:13:58 marcel Exp $ + * $FreeBSD: src/sys/sys/elf_common.h,v 1.15.8.1 2005/12/30 22:13:58 marcel Exp $ + * $FreeBSD: src/sys/alpha/include/elf.h,v 1.14 2003/09/25 01:10:22 peter Exp $ + * $FreeBSD: src/sys/amd64/include/elf.h,v 1.18 2004/08/03 08:21:48 dfr Exp $ + * $FreeBSD: src/sys/arm/include/elf.h,v 1.5.2.1 2006/06/30 21:42:52 cognet Exp $ + * $FreeBSD: src/sys/i386/include/elf.h,v 1.16 2004/08/02 19:12:17 dfr Exp $ + * $FreeBSD: src/sys/powerpc/include/elf.h,v 1.7 2004/11/02 09:47:01 ssouhlal Exp $ + * $FreeBSD: src/sys/sparc64/include/elf.h,v 1.12 2003/09/25 01:10:26 peter Exp $ + * + * Copyright (c) 1996-1998 John D. Polstra. All rights reserved. + * Copyright (c) 2001 David E. O'Brien + * Portions Copyright 2009 The Go Authors. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* + * ELF definitions that are independent of architecture or word size. + */ + +/* + * Note header. The ".note" section contains an array of notes. Each + * begins with this header, aligned to a word boundary. Immediately + * following the note header is n_namesz bytes of name, padded to the + * next word boundary. Then comes n_descsz bytes of descriptor, again + * padded to a word boundary. The values of n_namesz and n_descsz do + * not include the padding. + */ + +typedef struct { + uint32 n_namesz; /* Length of name. */ + uint32 n_descsz; /* Length of descriptor. */ + uint32 n_type; /* Type of this note. */ +} Elf_Note; + +/* Indexes into the e_ident array. Keep synced with + http://www.sco.com/developer/gabi/ch4.eheader.html */ +#define EI_MAG0 0 /* Magic number, byte 0. */ +#define EI_MAG1 1 /* Magic number, byte 1. */ +#define EI_MAG2 2 /* Magic number, byte 2. */ +#define EI_MAG3 3 /* Magic number, byte 3. */ +#define EI_CLASS 4 /* Class of machine. */ +#define EI_DATA 5 /* Data format. */ +#define EI_VERSION 6 /* ELF format version. */ +#define EI_OSABI 7 /* Operating system / ABI identification */ +#define EI_ABIVERSION 8 /* ABI version */ +#define OLD_EI_BRAND 8 /* Start of architecture identification. */ +#define EI_PAD 9 /* Start of padding (per SVR4 ABI). */ +#define EI_NIDENT 16 /* Size of e_ident array. */ + +/* Values for the magic number bytes. */ +#define ELFMAG0 0x7f +#define ELFMAG1 'E' +#define ELFMAG2 'L' +#define ELFMAG3 'F' +#define ELFMAG "\177ELF" /* magic string */ +#define SELFMAG 4 /* magic string size */ + +/* Values for e_ident[EI_VERSION] and e_version. */ +#define EV_NONE 0 +#define EV_CURRENT 1 + +/* Values for e_ident[EI_CLASS]. */ +#define ELFCLASSNONE 0 /* Unknown class. */ +#define ELFCLASS32 1 /* 32-bit architecture. */ +#define ELFCLASS64 2 /* 64-bit architecture. */ + +/* Values for e_ident[EI_DATA]. */ +#define ELFDATANONE 0 /* Unknown data format. */ +#define ELFDATA2LSB 1 /* 2's complement little-endian. */ +#define ELFDATA2MSB 2 /* 2's complement big-endian. */ + +/* Values for e_ident[EI_OSABI]. */ +#define ELFOSABI_NONE 0 /* UNIX System V ABI */ +#define ELFOSABI_HPUX 1 /* HP-UX operating system */ +#define ELFOSABI_NETBSD 2 /* NetBSD */ +#define ELFOSABI_LINUX 3 /* GNU/Linux */ +#define ELFOSABI_HURD 4 /* GNU/Hurd */ +#define ELFOSABI_86OPEN 5 /* 86Open common IA32 ABI */ +#define ELFOSABI_SOLARIS 6 /* Solaris */ +#define ELFOSABI_AIX 7 /* AIX */ +#define ELFOSABI_IRIX 8 /* IRIX */ +#define ELFOSABI_FREEBSD 9 /* FreeBSD */ +#define ELFOSABI_TRU64 10 /* TRU64 UNIX */ +#define ELFOSABI_MODESTO 11 /* Novell Modesto */ +#define ELFOSABI_OPENBSD 12 /* OpenBSD */ +#define ELFOSABI_OPENVMS 13 /* Open VMS */ +#define ELFOSABI_NSK 14 /* HP Non-Stop Kernel */ +#define ELFOSABI_ARM 97 /* ARM */ +#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ + +#define ELFOSABI_SYSV ELFOSABI_NONE /* symbol used in old spec */ +#define ELFOSABI_MONTEREY ELFOSABI_AIX /* Monterey */ + +/* e_ident */ +#define IS_ELF(ehdr) ((ehdr).e_ident[EI_MAG0] == ELFMAG0 && \ + (ehdr).e_ident[EI_MAG1] == ELFMAG1 && \ + (ehdr).e_ident[EI_MAG2] == ELFMAG2 && \ + (ehdr).e_ident[EI_MAG3] == ELFMAG3) + +/* Values for e_type. */ +#define ET_NONE 0 /* Unknown type. */ +#define ET_REL 1 /* Relocatable. */ +#define ET_EXEC 2 /* Executable. */ +#define ET_DYN 3 /* Shared object. */ +#define ET_CORE 4 /* Core file. */ +#define ET_LOOS 0xfe00 /* First operating system specific. */ +#define ET_HIOS 0xfeff /* Last operating system-specific. */ +#define ET_LOPROC 0xff00 /* First processor-specific. */ +#define ET_HIPROC 0xffff /* Last processor-specific. */ + +/* Values for e_machine. */ +#define EM_NONE 0 /* Unknown machine. */ +#define EM_M32 1 /* AT&T WE32100. */ +#define EM_SPARC 2 /* Sun SPARC. */ +#define EM_386 3 /* Intel i386. */ +#define EM_68K 4 /* Motorola 68000. */ +#define EM_88K 5 /* Motorola 88000. */ +#define EM_860 7 /* Intel i860. */ +#define EM_MIPS 8 /* MIPS R3000 Big-Endian only. */ +#define EM_S370 9 /* IBM System/370. */ +#define EM_MIPS_RS3_LE 10 /* MIPS R3000 Little-Endian. */ +#define EM_PARISC 15 /* HP PA-RISC. */ +#define EM_VPP500 17 /* Fujitsu VPP500. */ +#define EM_SPARC32PLUS 18 /* SPARC v8plus. */ +#define EM_960 19 /* Intel 80960. */ +#define EM_PPC 20 /* PowerPC 32-bit. */ +#define EM_PPC64 21 /* PowerPC 64-bit. */ +#define EM_S390 22 /* IBM System/390. */ +#define EM_V800 36 /* NEC V800. */ +#define EM_FR20 37 /* Fujitsu FR20. */ +#define EM_RH32 38 /* TRW RH-32. */ +#define EM_RCE 39 /* Motorola RCE. */ +#define EM_ARM 40 /* ARM. */ +#define EM_SH 42 /* Hitachi SH. */ +#define EM_SPARCV9 43 /* SPARC v9 64-bit. */ +#define EM_TRICORE 44 /* Siemens TriCore embedded processor. */ +#define EM_ARC 45 /* Argonaut RISC Core. */ +#define EM_H8_300 46 /* Hitachi H8/300. */ +#define EM_H8_300H 47 /* Hitachi H8/300H. */ +#define EM_H8S 48 /* Hitachi H8S. */ +#define EM_H8_500 49 /* Hitachi H8/500. */ +#define EM_IA_64 50 /* Intel IA-64 Processor. */ +#define EM_MIPS_X 51 /* Stanford MIPS-X. */ +#define EM_COLDFIRE 52 /* Motorola ColdFire. */ +#define EM_68HC12 53 /* Motorola M68HC12. */ +#define EM_MMA 54 /* Fujitsu MMA. */ +#define EM_PCP 55 /* Siemens PCP. */ +#define EM_NCPU 56 /* Sony nCPU. */ +#define EM_NDR1 57 /* Denso NDR1 microprocessor. */ +#define EM_STARCORE 58 /* Motorola Star*Core processor. */ +#define EM_ME16 59 /* Toyota ME16 processor. */ +#define EM_ST100 60 /* STMicroelectronics ST100 processor. */ +#define EM_TINYJ 61 /* Advanced Logic Corp. TinyJ processor. */ +#define EM_X86_64 62 /* Advanced Micro Devices x86-64 */ + +/* Non-standard or deprecated. */ +#define EM_486 6 /* Intel i486. */ +#define EM_MIPS_RS4_BE 10 /* MIPS R4000 Big-Endian */ +#define EM_ALPHA_STD 41 /* Digital Alpha (standard value). */ +#define EM_ALPHA 0x9026 /* Alpha (written in the absence of an ABI) */ + +/* Special section indexes. */ +#define SHN_UNDEF 0 /* Undefined, missing, irrelevant. */ +#define SHN_LORESERVE 0xff00 /* First of reserved range. */ +#define SHN_LOPROC 0xff00 /* First processor-specific. */ +#define SHN_HIPROC 0xff1f /* Last processor-specific. */ +#define SHN_LOOS 0xff20 /* First operating system-specific. */ +#define SHN_HIOS 0xff3f /* Last operating system-specific. */ +#define SHN_ABS 0xfff1 /* Absolute values. */ +#define SHN_COMMON 0xfff2 /* Common data. */ +#define SHN_XINDEX 0xffff /* Escape -- index stored elsewhere. */ +#define SHN_HIRESERVE 0xffff /* Last of reserved range. */ + +/* sh_type */ +#define SHT_NULL 0 /* inactive */ +#define SHT_PROGBITS 1 /* program defined information */ +#define SHT_SYMTAB 2 /* symbol table section */ +#define SHT_STRTAB 3 /* string table section */ +#define SHT_RELA 4 /* relocation section with addends */ +#define SHT_HASH 5 /* symbol hash table section */ +#define SHT_DYNAMIC 6 /* dynamic section */ +#define SHT_NOTE 7 /* note section */ +#define SHT_NOBITS 8 /* no space section */ +#define SHT_REL 9 /* relocation section - no addends */ +#define SHT_SHLIB 10 /* reserved - purpose unknown */ +#define SHT_DYNSYM 11 /* dynamic symbol table section */ +#define SHT_INIT_ARRAY 14 /* Initialization function pointers. */ +#define SHT_FINI_ARRAY 15 /* Termination function pointers. */ +#define SHT_PREINIT_ARRAY 16 /* Pre-initialization function ptrs. */ +#define SHT_GROUP 17 /* Section group. */ +#define SHT_SYMTAB_SHNDX 18 /* Section indexes (see SHN_XINDEX). */ +#define SHT_LOOS 0x60000000 /* First of OS specific semantics */ +#define SHT_HIOS 0x6fffffff /* Last of OS specific semantics */ +#define SHT_GNU_VERDEF 0x6ffffffd +#define SHT_GNU_VERNEED 0x6ffffffe +#define SHT_GNU_VERSYM 0x6fffffff +#define SHT_LOPROC 0x70000000 /* reserved range for processor */ +#define SHT_HIPROC 0x7fffffff /* specific section header types */ +#define SHT_LOUSER 0x80000000 /* reserved range for application */ +#define SHT_HIUSER 0xffffffff /* specific indexes */ + +/* Flags for sh_flags. */ +#define SHF_WRITE 0x1 /* Section contains writable data. */ +#define SHF_ALLOC 0x2 /* Section occupies memory. */ +#define SHF_EXECINSTR 0x4 /* Section contains instructions. */ +#define SHF_MERGE 0x10 /* Section may be merged. */ +#define SHF_STRINGS 0x20 /* Section contains strings. */ +#define SHF_INFO_LINK 0x40 /* sh_info holds section index. */ +#define SHF_LINK_ORDER 0x80 /* Special ordering requirements. */ +#define SHF_OS_NONCONFORMING 0x100 /* OS-specific processing required. */ +#define SHF_GROUP 0x200 /* Member of section group. */ +#define SHF_TLS 0x400 /* Section contains TLS data. */ +#define SHF_MASKOS 0x0ff00000 /* OS-specific semantics. */ +#define SHF_MASKPROC 0xf0000000 /* Processor-specific semantics. */ + +/* Values for p_type. */ +#define PT_NULL 0 /* Unused entry. */ +#define PT_LOAD 1 /* Loadable segment. */ +#define PT_DYNAMIC 2 /* Dynamic linking information segment. */ +#define PT_INTERP 3 /* Pathname of interpreter. */ +#define PT_NOTE 4 /* Auxiliary information. */ +#define PT_SHLIB 5 /* Reserved (not used). */ +#define PT_PHDR 6 /* Location of program header itself. */ +#define PT_TLS 7 /* Thread local storage segment */ +#define PT_LOOS 0x60000000 /* First OS-specific. */ +#define PT_HIOS 0x6fffffff /* Last OS-specific. */ +#define PT_LOPROC 0x70000000 /* First processor-specific type. */ +#define PT_HIPROC 0x7fffffff /* Last processor-specific type. */ +#define PT_GNU_STACK 0x6474e551 + +/* Values for p_flags. */ +#define PF_X 0x1 /* Executable. */ +#define PF_W 0x2 /* Writable. */ +#define PF_R 0x4 /* Readable. */ +#define PF_MASKOS 0x0ff00000 /* Operating system-specific. */ +#define PF_MASKPROC 0xf0000000 /* Processor-specific. */ + +/* Values for d_tag. */ +#define DT_NULL 0 /* Terminating entry. */ +/* String table offset of a needed shared library. */ +#define DT_NEEDED 1 +#define DT_PLTRELSZ 2 /* Total size in bytes of PLT relocations. */ +#define DT_PLTGOT 3 /* Processor-dependent address. */ +#define DT_HASH 4 /* Address of symbol hash table. */ +#define DT_STRTAB 5 /* Address of string table. */ +#define DT_SYMTAB 6 /* Address of symbol table. */ +#define DT_RELA 7 /* Address of ElfNN_Rela relocations. */ +#define DT_RELASZ 8 /* Total size of ElfNN_Rela relocations. */ +#define DT_RELAENT 9 /* Size of each ElfNN_Rela relocation entry. */ +#define DT_STRSZ 10 /* Size of string table. */ +#define DT_SYMENT 11 /* Size of each symbol table entry. */ +#define DT_INIT 12 /* Address of initialization function. */ +#define DT_FINI 13 /* Address of finalization function. */ +/* String table offset of shared object name. */ +#define DT_SONAME 14 +#define DT_RPATH 15 /* String table offset of library path. [sup] */ +#define DT_SYMBOLIC 16 /* Indicates "symbolic" linking. [sup] */ +#define DT_REL 17 /* Address of ElfNN_Rel relocations. */ +#define DT_RELSZ 18 /* Total size of ElfNN_Rel relocations. */ +#define DT_RELENT 19 /* Size of each ElfNN_Rel relocation. */ +#define DT_PLTREL 20 /* Type of relocation used for PLT. */ +#define DT_DEBUG 21 /* Reserved (not used). */ +/* Indicates there may be relocations in non-writable segments. [sup] */ +#define DT_TEXTREL 22 +#define DT_JMPREL 23 /* Address of PLT relocations. */ +#define DT_BIND_NOW 24 /* [sup] */ +/* Address of the array of pointers to initialization functions */ +#define DT_INIT_ARRAY 25 +/* Address of the array of pointers to termination functions */ +#define DT_FINI_ARRAY 26 +/* Size in bytes of the array of initialization functions. */ +#define DT_INIT_ARRAYSZ 27 +/* Size in bytes of the array of terminationfunctions. */ +#define DT_FINI_ARRAYSZ 28 +/* String table offset of a null-terminated library search path string. */ +#define DT_RUNPATH 29 +#define DT_FLAGS 30 /* Object specific flag values. */ +/* Values greater than or equal to DT_ENCODING and less than + DT_LOOS follow the rules for the interpretation of the d_un + union as follows: even == 'd_ptr', even == 'd_val' or none */ +#define DT_ENCODING 32 +/* Address of the array of pointers to pre-initialization functions. */ +#define DT_PREINIT_ARRAY 32 +/* Size in bytes of the array of pre-initialization functions. */ +#define DT_PREINIT_ARRAYSZ 33 +#define DT_LOOS 0x6000000d /* First OS-specific */ +#define DT_HIOS 0x6ffff000 /* Last OS-specific */ +#define DT_LOPROC 0x70000000 /* First processor-specific type. */ +#define DT_HIPROC 0x7fffffff /* Last processor-specific type. */ + +#define DT_VERNEED 0x6ffffffe +#define DT_VERNEEDNUM 0x6fffffff +#define DT_VERSYM 0x6ffffff0 + +/* Values for DT_FLAGS */ +/* Indicates that the object being loaded may make reference to + the $ORIGIN substitution string */ +#define DF_ORIGIN 0x0001 +#define DF_SYMBOLIC 0x0002 /* Indicates "symbolic" linking. */ +/* Indicates there may be relocations in non-writable segments. */ +#define DF_TEXTREL 0x0004 +/* Indicates that the dynamic linker should process all + relocations for the object containing this entry before + transferring control to the program. */ +#define DF_BIND_NOW 0x0008 +/* Indicates that the shared object or executable contains code + using a static thread-local storage scheme. */ +#define DF_STATIC_TLS 0x0010 + +/* Values for n_type. Used in core files. */ +#define NT_PRSTATUS 1 /* Process status. */ +#define NT_FPREGSET 2 /* Floating point registers. */ +#define NT_PRPSINFO 3 /* Process state info. */ + +/* Symbol Binding - ELFNN_ST_BIND - st_info */ +#define STB_LOCAL 0 /* Local symbol */ +#define STB_GLOBAL 1 /* Global symbol */ +#define STB_WEAK 2 /* like global - lower precedence */ +#define STB_LOOS 10 /* Reserved range for operating system */ +#define STB_HIOS 12 /* specific semantics. */ +#define STB_LOPROC 13 /* reserved range for processor */ +#define STB_HIPROC 15 /* specific semantics. */ + +/* Symbol type - ELFNN_ST_TYPE - st_info */ +#define STT_NOTYPE 0 /* Unspecified type. */ +#define STT_OBJECT 1 /* Data object. */ +#define STT_FUNC 2 /* Function. */ +#define STT_SECTION 3 /* Section. */ +#define STT_FILE 4 /* Source file. */ +#define STT_COMMON 5 /* Uninitialized common block. */ +#define STT_TLS 6 /* TLS object. */ +#define STT_LOOS 10 /* Reserved range for operating system */ +#define STT_HIOS 12 /* specific semantics. */ +#define STT_LOPROC 13 /* reserved range for processor */ +#define STT_HIPROC 15 /* specific semantics. */ + +/* Symbol visibility - ELFNN_ST_VISIBILITY - st_other */ +#define STV_DEFAULT 0x0 /* Default visibility (see binding). */ +#define STV_INTERNAL 0x1 /* Special meaning in relocatable objects. */ +#define STV_HIDDEN 0x2 /* Not visible. */ +#define STV_PROTECTED 0x3 /* Visible but not preemptible. */ + +/* Special symbol table indexes. */ +#define STN_UNDEF 0 /* Undefined symbol index. */ + +/* + * ELF definitions common to all 32-bit architectures. + */ + +typedef uint32 Elf32_Addr; +typedef uint16 Elf32_Half; +typedef uint32 Elf32_Off; +typedef int32 Elf32_Sword; +typedef uint32 Elf32_Word; + +typedef Elf32_Word Elf32_Hashelt; + +/* Non-standard class-dependent datatype used for abstraction. */ +typedef Elf32_Word Elf32_Size; +typedef Elf32_Sword Elf32_Ssize; + +/* + * ELF header. + */ + +typedef struct { + unsigned char ident[EI_NIDENT]; /* File identification. */ + Elf32_Half type; /* File type. */ + Elf32_Half machine; /* Machine architecture. */ + Elf32_Word version; /* ELF format version. */ + Elf32_Addr entry; /* Entry point. */ + Elf32_Off phoff; /* Program header file offset. */ + Elf32_Off shoff; /* Section header file offset. */ + Elf32_Word flags; /* Architecture-specific flags. */ + Elf32_Half ehsize; /* Size of ELF header in bytes. */ + Elf32_Half phentsize; /* Size of program header entry. */ + Elf32_Half phnum; /* Number of program header entries. */ + Elf32_Half shentsize; /* Size of section header entry. */ + Elf32_Half shnum; /* Number of section header entries. */ + Elf32_Half shstrndx; /* Section name strings section. */ +} Elf32_Ehdr; + +/* + * Section header. + */ + +typedef struct { + Elf32_Word name; /* Section name (index into the + section header string table). */ + Elf32_Word type; /* Section type. */ + Elf32_Word flags; /* Section flags. */ + Elf32_Addr vaddr; /* Address in memory image. */ + Elf32_Off off; /* Offset in file. */ + Elf32_Word size; /* Size in bytes. */ + Elf32_Word link; /* Index of a related section. */ + Elf32_Word info; /* Depends on section type. */ + Elf32_Word addralign; /* Alignment in bytes. */ + Elf32_Word entsize; /* Size of each entry in section. */ +} Elf32_Shdr; + +/* + * Program header. + */ + +typedef struct { + Elf32_Word type; /* Entry type. */ + Elf32_Off off; /* File offset of contents. */ + Elf32_Addr vaddr; /* Virtual address in memory image. */ + Elf32_Addr paddr; /* Physical address (not used). */ + Elf32_Word filesz; /* Size of contents in file. */ + Elf32_Word memsz; /* Size of contents in memory. */ + Elf32_Word flags; /* Access permission flags. */ + Elf32_Word align; /* Alignment in memory and file. */ +} Elf32_Phdr; + +/* + * Dynamic structure. The ".dynamic" section contains an array of them. + */ + +typedef struct { + Elf32_Sword d_tag; /* Entry type. */ + union { + Elf32_Word d_val; /* Integer value. */ + Elf32_Addr d_ptr; /* Address value. */ + } d_un; +} Elf32_Dyn; + +/* + * Relocation entries. + */ + +/* Relocations that don't need an addend field. */ +typedef struct { + Elf32_Addr off; /* Location to be relocated. */ + Elf32_Word info; /* Relocation type and symbol index. */ +} Elf32_Rel; + +/* Relocations that need an addend field. */ +typedef struct { + Elf32_Addr off; /* Location to be relocated. */ + Elf32_Word info; /* Relocation type and symbol index. */ + Elf32_Sword addend; /* Addend. */ +} Elf32_Rela; + +/* Macros for accessing the fields of r_info. */ +#define ELF32_R_SYM(info) ((info) >> 8) +#define ELF32_R_TYPE(info) ((unsigned char)(info)) + +/* Macro for constructing r_info from field values. */ +#define ELF32_R_INFO(sym, type) (((sym) << 8) + (unsigned char)(type)) + +/* + * Relocation types. + */ + +#define R_X86_64_NONE 0 /* No relocation. */ +#define R_X86_64_64 1 /* Add 64 bit symbol value. */ +#define R_X86_64_PC32 2 /* PC-relative 32 bit signed sym value. */ +#define R_X86_64_GOT32 3 /* PC-relative 32 bit GOT offset. */ +#define R_X86_64_PLT32 4 /* PC-relative 32 bit PLT offset. */ +#define R_X86_64_COPY 5 /* Copy data from shared object. */ +#define R_X86_64_GLOB_DAT 6 /* Set GOT entry to data address. */ +#define R_X86_64_JMP_SLOT 7 /* Set GOT entry to code address. */ +#define R_X86_64_RELATIVE 8 /* Add load address of shared object. */ +#define R_X86_64_GOTPCREL 9 /* Add 32 bit signed pcrel offset to GOT. */ +#define R_X86_64_32 10 /* Add 32 bit zero extended symbol value */ +#define R_X86_64_32S 11 /* Add 32 bit sign extended symbol value */ +#define R_X86_64_16 12 /* Add 16 bit zero extended symbol value */ +#define R_X86_64_PC16 13 /* Add 16 bit signed extended pc relative symbol value */ +#define R_X86_64_8 14 /* Add 8 bit zero extended symbol value */ +#define R_X86_64_PC8 15 /* Add 8 bit signed extended pc relative symbol value */ +#define R_X86_64_DTPMOD64 16 /* ID of module containing symbol */ +#define R_X86_64_DTPOFF64 17 /* Offset in TLS block */ +#define R_X86_64_TPOFF64 18 /* Offset in static TLS block */ +#define R_X86_64_TLSGD 19 /* PC relative offset to GD GOT entry */ +#define R_X86_64_TLSLD 20 /* PC relative offset to LD GOT entry */ +#define R_X86_64_DTPOFF32 21 /* Offset in TLS block */ +#define R_X86_64_GOTTPOFF 22 /* PC relative offset to IE GOT entry */ +#define R_X86_64_TPOFF32 23 /* Offset in static TLS block */ + +#define R_X86_64_COUNT 24 /* Count of defined relocation types. */ + + +#define R_ALPHA_NONE 0 /* No reloc */ +#define R_ALPHA_REFLONG 1 /* Direct 32 bit */ +#define R_ALPHA_REFQUAD 2 /* Direct 64 bit */ +#define R_ALPHA_GPREL32 3 /* GP relative 32 bit */ +#define R_ALPHA_LITERAL 4 /* GP relative 16 bit w/optimization */ +#define R_ALPHA_LITUSE 5 /* Optimization hint for LITERAL */ +#define R_ALPHA_GPDISP 6 /* Add displacement to GP */ +#define R_ALPHA_BRADDR 7 /* PC+4 relative 23 bit shifted */ +#define R_ALPHA_HINT 8 /* PC+4 relative 16 bit shifted */ +#define R_ALPHA_SREL16 9 /* PC relative 16 bit */ +#define R_ALPHA_SREL32 10 /* PC relative 32 bit */ +#define R_ALPHA_SREL64 11 /* PC relative 64 bit */ +#define R_ALPHA_OP_PUSH 12 /* OP stack push */ +#define R_ALPHA_OP_STORE 13 /* OP stack pop and store */ +#define R_ALPHA_OP_PSUB 14 /* OP stack subtract */ +#define R_ALPHA_OP_PRSHIFT 15 /* OP stack right shift */ +#define R_ALPHA_GPVALUE 16 +#define R_ALPHA_GPRELHIGH 17 +#define R_ALPHA_GPRELLOW 18 +#define R_ALPHA_IMMED_GP_16 19 +#define R_ALPHA_IMMED_GP_HI32 20 +#define R_ALPHA_IMMED_SCN_HI32 21 +#define R_ALPHA_IMMED_BR_HI32 22 +#define R_ALPHA_IMMED_LO32 23 +#define R_ALPHA_COPY 24 /* Copy symbol at runtime */ +#define R_ALPHA_GLOB_DAT 25 /* Create GOT entry */ +#define R_ALPHA_JMP_SLOT 26 /* Create PLT entry */ +#define R_ALPHA_RELATIVE 27 /* Adjust by program base */ + +#define R_ALPHA_COUNT 28 + + +#define R_ARM_NONE 0 /* No relocation. */ +#define R_ARM_PC24 1 +#define R_ARM_ABS32 2 +#define R_ARM_REL32 3 +#define R_ARM_PC13 4 +#define R_ARM_ABS16 5 +#define R_ARM_ABS12 6 +#define R_ARM_THM_ABS5 7 +#define R_ARM_ABS8 8 +#define R_ARM_SBREL32 9 +#define R_ARM_THM_PC22 10 +#define R_ARM_THM_PC8 11 +#define R_ARM_AMP_VCALL9 12 +#define R_ARM_SWI24 13 +#define R_ARM_THM_SWI8 14 +#define R_ARM_XPC25 15 +#define R_ARM_THM_XPC22 16 +#define R_ARM_COPY 20 /* Copy data from shared object. */ +#define R_ARM_GLOB_DAT 21 /* Set GOT entry to data address. */ +#define R_ARM_JUMP_SLOT 22 /* Set GOT entry to code address. */ +#define R_ARM_RELATIVE 23 /* Add load address of shared object. */ +#define R_ARM_GOTOFF 24 /* Add GOT-relative symbol address. */ +#define R_ARM_GOTPC 25 /* Add PC-relative GOT table address. */ +#define R_ARM_GOT32 26 /* Add PC-relative GOT offset. */ +#define R_ARM_PLT32 27 /* Add PC-relative PLT offset. */ +#define R_ARM_GNU_VTENTRY 100 +#define R_ARM_GNU_VTINHERIT 101 +#define R_ARM_RSBREL32 250 +#define R_ARM_THM_RPC22 251 +#define R_ARM_RREL32 252 +#define R_ARM_RABS32 253 +#define R_ARM_RPC24 254 +#define R_ARM_RBASE 255 + +#define R_ARM_COUNT 33 /* Count of defined relocation types. */ + + +#define R_386_NONE 0 /* No relocation. */ +#define R_386_32 1 /* Add symbol value. */ +#define R_386_PC32 2 /* Add PC-relative symbol value. */ +#define R_386_GOT32 3 /* Add PC-relative GOT offset. */ +#define R_386_PLT32 4 /* Add PC-relative PLT offset. */ +#define R_386_COPY 5 /* Copy data from shared object. */ +#define R_386_GLOB_DAT 6 /* Set GOT entry to data address. */ +#define R_386_JMP_SLOT 7 /* Set GOT entry to code address. */ +#define R_386_RELATIVE 8 /* Add load address of shared object. */ +#define R_386_GOTOFF 9 /* Add GOT-relative symbol address. */ +#define R_386_GOTPC 10 /* Add PC-relative GOT table address. */ +#define R_386_TLS_TPOFF 14 /* Negative offset in static TLS block */ +#define R_386_TLS_IE 15 /* Absolute address of GOT for -ve static TLS */ +#define R_386_TLS_GOTIE 16 /* GOT entry for negative static TLS block */ +#define R_386_TLS_LE 17 /* Negative offset relative to static TLS */ +#define R_386_TLS_GD 18 /* 32 bit offset to GOT (index,off) pair */ +#define R_386_TLS_LDM 19 /* 32 bit offset to GOT (index,zero) pair */ +#define R_386_TLS_GD_32 24 /* 32 bit offset to GOT (index,off) pair */ +#define R_386_TLS_GD_PUSH 25 /* pushl instruction for Sun ABI GD sequence */ +#define R_386_TLS_GD_CALL 26 /* call instruction for Sun ABI GD sequence */ +#define R_386_TLS_GD_POP 27 /* popl instruction for Sun ABI GD sequence */ +#define R_386_TLS_LDM_32 28 /* 32 bit offset to GOT (index,zero) pair */ +#define R_386_TLS_LDM_PUSH 29 /* pushl instruction for Sun ABI LD sequence */ +#define R_386_TLS_LDM_CALL 30 /* call instruction for Sun ABI LD sequence */ +#define R_386_TLS_LDM_POP 31 /* popl instruction for Sun ABI LD sequence */ +#define R_386_TLS_LDO_32 32 /* 32 bit offset from start of TLS block */ +#define R_386_TLS_IE_32 33 /* 32 bit offset to GOT static TLS offset entry */ +#define R_386_TLS_LE_32 34 /* 32 bit offset within static TLS block */ +#define R_386_TLS_DTPMOD32 35 /* GOT entry containing TLS index */ +#define R_386_TLS_DTPOFF32 36 /* GOT entry containing TLS offset */ +#define R_386_TLS_TPOFF32 37 /* GOT entry of -ve static TLS offset */ + +#define R_386_COUNT 38 /* Count of defined relocation types. */ + +#define R_PPC_NONE 0 /* No relocation. */ +#define R_PPC_ADDR32 1 +#define R_PPC_ADDR24 2 +#define R_PPC_ADDR16 3 +#define R_PPC_ADDR16_LO 4 +#define R_PPC_ADDR16_HI 5 +#define R_PPC_ADDR16_HA 6 +#define R_PPC_ADDR14 7 +#define R_PPC_ADDR14_BRTAKEN 8 +#define R_PPC_ADDR14_BRNTAKEN 9 +#define R_PPC_REL24 10 +#define R_PPC_REL14 11 +#define R_PPC_REL14_BRTAKEN 12 +#define R_PPC_REL14_BRNTAKEN 13 +#define R_PPC_GOT16 14 +#define R_PPC_GOT16_LO 15 +#define R_PPC_GOT16_HI 16 +#define R_PPC_GOT16_HA 17 +#define R_PPC_PLTREL24 18 +#define R_PPC_COPY 19 +#define R_PPC_GLOB_DAT 20 +#define R_PPC_JMP_SLOT 21 +#define R_PPC_RELATIVE 22 +#define R_PPC_LOCAL24PC 23 +#define R_PPC_UADDR32 24 +#define R_PPC_UADDR16 25 +#define R_PPC_REL32 26 +#define R_PPC_PLT32 27 +#define R_PPC_PLTREL32 28 +#define R_PPC_PLT16_LO 29 +#define R_PPC_PLT16_HI 30 +#define R_PPC_PLT16_HA 31 +#define R_PPC_SDAREL16 32 +#define R_PPC_SECTOFF 33 +#define R_PPC_SECTOFF_LO 34 +#define R_PPC_SECTOFF_HI 35 +#define R_PPC_SECTOFF_HA 36 + +#define R_PPC_COUNT 37 /* Count of defined relocation types. */ + +#define R_PPC_TLS 67 +#define R_PPC_DTPMOD32 68 +#define R_PPC_TPREL16 69 +#define R_PPC_TPREL16_LO 70 +#define R_PPC_TPREL16_HI 71 +#define R_PPC_TPREL16_HA 72 +#define R_PPC_TPREL32 73 +#define R_PPC_DTPREL16 74 +#define R_PPC_DTPREL16_LO 75 +#define R_PPC_DTPREL16_HI 76 +#define R_PPC_DTPREL16_HA 77 +#define R_PPC_DTPREL32 78 +#define R_PPC_GOT_TLSGD16 79 +#define R_PPC_GOT_TLSGD16_LO 80 +#define R_PPC_GOT_TLSGD16_HI 81 +#define R_PPC_GOT_TLSGD16_HA 82 +#define R_PPC_GOT_TLSLD16 83 +#define R_PPC_GOT_TLSLD16_LO 84 +#define R_PPC_GOT_TLSLD16_HI 85 +#define R_PPC_GOT_TLSLD16_HA 86 +#define R_PPC_GOT_TPREL16 87 +#define R_PPC_GOT_TPREL16_LO 88 +#define R_PPC_GOT_TPREL16_HI 89 +#define R_PPC_GOT_TPREL16_HA 90 + +#define R_PPC_EMB_NADDR32 101 +#define R_PPC_EMB_NADDR16 102 +#define R_PPC_EMB_NADDR16_LO 103 +#define R_PPC_EMB_NADDR16_HI 104 +#define R_PPC_EMB_NADDR16_HA 105 +#define R_PPC_EMB_SDAI16 106 +#define R_PPC_EMB_SDA2I16 107 +#define R_PPC_EMB_SDA2REL 108 +#define R_PPC_EMB_SDA21 109 +#define R_PPC_EMB_MRKREF 110 +#define R_PPC_EMB_RELSEC16 111 +#define R_PPC_EMB_RELST_LO 112 +#define R_PPC_EMB_RELST_HI 113 +#define R_PPC_EMB_RELST_HA 114 +#define R_PPC_EMB_BIT_FLD 115 +#define R_PPC_EMB_RELSDA 116 + + /* Count of defined relocation types. */ +#define R_PPC_EMB_COUNT (R_PPC_EMB_RELSDA - R_PPC_EMB_NADDR32 + 1) + + +#define R_SPARC_NONE 0 +#define R_SPARC_8 1 +#define R_SPARC_16 2 +#define R_SPARC_32 3 +#define R_SPARC_DISP8 4 +#define R_SPARC_DISP16 5 +#define R_SPARC_DISP32 6 +#define R_SPARC_WDISP30 7 +#define R_SPARC_WDISP22 8 +#define R_SPARC_HI22 9 +#define R_SPARC_22 10 +#define R_SPARC_13 11 +#define R_SPARC_LO10 12 +#define R_SPARC_GOT10 13 +#define R_SPARC_GOT13 14 +#define R_SPARC_GOT22 15 +#define R_SPARC_PC10 16 +#define R_SPARC_PC22 17 +#define R_SPARC_WPLT30 18 +#define R_SPARC_COPY 19 +#define R_SPARC_GLOB_DAT 20 +#define R_SPARC_JMP_SLOT 21 +#define R_SPARC_RELATIVE 22 +#define R_SPARC_UA32 23 +#define R_SPARC_PLT32 24 +#define R_SPARC_HIPLT22 25 +#define R_SPARC_LOPLT10 26 +#define R_SPARC_PCPLT32 27 +#define R_SPARC_PCPLT22 28 +#define R_SPARC_PCPLT10 29 +#define R_SPARC_10 30 +#define R_SPARC_11 31 +#define R_SPARC_64 32 +#define R_SPARC_OLO10 33 +#define R_SPARC_HH22 34 +#define R_SPARC_HM10 35 +#define R_SPARC_LM22 36 +#define R_SPARC_PC_HH22 37 +#define R_SPARC_PC_HM10 38 +#define R_SPARC_PC_LM22 39 +#define R_SPARC_WDISP16 40 +#define R_SPARC_WDISP19 41 +#define R_SPARC_GLOB_JMP 42 +#define R_SPARC_7 43 +#define R_SPARC_5 44 +#define R_SPARC_6 45 +#define R_SPARC_DISP64 46 +#define R_SPARC_PLT64 47 +#define R_SPARC_HIX22 48 +#define R_SPARC_LOX10 49 +#define R_SPARC_H44 50 +#define R_SPARC_M44 51 +#define R_SPARC_L44 52 +#define R_SPARC_REGISTER 53 +#define R_SPARC_UA64 54 +#define R_SPARC_UA16 55 + + +/* + * Magic number for the elf trampoline, chosen wisely to be an immediate + * value. + */ +#define ARM_MAGIC_TRAMP_NUMBER 0x5c000003 + + +/* + * Symbol table entries. + */ + +typedef struct { + Elf32_Word name; /* String table index of name. */ + Elf32_Addr value; /* Symbol value. */ + Elf32_Word size; /* Size of associated object. */ + unsigned char info; /* Type and binding information. */ + unsigned char other; /* Reserved (not used). */ + Elf32_Half shndx; /* Section index of symbol. */ +} Elf32_Sym; + +/* Macros for accessing the fields of st_info. */ +#define ELF32_ST_BIND(info) ((info) >> 4) +#define ELF32_ST_TYPE(info) ((info) & 0xf) + +/* Macro for constructing st_info from field values. */ +#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) + +/* Macro for accessing the fields of st_other. */ +#define ELF32_ST_VISIBILITY(oth) ((oth) & 0x3) + +/* + * ELF definitions common to all 64-bit architectures. + */ + +typedef uint64 Elf64_Addr; +typedef uint16 Elf64_Half; +typedef uint64 Elf64_Off; +typedef int32 Elf64_Sword; +typedef int64 Elf64_Sxword; +typedef uint32 Elf64_Word; +typedef uint64 Elf64_Xword; + +/* + * Types of dynamic symbol hash table bucket and chain elements. + * + * This is inconsistent among 64 bit architectures, so a machine dependent + * typedef is required. + */ + +#ifdef __alpha__ +typedef Elf64_Off Elf64_Hashelt; +#else +typedef Elf64_Word Elf64_Hashelt; +#endif + +/* Non-standard class-dependent datatype used for abstraction. */ +typedef Elf64_Xword Elf64_Size; +typedef Elf64_Sxword Elf64_Ssize; + +/* + * ELF header. + */ + +typedef struct { + unsigned char ident[EI_NIDENT]; /* File identification. */ + Elf64_Half type; /* File type. */ + Elf64_Half machine; /* Machine architecture. */ + Elf64_Word version; /* ELF format version. */ + Elf64_Addr entry; /* Entry point. */ + Elf64_Off phoff; /* Program header file offset. */ + Elf64_Off shoff; /* Section header file offset. */ + Elf64_Word flags; /* Architecture-specific flags. */ + Elf64_Half ehsize; /* Size of ELF header in bytes. */ + Elf64_Half phentsize; /* Size of program header entry. */ + Elf64_Half phnum; /* Number of program header entries. */ + Elf64_Half shentsize; /* Size of section header entry. */ + Elf64_Half shnum; /* Number of section header entries. */ + Elf64_Half shstrndx; /* Section name strings section. */ +} Elf64_Ehdr; + +/* + * Section header. + */ + +typedef struct { + Elf64_Word name; /* Section name (index into the + section header string table). */ + Elf64_Word type; /* Section type. */ + Elf64_Xword flags; /* Section flags. */ + Elf64_Addr addr; /* Address in memory image. */ + Elf64_Off off; /* Offset in file. */ + Elf64_Xword size; /* Size in bytes. */ + Elf64_Word link; /* Index of a related section. */ + Elf64_Word info; /* Depends on section type. */ + Elf64_Xword addralign; /* Alignment in bytes. */ + Elf64_Xword entsize; /* Size of each entry in section. */ +} Elf64_Shdr; + +/* + * Program header. + */ + +typedef struct { + Elf64_Word type; /* Entry type. */ + Elf64_Word flags; /* Access permission flags. */ + Elf64_Off off; /* File offset of contents. */ + Elf64_Addr vaddr; /* Virtual address in memory image. */ + Elf64_Addr paddr; /* Physical address (not used). */ + Elf64_Xword filesz; /* Size of contents in file. */ + Elf64_Xword memsz; /* Size of contents in memory. */ + Elf64_Xword align; /* Alignment in memory and file. */ +} Elf64_Phdr; + +/* + * Dynamic structure. The ".dynamic" section contains an array of them. + */ + +typedef struct { + Elf64_Sxword d_tag; /* Entry type. */ + union { + Elf64_Xword d_val; /* Integer value. */ + Elf64_Addr d_ptr; /* Address value. */ + } d_un; +} Elf64_Dyn; + +/* + * Relocation entries. + */ + +/* Relocations that don't need an addend field. */ +typedef struct { + Elf64_Addr off; /* Location to be relocated. */ + Elf64_Xword info; /* Relocation type and symbol index. */ +} Elf64_Rel; + +/* Relocations that need an addend field. */ +typedef struct { + Elf64_Addr off; /* Location to be relocated. */ + Elf64_Xword info; /* Relocation type and symbol index. */ + Elf64_Sxword addend; /* Addend. */ +} Elf64_Rela; + +/* Macros for accessing the fields of r_info. */ +#define ELF64_R_SYM(info) ((info) >> 32) +#define ELF64_R_TYPE(info) ((info) & 0xffffffffL) + +/* Macro for constructing r_info from field values. */ +#define ELF64_R_INFO(sym, type) ((((uint64)(sym)) << 32) + (((uint64)(type)) & 0xffffffffULL)) + +/* + * Symbol table entries. + */ + +typedef struct { + Elf64_Word name; /* String table index of name. */ + unsigned char info; /* Type and binding information. */ + unsigned char other; /* Reserved (not used). */ + Elf64_Half shndx; /* Section index of symbol. */ + Elf64_Addr value; /* Symbol value. */ + Elf64_Xword size; /* Size of associated object. */ +} Elf64_Sym; + +/* Macros for accessing the fields of st_info. */ +#define ELF64_ST_BIND(info) ((info) >> 4) +#define ELF64_ST_TYPE(info) ((info) & 0xf) + +/* Macro for constructing st_info from field values. */ +#define ELF64_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) + +/* Macro for accessing the fields of st_other. */ +#define ELF64_ST_VISIBILITY(oth) ((oth) & 0x3) + +/* + * Go linker interface + */ + +#define ELF64HDRSIZE 64 +#define ELF64PHDRSIZE 56 +#define ELF64SHDRSIZE 64 +#define ELF64RELSIZE 16 +#define ELF64RELASIZE 24 +#define ELF64SYMSIZE sizeof(Elf64_Sym) + +#define ELF32HDRSIZE sizeof(Elf32_Ehdr) +#define ELF32PHDRSIZE sizeof(Elf32_Phdr) +#define ELF32SHDRSIZE sizeof(Elf32_Shdr) +#define ELF32SYMSIZE sizeof(Elf32_Sym) +#define ELF32RELSIZE 8 + +/* + * The interface uses the 64-bit structures always, + * to avoid code duplication. The writers know how to + * marshal a 32-bit representation from the 64-bit structure. + */ +typedef Elf64_Ehdr ElfEhdr; +typedef Elf64_Shdr ElfShdr; +typedef Elf64_Phdr ElfPhdr; + +void elfinit(void); +ElfEhdr *getElfEhdr(void); +ElfShdr *newElfShstrtab(vlong); +ElfShdr *newElfShdr(vlong); +ElfPhdr *newElfPhdr(void); +uint32 elfwritehdr(void); +uint32 elfwritephdrs(void); +uint32 elfwriteshdrs(void); +void elfwritedynent(Sym*, int, uint64); +void elfwritedynentsym(Sym*, int, Sym*); +void elfwritedynentsymsize(Sym*, int, Sym*); +uint32 elfhash(uchar*); +uint64 startelf(void); +uint64 endelf(void); +extern int numelfphdr; +extern int numelfshdr; +extern int iself; +extern int elfverneed; +int elfwriteinterp(void); +void elfinterp(ElfShdr*, uint64, char*); +void elfdynhash(void); +ElfPhdr* elfphload(Segment*); +ElfShdr* elfshbits(Section*); +void elfsetstring(char*, int); +void elfaddverneed(Sym*); + +EXTERN int elfstrsize; +EXTERN char* elfstrdat; +EXTERN int elftextsh; + +/* + * Total amount of space to reserve at the start of the file + * for Header, PHeaders, SHeaders, and interp. + * May waste some. + * On FreeBSD, cannot be larger than a page. + */ +#define ELFRESERVE 3072 diff --git a/src/cmd/ld/go.c b/src/cmd/ld/go.c new file mode 100644 index 000000000..fd7278a7b --- /dev/null +++ b/src/cmd/ld/go.c @@ -0,0 +1,869 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// go-specific code shared across loaders (5l, 6l, 8l). + +#include "l.h" +#include "../ld/lib.h" + +// accumulate all type information from .6 files. +// check for inconsistencies. + +// TODO: +// generate debugging section in binary. +// once the dust settles, try to move some code to +// libmach, so that other linkers and ar can share. + +/* + * package import data + */ +typedef struct Import Import; +struct Import +{ + Import *hash; // next in hash table + char *prefix; // "type", "var", "func", "const" + char *name; + char *def; + char *file; +}; +enum { + NIHASH = 1024 +}; +static Import *ihash[NIHASH]; +static int nimport; +static void imported(char *pkg, char *import); + +static int +hashstr(char *name) +{ + int h; + char *cp; + + h = 0; + for(cp = name; *cp; h += *cp++) + h *= 1119; + // not if(h < 0) h = ~h, because gcc 4.3 -O2 miscompiles it. + h &= 0xffffff; + return h; +} + +static Import * +ilookup(char *name) +{ + int h; + Import *x; + + h = hashstr(name) % NIHASH; + for(x=ihash[h]; x; x=x->hash) + if(x->name[0] == name[0] && strcmp(x->name, name) == 0) + return x; + x = mal(sizeof *x); + x->name = strdup(name); + x->hash = ihash[h]; + ihash[h] = x; + nimport++; + return x; +} + +static void loadpkgdata(char*, char*, char*, int); +static void loaddynimport(char*, char*, char*, int); +static void loaddynexport(char*, char*, char*, int); +static int parsemethod(char**, char*, char**); +static int parsepkgdata(char*, char*, char**, char*, char**, char**, char**); + +static Sym **dynexp; + +void +ldpkg(Biobuf *f, char *pkg, int64 len, char *filename, int whence) +{ + char *data, *p0, *p1, *name; + + if(debug['g']) + return; + + if((int)len != len) { + fprint(2, "%s: too much pkg data in %s\n", argv0, filename); + if(debug['u']) + errorexit(); + return; + } + data = mal(len+1); + if(Bread(f, data, len) != len) { + fprint(2, "%s: short pkg read %s\n", argv0, filename); + if(debug['u']) + errorexit(); + return; + } + data[len] = '\0'; + + // first \n$$ marks beginning of exports - skip rest of line + p0 = strstr(data, "\n$$"); + if(p0 == nil) { + if(debug['u'] && whence != ArchiveObj) { + fprint(2, "%s: cannot find export data in %s\n", argv0, filename); + errorexit(); + } + return; + } + p0 += 3; + while(*p0 != '\n' && *p0 != '\0') + p0++; + + // second marks end of exports / beginning of local data + p1 = strstr(p0, "\n$$"); + if(p1 == nil) { + fprint(2, "%s: cannot find end of exports in %s\n", argv0, filename); + if(debug['u']) + errorexit(); + return; + } + while(p0 < p1 && (*p0 == ' ' || *p0 == '\t' || *p0 == '\n')) + p0++; + if(p0 < p1) { + if(strncmp(p0, "package ", 8) != 0) { + fprint(2, "%s: bad package section in %s - %s\n", argv0, filename, p0); + if(debug['u']) + errorexit(); + return; + } + p0 += 8; + while(p0 < p1 && (*p0 == ' ' || *p0 == '\t' || *p0 == '\n')) + p0++; + name = p0; + while(p0 < p1 && *p0 != ' ' && *p0 != '\t' && *p0 != '\n') + p0++; + if(debug['u'] && whence != ArchiveObj && + (p0+6 > p1 || memcmp(p0, " safe\n", 6) != 0)) { + fprint(2, "%s: load of unsafe package %s\n", argv0, filename); + nerrors++; + errorexit(); + } + if(p0 < p1) { + if(*p0 == '\n') + *p0++ = '\0'; + else { + *p0++ = '\0'; + while(p0 < p1 && *p0++ != '\n') + ; + } + } + if(strcmp(pkg, "main") == 0 && strcmp(name, "main") != 0) { + fprint(2, "%s: %s: not package main (package %s)\n", argv0, filename, name); + nerrors++; + errorexit(); + } + loadpkgdata(filename, pkg, p0, p1 - p0); + } + + // The __.PKGDEF archive summary has no local types. + if(whence == Pkgdef) + return; + + // local types begin where exports end. + // skip rest of line after $$ we found above + p0 = p1 + 3; + while(*p0 != '\n' && *p0 != '\0') + p0++; + + // local types end at next \n$$. + p1 = strstr(p0, "\n$$"); + if(p1 == nil) { + fprint(2, "%s: cannot find end of local types in %s\n", argv0, filename); + if(debug['u']) + errorexit(); + return; + } + + loadpkgdata(filename, pkg, p0, p1 - p0); + + // look for dynimport section + p0 = strstr(p1, "\n$$ // dynimport"); + if(p0 != nil) { + p0 = strchr(p0+1, '\n'); + if(p0 == nil) { + fprint(2, "%s: found $$ // dynimport but no newline in %s\n", argv0, filename); + if(debug['u']) + errorexit(); + return; + } + p1 = strstr(p0, "\n$$"); + if(p1 == nil) + p1 = strstr(p0, "\n!\n"); + if(p1 == nil) { + fprint(2, "%s: cannot find end of // dynimport section in %s\n", argv0, filename); + if(debug['u']) + errorexit(); + return; + } + loaddynimport(filename, pkg, p0 + 1, p1 - (p0+1)); + } + + // look for dynexp section + p0 = strstr(p1, "\n$$ // dynexport"); + if(p0 != nil) { + p0 = strchr(p0+1, '\n'); + if(p0 == nil) { + fprint(2, "%s: found $$ // dynexporg but no newline in %s\n", argv0, filename); + if(debug['u']) + errorexit(); + return; + } + p1 = strstr(p0, "\n$$"); + if(p1 == nil) + p1 = strstr(p0, "\n!\n"); + if(p1 == nil) { + fprint(2, "%s: cannot find end of // dynexporg section in %s\n", argv0, filename); + if(debug['u']) + errorexit(); + return; + } + loaddynexport(filename, pkg, p0 + 1, p1 - (p0+1)); + } +} + +static void +loadpkgdata(char *file, char *pkg, char *data, int len) +{ + char *p, *ep, *prefix, *name, *def; + Import *x; + + file = strdup(file); + p = data; + ep = data + len; + while(parsepkgdata(file, pkg, &p, ep, &prefix, &name, &def) > 0) { + x = ilookup(name); + if(x->prefix == nil) { + x->prefix = prefix; + x->def = def; + x->file = file; + } else if(strcmp(x->prefix, prefix) != 0) { + fprint(2, "%s: conflicting definitions for %s\n", argv0, name); + fprint(2, "%s:\t%s %s ...\n", x->file, x->prefix, name); + fprint(2, "%s:\t%s %s ...\n", file, prefix, name); + nerrors++; + } else if(strcmp(x->def, def) != 0) { + fprint(2, "%s: conflicting definitions for %s\n", argv0, name); + fprint(2, "%s:\t%s %s %s\n", x->file, x->prefix, name, x->def); + fprint(2, "%s:\t%s %s %s\n", file, prefix, name, def); + nerrors++; + } + } +} + +// replace all "". with pkg. +char* +expandpkg(char *t0, char *pkg) +{ + int n; + char *p; + char *w, *w0, *t; + + n = 0; + for(p=t0; (p=strstr(p, "\"\".")) != nil; p+=3) + n++; + + if(n == 0) + return t0; + + // use malloc, not mal, so that caller can free + w0 = malloc(strlen(t0) + strlen(pkg)*n); + if(w0 == nil) { + diag("out of memory"); + errorexit(); + } + w = w0; + for(p=t=t0; (p=strstr(p, "\"\".")) != nil; p=t) { + memmove(w, t, p - t); + w += p-t; + strcpy(w, pkg); + w += strlen(pkg); + t = p+2; + } + strcpy(w, t); + return w0; +} + +static int +parsepkgdata(char *file, char *pkg, char **pp, char *ep, char **prefixp, char **namep, char **defp) +{ + char *p, *prefix, *name, *def, *edef, *meth; + int n, inquote; + + // skip white space + p = *pp; +loop: + while(p < ep && (*p == ' ' || *p == '\t' || *p == '\n')) + p++; + if(p == ep || strncmp(p, "$$\n", 3) == 0) + return 0; + + // prefix: (var|type|func|const) + prefix = p; + if(p + 7 > ep) + return -1; + if(strncmp(p, "var ", 4) == 0) + p += 4; + else if(strncmp(p, "type ", 5) == 0) + p += 5; + else if(strncmp(p, "func ", 5) == 0) + p += 5; + else if(strncmp(p, "const ", 6) == 0) + p += 6; + else if(strncmp(p, "import ", 7) == 0) { + p += 7; + while(p < ep && *p != ' ') + p++; + p++; + name = p; + while(p < ep && *p != '\n') + p++; + if(p >= ep) { + fprint(2, "%s: %s: confused in import line\n", argv0, file); + nerrors++; + return -1; + } + *p++ = '\0'; + imported(pkg, name); + goto loop; + } + else { + fprint(2, "%s: %s: confused in pkg data near <<%.40s>>\n", argv0, file, prefix); + nerrors++; + return -1; + } + p[-1] = '\0'; + + // name: a.b followed by space + name = p; + inquote = 0; + while(p < ep) { + if (*p == ' ' && !inquote) + break; + + if(*p == '\\') + p++; + else if(*p == '"') + inquote = !inquote; + + p++; + } + + if(p >= ep) + return -1; + *p++ = '\0'; + + // def: free form to new line + def = p; + while(p < ep && *p != '\n') + p++; + if(p >= ep) + return -1; + edef = p; + *p++ = '\0'; + + // include methods on successive lines in def of named type + while(parsemethod(&p, ep, &meth) > 0) { + *edef++ = '\n'; // overwrites '\0' + if(edef+1 > meth) { + // We want to indent methods with a single \t. + // 6g puts at least one char of indent before all method defs, + // so there will be room for the \t. If the method def wasn't + // indented we could do something more complicated, + // but for now just diagnose the problem and assume + // 6g will keep indenting for us. + fprint(2, "%s: %s: expected methods to be indented %p %p %.10s\n", argv0, + file, edef, meth, meth); + nerrors++; + return -1; + } + *edef++ = '\t'; + n = strlen(meth); + memmove(edef, meth, n); + edef += n; + } + + name = expandpkg(name, pkg); + def = expandpkg(def, pkg); + + // done + *pp = p; + *prefixp = prefix; + *namep = name; + *defp = def; + return 1; +} + +static int +parsemethod(char **pp, char *ep, char **methp) +{ + char *p; + + // skip white space + p = *pp; + while(p < ep && (*p == ' ' || *p == '\t')) + p++; + if(p == ep) + return 0; + + // if it says "func (", it's a method + if(p + 6 >= ep || strncmp(p, "func (", 6) != 0) + return 0; + + // definition to end of line + *methp = p; + while(p < ep && *p != '\n') + p++; + if(p >= ep) { + fprint(2, "%s: lost end of line in method definition\n", argv0); + *pp = ep; + return -1; + } + *p++ = '\0'; + *pp = p; + return 1; +} + +static void +loaddynimport(char *file, char *pkg, char *p, int n) +{ + char *pend, *next, *name, *def, *p0, *lib, *q; + Sym *s; + + USED(file); + pend = p + n; + for(; p<pend; p=next) { + next = strchr(p, '\n'); + if(next == nil) + next = ""; + else + *next++ = '\0'; + p0 = p; + if(strncmp(p, "dynimport ", 10) != 0) + goto err; + p += 10; + name = p; + p = strchr(name, ' '); + if(p == nil) + goto err; + while(*p == ' ') + p++; + def = p; + p = strchr(def, ' '); + if(p == nil) + goto err; + while(*p == ' ') + p++; + lib = p; + + // successful parse: now can edit the line + *strchr(name, ' ') = 0; + *strchr(def, ' ') = 0; + + if(debug['d']) { + fprint(2, "%s: %s: cannot use dynamic imports with -d flag\n", argv0, file); + nerrors++; + return; + } + + if(strcmp(name, "_") == 0 && strcmp(def, "_") == 0) { + // allow #pragma dynimport _ _ "foo.so" + // to force a link of foo.so. + havedynamic = 1; + adddynlib(lib); + continue; + } + + name = expandpkg(name, pkg); + q = strchr(def, '@'); + if(q) + *q++ = '\0'; + s = lookup(name, 0); + if(s->type == 0 || s->type == SXREF) { + s->dynimplib = lib; + s->dynimpname = def; + s->dynimpvers = q; + s->type = SDYNIMPORT; + havedynamic = 1; + } + } + return; + +err: + fprint(2, "%s: %s: invalid dynimport line: %s\n", argv0, file, p0); + nerrors++; +} + +static void +loaddynexport(char *file, char *pkg, char *p, int n) +{ + char *pend, *next, *local, *elocal, *remote, *p0; + Sym *s; + + USED(file); + pend = p + n; + for(; p<pend; p=next) { + next = strchr(p, '\n'); + if(next == nil) + next = ""; + else + *next++ = '\0'; + p0 = p; + if(strncmp(p, "dynexport ", 10) != 0) + goto err; + p += 10; + local = p; + p = strchr(local, ' '); + if(p == nil) + goto err; + while(*p == ' ') + p++; + remote = p; + + // successful parse: now can edit the line + *strchr(local, ' ') = 0; + + elocal = expandpkg(local, pkg); + + s = lookup(elocal, 0); + if(s->dynimplib != nil) { + fprint(2, "%s: symbol is both dynimport and dynexport %s\n", argv0, local); + nerrors++; + } + s->dynimpname = remote; + s->dynexport = 1; + + if(ndynexp%32 == 0) + dynexp = realloc(dynexp, (ndynexp+32)*sizeof dynexp[0]); + dynexp[ndynexp++] = s; + + if (elocal != local) + free(elocal); + } + return; + +err: + fprint(2, "%s: invalid dynexport line: %s\n", argv0, p0); + nerrors++; +} + +static int markdepth; + +static void +marktext(Sym *s) +{ + Auto *a; + Prog *p; + + if(s == S) + return; + markdepth++; + if(debug['v'] > 1) + Bprint(&bso, "%d marktext %s\n", markdepth, s->name); + for(a=s->autom; a; a=a->link) + mark(a->gotype); + for(p=s->text; p != P; p=p->link) { + if(p->from.sym) + mark(p->from.sym); + if(p->to.sym) + mark(p->to.sym); + } + markdepth--; +} + +void +mark(Sym *s) +{ + int i; + + if(s == S || s->reachable) + return; + if(strncmp(s->name, "weak.", 5) == 0) + return; + s->reachable = 1; + if(s->text) + marktext(s); + for(i=0; i<s->nr; i++) + mark(s->r[i].sym); + if(s->gotype) + mark(s->gotype); + if(s->sub) + mark(s->sub); + if(s->outer) + mark(s->outer); +} + +static char* +morename[] = +{ + "runtime.morestack", + "runtime.morestackx", + + "runtime.morestack00", + "runtime.morestack10", + "runtime.morestack01", + "runtime.morestack11", + + "runtime.morestack8", + "runtime.morestack16", + "runtime.morestack24", + "runtime.morestack32", + "runtime.morestack40", + "runtime.morestack48", +}; + +static int +isz(Auto *a) +{ + for(; a; a=a->link) + if(a->type == D_FILE || a->type == D_FILE1) + return 1; + return 0; +} + +static void +addz(Sym *s, Auto *z) +{ + Auto *a, *last; + + // strip out non-z + last = nil; + for(a = z; a != nil; a = a->link) { + if(a->type == D_FILE || a->type == D_FILE1) { + if(last == nil) + z = a; + else + last->link = a; + last = a; + } + } + if(last) { + last->link = s->autom; + s->autom = z; + } +} + +void +deadcode(void) +{ + int i; + Sym *s, *last; + Auto *z; + + if(debug['v']) + Bprint(&bso, "%5.2f deadcode\n", cputime()); + + mark(lookup(INITENTRY, 0)); + for(i=0; i<nelem(morename); i++) + mark(lookup(morename[i], 0)); + + for(i=0; i<ndynexp; i++) + mark(dynexp[i]); + + // remove dead text but keep file information (z symbols). + last = nil; + z = nil; + for(s = textp; s != nil; s = s->next) { + if(!s->reachable) { + if(isz(s->autom)) + z = s->autom; + continue; + } + if(last == nil) + textp = s; + else + last->next = s; + last = s; + if(z != nil) { + if(!isz(s->autom)) + addz(s, z); + z = nil; + } + } + if(last == nil) + textp = nil; + else + last->next = nil; + + for(s = allsym; s != S; s = s->allsym) + if(strncmp(s->name, "weak.", 5) == 0) { + s->special = 1; // do not lay out in data segment + s->reachable = 1; + s->hide = 1; + } +} + +void +doweak(void) +{ + Sym *s, *t; + + // resolve weak references only if + // target symbol will be in binary anyway. + for(s = allsym; s != S; s = s->allsym) { + if(strncmp(s->name, "weak.", 5) == 0) { + t = rlookup(s->name+5, s->version); + if(t && t->type != 0 && t->reachable) { + s->value = t->value; + s->type = t->type; + } else { + s->type = SCONST; + s->value = 0; + } + continue; + } + } +} + +void +addexport(void) +{ + int i; + + for(i=0; i<ndynexp; i++) + adddynsym(dynexp[i]); +} + +/* %Z from gc, for quoting import paths */ +int +Zconv(Fmt *fp) +{ + Rune r; + char *s, *se; + int n; + + s = va_arg(fp->args, char*); + if(s == nil) + return fmtstrcpy(fp, "<nil>"); + + se = s + strlen(s); + while(s < se) { + n = chartorune(&r, s); + s += n; + switch(r) { + case Runeerror: + if(n == 1) { + fmtprint(fp, "\\x%02x", (uchar)*(s-1)); + break; + } + // fall through + default: + if(r < ' ') { + fmtprint(fp, "\\x%02x", r); + break; + } + fmtrune(fp, r); + break; + case '\t': + fmtstrcpy(fp, "\\t"); + break; + case '\n': + fmtstrcpy(fp, "\\n"); + break; + case '\"': + case '\\': + fmtrune(fp, '\\'); + fmtrune(fp, r); + break; + } + } + return 0; +} + + +typedef struct Pkg Pkg; +struct Pkg +{ + uchar mark; + uchar checked; + Pkg *next; + char *path; + Pkg **impby; + int nimpby; + int mimpby; + Pkg *all; +}; + +static Pkg *phash[1024]; +static Pkg *pkgall; + +static Pkg* +getpkg(char *path) +{ + Pkg *p; + int h; + + h = hashstr(path) % nelem(phash); + for(p=phash[h]; p; p=p->next) + if(strcmp(p->path, path) == 0) + return p; + p = mal(sizeof *p); + p->path = strdup(path); + p->next = phash[h]; + phash[h] = p; + p->all = pkgall; + pkgall = p; + return p; +} + +static void +imported(char *pkg, char *import) +{ + Pkg *p, *i; + + // everyone imports runtime, even runtime. + if(strcmp(import, "\"runtime\"") == 0) + return; + + pkg = smprint("\"%Z\"", pkg); // turn pkg path into quoted form, freed below + p = getpkg(pkg); + i = getpkg(import); + if(i->nimpby >= i->mimpby) { + i->mimpby *= 2; + if(i->mimpby == 0) + i->mimpby = 16; + i->impby = realloc(i->impby, i->mimpby*sizeof i->impby[0]); + } + i->impby[i->nimpby++] = p; + free(pkg); +} + +static Pkg* +cycle(Pkg *p) +{ + int i; + Pkg *bad; + + if(p->checked) + return 0; + + if(p->mark) { + nerrors++; + print("import cycle:\n"); + print("\t%s\n", p->path); + return p; + } + p->mark = 1; + for(i=0; i<p->nimpby; i++) { + if((bad = cycle(p->impby[i])) != nil) { + p->mark = 0; + p->checked = 1; + print("\timports %s\n", p->path); + if(bad == p) + return nil; + return bad; + } + } + p->checked = 1; + p->mark = 0; + return 0; +} + +void +importcycles(void) +{ + Pkg *p; + + for(p=pkgall; p; p=p->all) + cycle(p); +} diff --git a/src/cmd/ld/ldelf.c b/src/cmd/ld/ldelf.c new file mode 100644 index 000000000..924687867 --- /dev/null +++ b/src/cmd/ld/ldelf.c @@ -0,0 +1,816 @@ +/* +Derived from Plan 9 from User Space's src/libmach/elf.h, elf.c +http://code.swtch.com/plan9port/src/tip/src/libmach/ + + Copyright © 2004 Russ Cox. + Portions Copyright © 2008-2010 Google Inc. + Portions Copyright © 2010 The Go Authors. + +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 "l.h" +#include "lib.h" +#include "../ld/elf.h" + +enum +{ + ElfClassNone = 0, + ElfClass32, + ElfClass64, + + ElfDataNone = 0, + ElfDataLsb, + ElfDataMsb, + + ElfTypeNone = 0, + ElfTypeRelocatable, + ElfTypeExecutable, + ElfTypeSharedObject, + ElfTypeCore, + /* 0xFF00 - 0xFFFF reserved for processor-specific types */ + + ElfMachNone = 0, + ElfMach32100, /* AT&T WE 32100 */ + ElfMachSparc, /* SPARC */ + ElfMach386, /* Intel 80386 */ + ElfMach68000, /* Motorola 68000 */ + ElfMach88000, /* Motorola 88000 */ + ElfMach486, /* Intel 80486, no longer used */ + ElfMach860, /* Intel 80860 */ + ElfMachMips, /* MIPS RS3000 */ + ElfMachS370, /* IBM System/370 */ + ElfMachMipsLe, /* MIPS RS3000 LE */ + ElfMachParisc = 15, /* HP PA RISC */ + ElfMachVpp500 = 17, /* Fujitsu VPP500 */ + ElfMachSparc32Plus, /* SPARC V8+ */ + ElfMach960, /* Intel 80960 */ + ElfMachPower, /* PowerPC */ + ElfMachPower64, /* PowerPC 64 */ + ElfMachS390, /* IBM System/390 */ + ElfMachV800 = 36, /* NEC V800 */ + ElfMachFr20, /* Fujitsu FR20 */ + ElfMachRh32, /* TRW RH-32 */ + ElfMachRce, /* Motorola RCE */ + ElfMachArm, /* ARM */ + ElfMachAlpha, /* Digital Alpha */ + ElfMachSH, /* Hitachi SH */ + ElfMachSparc9, /* SPARC V9 */ + ElfMachAmd64 = 62, + /* and the list goes on... */ + + ElfAbiNone = 0, + ElfAbiSystemV = 0, /* [sic] */ + ElfAbiHPUX, + ElfAbiNetBSD, + ElfAbiLinux, + ElfAbiSolaris = 6, + ElfAbiAix, + ElfAbiIrix, + ElfAbiFreeBSD, + ElfAbiTru64, + ElfAbiModesto, + ElfAbiOpenBSD, + ElfAbiARM = 97, + ElfAbiEmbedded = 255, + + /* some of sections 0xFF00 - 0xFFFF reserved for various things */ + ElfSectNone = 0, + ElfSectProgbits, + ElfSectSymtab, + ElfSectStrtab, + ElfSectRela, + ElfSectHash, + ElfSectDynamic, + ElfSectNote, + ElfSectNobits, + ElfSectRel, + ElfSectShlib, + ElfSectDynsym, + + ElfSectFlagWrite = 0x1, + ElfSectFlagAlloc = 0x2, + ElfSectFlagExec = 0x4, + /* 0xF0000000 are reserved for processor specific */ + + ElfSymBindLocal = 0, + ElfSymBindGlobal, + ElfSymBindWeak, + /* 13-15 reserved */ + + ElfSymTypeNone = 0, + ElfSymTypeObject, + ElfSymTypeFunc, + ElfSymTypeSection, + ElfSymTypeFile, + /* 13-15 reserved */ + + ElfSymShnNone = 0, + ElfSymShnAbs = 0xFFF1, + ElfSymShnCommon = 0xFFF2, + /* 0xFF00-0xFF1F reserved for processors */ + /* 0xFF20-0xFF3F reserved for operating systems */ + + ElfProgNone = 0, + ElfProgLoad, + ElfProgDynamic, + ElfProgInterp, + ElfProgNote, + ElfProgShlib, + ElfProgPhdr, + + ElfProgFlagExec = 0x1, + ElfProgFlagWrite = 0x2, + ElfProgFlagRead = 0x4, + + ElfNotePrStatus = 1, + ElfNotePrFpreg = 2, + ElfNotePrPsinfo = 3, + ElfNotePrTaskstruct = 4, + ElfNotePrAuxv = 6, + ElfNotePrXfpreg = 0x46e62b7f /* for gdb/386 */ +}; + +typedef struct ElfHdrBytes ElfHdrBytes; +typedef struct ElfSectBytes ElfSectBytes; +typedef struct ElfProgBytes ElfProgBytes; +typedef struct ElfSymBytes ElfSymBytes; + +typedef struct ElfHdrBytes64 ElfHdrBytes64; +typedef struct ElfSectBytes64 ElfSectBytes64; +typedef struct ElfProgBytes64 ElfProgBytes64; +typedef struct ElfSymBytes64 ElfSymBytes64; + +struct ElfHdrBytes +{ + uchar ident[16]; + uchar type[2]; + uchar machine[2]; + uchar version[4]; + uchar entry[4]; + uchar phoff[4]; + uchar shoff[4]; + uchar flags[4]; + uchar ehsize[2]; + uchar phentsize[2]; + uchar phnum[2]; + uchar shentsize[2]; + uchar shnum[2]; + uchar shstrndx[2]; +}; + +struct ElfHdrBytes64 +{ + uchar ident[16]; + uchar type[2]; + uchar machine[2]; + uchar version[4]; + uchar entry[8]; + uchar phoff[8]; + uchar shoff[8]; + uchar flags[4]; + uchar ehsize[2]; + uchar phentsize[2]; + uchar phnum[2]; + uchar shentsize[2]; + uchar shnum[2]; + uchar shstrndx[2]; +}; + +struct ElfSectBytes +{ + uchar name[4]; + uchar type[4]; + uchar flags[4]; + uchar addr[4]; + uchar off[4]; + uchar size[4]; + uchar link[4]; + uchar info[4]; + uchar align[4]; + uchar entsize[4]; +}; + +struct ElfSectBytes64 +{ + uchar name[4]; + uchar type[4]; + uchar flags[8]; + uchar addr[8]; + uchar off[8]; + uchar size[8]; + uchar link[4]; + uchar info[4]; + uchar align[8]; + uchar entsize[8]; +}; + +struct ElfSymBytes +{ + uchar name[4]; + uchar value[4]; + uchar size[4]; + uchar info; /* top4: bind, bottom4: type */ + uchar other; + uchar shndx[2]; +}; + +struct ElfSymBytes64 +{ + uchar name[4]; + uchar info; /* top4: bind, bottom4: type */ + uchar other; + uchar shndx[2]; + uchar value[8]; + uchar size[8]; +}; + +typedef struct ElfSect ElfSect; +typedef struct ElfObj ElfObj; +typedef struct ElfSym ElfSym; + +struct ElfSect +{ + char *name; + uint32 type; + uint64 flags; + uint64 addr; + uint64 off; + uint64 size; + uint32 link; + uint32 info; + uint64 align; + uint64 entsize; + uchar *base; + Sym *sym; +}; + +struct ElfObj +{ + Biobuf *f; + int64 base; // offset in f where ELF begins + int64 len; // length of ELF + int is64; + char *name; + + Endian *e; + ElfSect *sect; + uint nsect; + char *shstrtab; + int nsymtab; + ElfSect *symtab; + ElfSect *symstr; + + uint32 type; + uint32 machine; + uint32 version; + uint64 entry; + uint64 phoff; + uint64 shoff; + uint32 flags; + uint32 ehsize; + uint32 phentsize; + uint32 phnum; + uint32 shentsize; + uint32 shnum; + uint32 shstrndx; +}; + +struct ElfSym +{ + char* name; + uint64 value; + uint64 size; + uchar bind; + uchar type; + uchar other; + uint16 shndx; + Sym* sym; +}; + +uchar ElfMagic[4] = { 0x7F, 'E', 'L', 'F' }; + +static ElfSect* section(ElfObj*, char*); +static int map(ElfObj*, ElfSect*); +static int readsym(ElfObj*, int i, ElfSym*); +static int reltype(char*, int, uchar*); + +void +ldelf(Biobuf *f, char *pkg, int64 len, char *pn) +{ + int32 base; + uint64 add, info; + char *name; + int i, j, rela, is64, n; + uchar hdrbuf[64]; + uchar *p; + ElfHdrBytes *hdr; + ElfObj *obj; + ElfSect *sect, *rsect; + ElfSym sym; + Endian *e; + Reloc *r, *rp; + Sym *s; + + USED(pkg); + if(debug['v']) + Bprint(&bso, "%5.2f ldelf %s\n", cputime(), pn); + + version++; + base = Boffset(f); + + if(Bread(f, hdrbuf, sizeof hdrbuf) != sizeof hdrbuf) + goto bad; + hdr = (ElfHdrBytes*)hdrbuf; + if(memcmp(hdr->ident, ElfMagic, 4) != 0) + goto bad; + switch(hdr->ident[5]) { + case ElfDataLsb: + e = ≤ + break; + case ElfDataMsb: + e = &be; + break; + default: + goto bad; + } + + // read header + obj = mal(sizeof *obj); + obj->e = e; + obj->f = f; + obj->base = base; + obj->len = len; + obj->name = pn; + + is64 = 0; + if(hdr->ident[4] == ElfClass64) { + ElfHdrBytes64* hdr; + + is64 = 1; + hdr = (ElfHdrBytes64*)hdrbuf; + obj->type = e->e16(hdr->type); + obj->machine = e->e16(hdr->machine); + obj->version = e->e32(hdr->version); + obj->phoff = e->e64(hdr->phoff); + obj->shoff = e->e64(hdr->shoff); + obj->flags = e->e32(hdr->flags); + obj->ehsize = e->e16(hdr->ehsize); + obj->phentsize = e->e16(hdr->phentsize); + obj->phnum = e->e16(hdr->phnum); + obj->shentsize = e->e16(hdr->shentsize); + obj->shnum = e->e16(hdr->shnum); + obj->shstrndx = e->e16(hdr->shstrndx); + } else { + obj->type = e->e16(hdr->type); + obj->machine = e->e16(hdr->machine); + obj->version = e->e32(hdr->version); + obj->entry = e->e32(hdr->entry); + obj->phoff = e->e32(hdr->phoff); + obj->shoff = e->e32(hdr->shoff); + obj->flags = e->e32(hdr->flags); + obj->ehsize = e->e16(hdr->ehsize); + obj->phentsize = e->e16(hdr->phentsize); + obj->phnum = e->e16(hdr->phnum); + obj->shentsize = e->e16(hdr->shentsize); + obj->shnum = e->e16(hdr->shnum); + obj->shstrndx = e->e16(hdr->shstrndx); + } + obj->is64 = is64; + + if(hdr->ident[6] != obj->version) + goto bad; + + if(e->e16(hdr->type) != ElfTypeRelocatable) { + diag("%s: elf but not elf relocatable object", pn); + return; + } + + switch(thechar) { + default: + diag("%s: elf %s unimplemented", pn, thestring); + return; + case '5': + if(e != &le || obj->machine != ElfMachArm || hdr->ident[4] != ElfClass32) { + diag("%s: elf object but not arm", pn); + return; + } + break; + case '6': + if(e != &le || obj->machine != ElfMachAmd64 || hdr->ident[4] != ElfClass64) { + diag("%s: elf object but not amd64", pn); + return; + } + break; + case '8': + if(e != &le || obj->machine != ElfMach386 || hdr->ident[4] != ElfClass32) { + diag("%s: elf object but not 386", pn); + return; + } + break; + } + + // load section list into memory. + obj->sect = mal(obj->shnum*sizeof obj->sect[0]); + obj->nsect = obj->shnum; + for(i=0; i<obj->nsect; i++) { + if(Bseek(f, base+obj->shoff+i*obj->shentsize, 0) < 0) + goto bad; + sect = &obj->sect[i]; + if(is64) { + ElfSectBytes64 b; + + werrstr("short read"); + if(Bread(f, &b, sizeof b) != sizeof b) + goto bad; + + sect->name = (char*)(uintptr)e->e32(b.name); + sect->type = e->e32(b.type); + sect->flags = e->e64(b.flags); + sect->addr = e->e64(b.addr); + sect->off = e->e64(b.off); + sect->size = e->e64(b.size); + sect->link = e->e32(b.link); + sect->info = e->e32(b.info); + sect->align = e->e64(b.align); + sect->entsize = e->e64(b.entsize); + } else { + ElfSectBytes b; + + werrstr("short read"); + if(Bread(f, &b, sizeof b) != sizeof b) + goto bad; + + sect->name = (char*)(uintptr)e->e32(b.name); + sect->type = e->e32(b.type); + sect->flags = e->e32(b.flags); + sect->addr = e->e32(b.addr); + sect->off = e->e32(b.off); + sect->size = e->e32(b.size); + sect->link = e->e32(b.link); + sect->info = e->e32(b.info); + sect->align = e->e32(b.align); + sect->entsize = e->e32(b.entsize); + } + } + + // read section string table and translate names + if(obj->shstrndx >= obj->nsect) { + werrstr("shstrndx out of range %d >= %d", obj->shstrndx, obj->nsect); + goto bad; + } + sect = &obj->sect[obj->shstrndx]; + if(map(obj, sect) < 0) + goto bad; + for(i=0; i<obj->nsect; i++) + if(obj->sect[i].name != nil) + obj->sect[i].name = (char*)sect->base + (uintptr)obj->sect[i].name; + + // load string table for symbols into memory. + obj->symtab = section(obj, ".symtab"); + if(obj->symtab == nil) { + // our work is done here - no symbols means nothing can refer to this file + return; + } + if(obj->symtab->link <= 0 || obj->symtab->link >= obj->nsect) { + diag("%s: elf object has symbol table with invalid string table link", pn); + return; + } + obj->symstr = &obj->sect[obj->symtab->link]; + if(is64) + obj->nsymtab = obj->symtab->size / sizeof(ElfSymBytes64); + else + obj->nsymtab = obj->symtab->size / sizeof(ElfSymBytes); + + if(map(obj, obj->symtab) < 0) + goto bad; + if(map(obj, obj->symstr) < 0) + goto bad; + + // load text and data segments into memory. + // they are not as small as the section lists, but we'll need + // the memory anyway for the symbol images, so we might + // as well use one large chunk. + + // create symbols for mapped sections + for(i=0; i<obj->nsect; i++) { + sect = &obj->sect[i]; + if((sect->type != ElfSectProgbits && sect->type != ElfSectNobits) || !(sect->flags&ElfSectFlagAlloc)) + continue; + if(sect->type != ElfSectNobits && map(obj, sect) < 0) + goto bad; + + name = smprint("%s(%s)", pn, sect->name); + s = lookup(name, version); + free(name); + switch((int)sect->flags&(ElfSectFlagAlloc|ElfSectFlagWrite|ElfSectFlagExec)) { + default: + werrstr("unexpected flags for ELF section %s", sect->name); + goto bad; + case ElfSectFlagAlloc: + s->type = SRODATA; + break; + case ElfSectFlagAlloc + ElfSectFlagWrite: + s->type = SDATA; + break; + case ElfSectFlagAlloc + ElfSectFlagExec: + s->type = STEXT; + break; + } + if(sect->type == ElfSectProgbits) { + s->p = sect->base; + s->np = sect->size; + } + s->size = sect->size; + if(s->type == STEXT) { + if(etextp) + etextp->next = s; + else + textp = s; + etextp = s; + } + sect->sym = s; + } + + // load relocations + for(i=0; i<obj->nsect; i++) { + rsect = &obj->sect[i]; + if(rsect->type != ElfSectRela && rsect->type != ElfSectRel) + continue; + if(rsect->info >= obj->nsect || obj->sect[rsect->info].base == nil) + continue; + sect = &obj->sect[rsect->info]; + if(map(obj, rsect) < 0) + goto bad; + rela = rsect->type == ElfSectRela; + n = rsect->size/(4+4*is64)/(2+rela); + r = mal(n*sizeof r[0]); + p = rsect->base; + for(j=0; j<n; j++) { + add = 0; + rp = &r[j]; + if(is64) { + // 64-bit rel/rela + rp->off = e->e64(p); + p += 8; + info = e->e64(p); + p += 8; + if(rela) { + add = e->e64(p); + p += 8; + } + } else { + // 32-bit rel/rela + rp->off = e->e32(p); + p += 4; + info = e->e32(p); + info = info>>8<<32 | (info&0xff); // convert to 64-bit info + p += 4; + if(rela) { + add = e->e32(p); + p += 4; + } + } + if(readsym(obj, info>>32, &sym) < 0) + goto bad; + if(sym.sym == nil) { + werrstr("%s#%d: reloc of invalid sym #%d %s shndx=%d type=%d", + sect->sym->name, j, (int)(info>>32), sym.name, sym.shndx, sym.type); + goto bad; + } + rp->sym = sym.sym; + rp->type = reltype(pn, (uint32)info, &rp->siz); + if(rela) + rp->add = add; + else { + // load addend from image + if(rp->siz == 4) + rp->add = e->e32(sect->base+rp->off); + else if(rp->siz == 8) + rp->add = e->e64(sect->base+rp->off); + else + diag("invalid rela size %d", rp->siz); + } + } + qsort(r, n, sizeof r[0], rbyoff); // just in case + + s = sect->sym; + s->r = r; + s->nr = n; + } + + // enter sub-symbols into symbol table. + // symbol 0 is the null symbol. + for(i=1; i<obj->nsymtab; i++) { + if(readsym(obj, i, &sym) < 0) + goto bad; + if(sym.type != ElfSymTypeFunc && sym.type != ElfSymTypeObject && sym.type != ElfSymTypeNone) + continue; + if(sym.shndx == ElfSymShnCommon) { + s = sym.sym; + if(s->size < sym.size) + s->size = sym.size; + if(s->type == 0 || s->type == SXREF) + s->type = SBSS; + continue; + } + if(sym.shndx >= obj->nsect || sym.shndx == 0) + continue; + sect = obj->sect+sym.shndx; + if(sect->sym == nil) { + diag("%s: sym#%d: ignoring %s in section %d (type %d)", pn, i, sym.name, sym.shndx, sym.type); + continue; + } + s = sym.sym; + s->sub = sect->sym->sub; + sect->sym->sub = s; + s->type = sect->sym->type | SSUB; + if(!s->dynexport) { + s->dynimplib = nil; // satisfy dynimport + s->dynimpname = nil; // satisfy dynimport + } + s->value = sym.value; + s->size = sym.size; + s->outer = sect->sym; + if(sect->sym->type == STEXT) { + Prog *p; + + if(s->text != P) + diag("%s: duplicate definition of %s", pn, s->name); + // build a TEXT instruction with a unique pc + // just to make the rest of the linker happy. + p = prg(); + p->as = ATEXT; + p->from.type = D_EXTERN; + p->from.sym = s; + p->textflag = 7; + p->to.type = D_CONST; + p->link = nil; + p->pc = pc++; + s->text = p; + + etextp->next = s; + etextp = s; + } + } + return; + +bad: + diag("%s: malformed elf file: %r", pn); +} + +static ElfSect* +section(ElfObj *obj, char *name) +{ + int i; + + for(i=0; i<obj->nsect; i++) + if(obj->sect[i].name && name && strcmp(obj->sect[i].name, name) == 0) + return &obj->sect[i]; + return nil; +} + +static int +map(ElfObj *obj, ElfSect *sect) +{ + if(sect->base != nil) + return 0; + + if(sect->off+sect->size > obj->len) { + werrstr("elf section past end of file"); + return -1; + } + + sect->base = mal(sect->size); + werrstr("short read"); + if(Bseek(obj->f, obj->base+sect->off, 0) < 0 || Bread(obj->f, sect->base, sect->size) != sect->size) + return -1; + + return 0; +} + +static int +readsym(ElfObj *obj, int i, ElfSym *sym) +{ + Sym *s; + + if(i >= obj->nsymtab || i < 0) { + werrstr("invalid elf symbol index"); + return -1; + } + + if(obj->is64) { + ElfSymBytes64 *b; + + b = (ElfSymBytes64*)(obj->symtab->base + i*sizeof *b); + sym->name = (char*)obj->symstr->base + obj->e->e32(b->name); + sym->value = obj->e->e64(b->value); + sym->size = obj->e->e64(b->size); + sym->shndx = obj->e->e16(b->shndx); + sym->bind = b->info>>4; + sym->type = b->info&0xf; + sym->other = b->other; + } else { + ElfSymBytes *b; + + b = (ElfSymBytes*)(obj->symtab->base + i*sizeof *b); + sym->name = (char*)obj->symstr->base + obj->e->e32(b->name); + sym->value = obj->e->e32(b->value); + sym->size = obj->e->e32(b->size); + sym->shndx = obj->e->e16(b->shndx); + sym->bind = b->info>>4; + sym->type = b->info&0xf; + sym->other = b->other; + } + + s = nil; + if(strcmp(sym->name, "_GLOBAL_OFFSET_TABLE_") == 0) + sym->name = ".got"; + if(strcmp(sym->name, "__stack_chk_fail_local") == 0) + sym->other = 0; // rewrite hidden -> default visibility + switch(sym->type) { + case ElfSymTypeSection: + s = obj->sect[sym->shndx].sym; + break; + case ElfSymTypeObject: + case ElfSymTypeFunc: + case ElfSymTypeNone: + switch(sym->bind) { + case ElfSymBindGlobal: + if(sym->other != 2) { + s = lookup(sym->name, 0); + break; + } + // fall through + case ElfSymBindLocal: + s = lookup(sym->name, version); + break; + default: + werrstr("%s: invalid symbol binding %d", sym->name, sym->bind); + return -1; + } + break; + } + if(s != nil && s->type == 0 && sym->type != ElfSymTypeSection) + s->type = SXREF; + sym->sym = s; + + return 0; +} + +int +rbyoff(const void *va, const void *vb) +{ + Reloc *a, *b; + + a = (Reloc*)va; + b = (Reloc*)vb; + if(a->off < b->off) + return -1; + if(a->off > b->off) + return +1; + return 0; +} + +#define R(x, y) ((x)|((y)<<24)) + +static int +reltype(char *pn, int elftype, uchar *siz) +{ + switch(R(thechar, elftype)) { + default: + diag("%s: unknown relocation type %d; compiled without -fpic?", pn, elftype); + case R('6', R_X86_64_PC32): + case R('6', R_X86_64_PLT32): + case R('6', R_X86_64_GOTPCREL): + case R('8', R_386_32): + case R('8', R_386_PC32): + case R('8', R_386_GOT32): + case R('8', R_386_PLT32): + case R('8', R_386_GOTOFF): + case R('8', R_386_GOTPC): + *siz = 4; + break; + case R('6', R_X86_64_64): + *siz = 8; + break; + } + + return 256+elftype; +} diff --git a/src/cmd/ld/ldmacho.c b/src/cmd/ld/ldmacho.c new file mode 100644 index 000000000..388848767 --- /dev/null +++ b/src/cmd/ld/ldmacho.c @@ -0,0 +1,821 @@ +/* +Derived from Plan 9 from User Space's src/libmach/elf.h, elf.c +http://code.swtch.com/plan9port/src/tip/src/libmach/ + + Copyright © 2004 Russ Cox. + Portions Copyright © 2008-2010 Google Inc. + Portions Copyright © 2010 The Go Authors. + +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 "l.h" +#include "lib.h" + +enum { + MACHO_FAKE_GOTPCREL = 100, // from macho.h + + N_EXT = 0x01, + N_TYPE = 0x1e, + N_STAB = 0xe0, +}; + +typedef struct MachoObj MachoObj; +typedef struct MachoCmd MachoCmd; +typedef struct MachoSeg MachoSeg; +typedef struct MachoSect MachoSect; +typedef struct MachoRel MachoRel; +typedef struct MachoSymtab MachoSymtab; +typedef struct MachoSym MachoSym; +typedef struct MachoDysymtab MachoDysymtab; + +enum +{ + MachoCpuVax = 1, + MachoCpu68000 = 6, + MachoCpu386 = 7, + MachoCpuAmd64 = 0x1000007, + MachoCpuMips = 8, + MachoCpu98000 = 10, + MachoCpuHppa = 11, + MachoCpuArm = 12, + MachoCpu88000 = 13, + MachoCpuSparc = 14, + MachoCpu860 = 15, + MachoCpuAlpha = 16, + MachoCpuPower = 18, + + MachoCmdSegment = 1, + MachoCmdSymtab = 2, + MachoCmdSymseg = 3, + MachoCmdThread = 4, + MachoCmdDysymtab = 11, + MachoCmdSegment64 = 25, + + MachoFileObject = 1, + MachoFileExecutable = 2, + MachoFileFvmlib = 3, + MachoFileCore = 4, + MachoFilePreload = 5, +}; + +struct MachoSeg +{ + char name[16+1]; + uint64 vmaddr; + uint64 vmsize; + uint32 fileoff; + uint32 filesz; + uint32 maxprot; + uint32 initprot; + uint32 nsect; + uint32 flags; + MachoSect *sect; +}; + +struct MachoSect +{ + char name[16+1]; + char segname[16+1]; + uint64 addr; + uint64 size; + uint32 off; + uint32 align; + uint32 reloff; + uint32 nreloc; + uint32 flags; + uint32 res1; + uint32 res2; + Sym *sym; + + MachoRel *rel; +}; + +struct MachoRel +{ + uint32 addr; + uint32 symnum; + uint8 pcrel; + uint8 length; + uint8 extrn; + uint8 type; + uint8 scattered; + uint32 value; +}; + +struct MachoSymtab +{ + uint32 symoff; + uint32 nsym; + uint32 stroff; + uint32 strsize; + + char *str; + MachoSym *sym; +}; + +struct MachoSym +{ + char *name; + uint8 type; + uint8 sectnum; + uint16 desc; + char kind; + uint64 value; + Sym *sym; +}; + +struct MachoDysymtab +{ + uint32 ilocalsym; + uint32 nlocalsym; + uint32 iextdefsym; + uint32 nextdefsym; + uint32 iundefsym; + uint32 nundefsym; + uint32 tocoff; + uint32 ntoc; + uint32 modtaboff; + uint32 nmodtab; + uint32 extrefsymoff; + uint32 nextrefsyms; + uint32 indirectsymoff; + uint32 nindirectsyms; + uint32 extreloff; + uint32 nextrel; + uint32 locreloff; + uint32 nlocrel; + uint32 *indir; +}; + +struct MachoCmd +{ + int type; + uint32 off; + uint32 size; + MachoSeg seg; + MachoSymtab sym; + MachoDysymtab dsym; +}; + +struct MachoObj +{ + Biobuf *f; + int64 base; // off in f where Mach-O begins + int64 len; // length of Mach-O + int is64; + char *name; + + Endian *e; + uint cputype; + uint subcputype; + uint32 filetype; + uint32 flags; + MachoCmd *cmd; + uint ncmd; +}; + +static int +unpackcmd(uchar *p, MachoObj *m, MachoCmd *c, uint type, uint sz) +{ + uint32 (*e4)(uchar*); + uint64 (*e8)(uchar*); + MachoSect *s; + int i; + + e4 = m->e->e32; + e8 = m->e->e64; + + c->type = type; + c->size = sz; + switch(type){ + default: + return -1; + case MachoCmdSegment: + if(sz < 56) + return -1; + strecpy(c->seg.name, c->seg.name+sizeof c->seg.name, (char*)p+8); + c->seg.vmaddr = e4(p+24); + c->seg.vmsize = e4(p+28); + c->seg.fileoff = e4(p+32); + c->seg.filesz = e4(p+36); + c->seg.maxprot = e4(p+40); + c->seg.initprot = e4(p+44); + c->seg.nsect = e4(p+48); + c->seg.flags = e4(p+52); + c->seg.sect = mal(c->seg.nsect * sizeof c->seg.sect[0]); + if(sz < 56+c->seg.nsect*68) + return -1; + p += 56; + for(i=0; i<c->seg.nsect; i++) { + s = &c->seg.sect[i]; + strecpy(s->name, s->name+sizeof s->name, (char*)p+0); + strecpy(s->segname, s->segname+sizeof s->segname, (char*)p+16); + s->addr = e4(p+32); + s->size = e4(p+36); + s->off = e4(p+40); + s->align = e4(p+44); + s->reloff = e4(p+48); + s->nreloc = e4(p+52); + s->flags = e4(p+56); + s->res1 = e4(p+60); + s->res2 = e4(p+64); + p += 68; + } + break; + case MachoCmdSegment64: + if(sz < 72) + return -1; + strecpy(c->seg.name, c->seg.name+sizeof c->seg.name, (char*)p+8); + c->seg.vmaddr = e8(p+24); + c->seg.vmsize = e8(p+32); + c->seg.fileoff = e8(p+40); + c->seg.filesz = e8(p+48); + c->seg.maxprot = e4(p+56); + c->seg.initprot = e4(p+60); + c->seg.nsect = e4(p+64); + c->seg.flags = e4(p+68); + c->seg.sect = mal(c->seg.nsect * sizeof c->seg.sect[0]); + if(sz < 72+c->seg.nsect*80) + return -1; + p += 72; + for(i=0; i<c->seg.nsect; i++) { + s = &c->seg.sect[i]; + strecpy(s->name, s->name+sizeof s->name, (char*)p+0); + strecpy(s->segname, s->segname+sizeof s->segname, (char*)p+16); + s->addr = e8(p+32); + s->size = e8(p+40); + s->off = e4(p+48); + s->align = e4(p+52); + s->reloff = e4(p+56); + s->nreloc = e4(p+60); + s->flags = e4(p+64); + s->res1 = e4(p+68); + s->res2 = e4(p+72); + // p+76 is reserved + p += 80; + } + break; + case MachoCmdSymtab: + if(sz < 24) + return -1; + c->sym.symoff = e4(p+8); + c->sym.nsym = e4(p+12); + c->sym.stroff = e4(p+16); + c->sym.strsize = e4(p+20); + break; + case MachoCmdDysymtab: + if(sz < 80) + return -1; + c->dsym.ilocalsym = e4(p+8); + c->dsym.nlocalsym = e4(p+12); + c->dsym.iextdefsym = e4(p+16); + c->dsym.nextdefsym = e4(p+20); + c->dsym.iundefsym = e4(p+24); + c->dsym.nundefsym = e4(p+28); + c->dsym.tocoff = e4(p+32); + c->dsym.ntoc = e4(p+36); + c->dsym.modtaboff = e4(p+40); + c->dsym.nmodtab = e4(p+44); + c->dsym.extrefsymoff = e4(p+48); + c->dsym.nextrefsyms = e4(p+52); + c->dsym.indirectsymoff = e4(p+56); + c->dsym.nindirectsyms = e4(p+60); + c->dsym.extreloff = e4(p+64); + c->dsym.nextrel = e4(p+68); + c->dsym.locreloff = e4(p+72); + c->dsym.nlocrel = e4(p+76); + break; + } + return 0; +} + +static int +macholoadrel(MachoObj *m, MachoSect *sect) +{ + MachoRel *rel, *r; + uchar *buf, *p; + int i, n; + uint32 v; + + if(sect->rel != nil || sect->nreloc == 0) + return 0; + rel = mal(sect->nreloc * sizeof r[0]); + n = sect->nreloc * 8; + buf = mal(n); + if(Bseek(m->f, m->base + sect->reloff, 0) < 0 || Bread(m->f, buf, n) != n) + return -1; + for(i=0; i<sect->nreloc; i++) { + r = &rel[i]; + p = buf+i*8; + r->addr = m->e->e32(p); + + // TODO(rsc): Wrong interpretation for big-endian bitfields? + if(r->addr & 0x80000000) { + // scatterbrained relocation + r->scattered = 1; + v = r->addr >> 24; + r->addr &= 0xFFFFFF; + r->type = v & 0xF; + v >>= 4; + r->length = 1<<(v&3); + v >>= 2; + r->pcrel = v & 1; + r->value = m->e->e32(p+4); + } else { + v = m->e->e32(p+4); + r->symnum = v & 0xFFFFFF; + v >>= 24; + r->pcrel = v&1; + v >>= 1; + r->length = 1<<(v&3); + v >>= 2; + r->extrn = v&1; + v >>= 1; + r->type = v; + } + } + sect->rel = rel; + return 0; +} + +static int +macholoaddsym(MachoObj *m, MachoDysymtab *d) +{ + uchar *p; + int i, n; + + n = d->nindirectsyms; + + p = mal(n*4); + if(Bseek(m->f, m->base + d->indirectsymoff, 0) < 0 || Bread(m->f, p, n*4) != n*4) + return -1; + + d->indir = (uint32*)p; + for(i=0; i<n; i++) + d->indir[i] = m->e->e32(p+4*i); + return 0; +} + +static int +macholoadsym(MachoObj *m, MachoSymtab *symtab) +{ + char *strbuf; + uchar *symbuf, *p; + int i, n, symsize; + MachoSym *sym, *s; + uint32 v; + + if(symtab->sym != nil) + return 0; + + strbuf = mal(symtab->strsize); + if(Bseek(m->f, m->base + symtab->stroff, 0) < 0 || Bread(m->f, strbuf, symtab->strsize) != symtab->strsize) + return -1; + + symsize = 12; + if(m->is64) + symsize = 16; + n = symtab->nsym * symsize; + symbuf = mal(n); + if(Bseek(m->f, m->base + symtab->symoff, 0) < 0 || Bread(m->f, symbuf, n) != n) + return -1; + sym = mal(symtab->nsym * sizeof sym[0]); + p = symbuf; + for(i=0; i<symtab->nsym; i++) { + s = &sym[i]; + v = m->e->e32(p); + if(v >= symtab->strsize) + return -1; + s->name = strbuf + v; + s->type = p[4]; + s->sectnum = p[5]; + s->desc = m->e->e16(p+6); + if(m->is64) + s->value = m->e->e64(p+8); + else + s->value = m->e->e32(p+8); + p += symsize; + } + symtab->str = strbuf; + symtab->sym = sym; + return 0; +} + +void +ldmacho(Biobuf *f, char *pkg, int64 len, char *pn) +{ + int i, j, is64; + uint64 secaddr; + uchar hdr[7*4], *cmdp; + uchar tmp[4]; + uchar *dat; + ulong ncmd, cmdsz, ty, sz, off; + MachoObj *m; + Endian *e; + int64 base; + MachoSect *sect; + MachoRel *rel; + Sym *s, *outer; + MachoCmd *c; + MachoSymtab *symtab; + MachoDysymtab *dsymtab; + MachoSym *sym; + Reloc *r, *rp; + char *name; + + USED(pkg); + version++; + base = Boffset(f); + if(Bread(f, hdr, sizeof hdr) != sizeof hdr) + goto bad; + + if((be.e32(hdr)&~1) == 0xFEEDFACE){ + e = &be; + }else if((le.e32(hdr)&~1) == 0xFEEDFACE){ + e = ≤ + }else{ + werrstr("bad magic - not mach-o file"); + goto bad; + } + + is64 = e->e32(hdr) == 0xFEEDFACF; + ncmd = e->e32(hdr+4*4); + cmdsz = e->e32(hdr+5*4); + if(ncmd > 0x10000 || cmdsz >= 0x01000000){ + werrstr("implausible mach-o header ncmd=%lud cmdsz=%lud", ncmd, cmdsz); + goto bad; + } + if(is64) + Bread(f, tmp, 4); // skip reserved word in header + + m = mal(sizeof(*m)+ncmd*sizeof(MachoCmd)+cmdsz); + m->f = f; + m->e = e; + m->cputype = e->e32(hdr+1*4); + m->subcputype = e->e32(hdr+2*4); + m->filetype = e->e32(hdr+3*4); + m->ncmd = ncmd; + m->flags = e->e32(hdr+6*4); + m->is64 = is64; + m->base = base; + m->len = len; + m->name = pn; + + switch(thechar) { + default: + diag("%s: mach-o %s unimplemented", pn, thestring); + return; + case '6': + if(e != &le || m->cputype != MachoCpuAmd64) { + diag("%s: mach-o object but not amd64", pn); + return; + } + break; + case '8': + if(e != &le || m->cputype != MachoCpu386) { + diag("%s: mach-o object but not 386", pn); + return; + } + break; + } + + m->cmd = (MachoCmd*)(m+1); + off = sizeof hdr; + cmdp = (uchar*)(m->cmd+ncmd); + if(Bread(f, cmdp, cmdsz) != cmdsz){ + werrstr("reading cmds: %r"); + goto bad; + } + + // read and parse load commands + c = nil; + symtab = nil; + dsymtab = nil; + for(i=0; i<ncmd; i++){ + ty = e->e32(cmdp); + sz = e->e32(cmdp+4); + m->cmd[i].off = off; + unpackcmd(cmdp, m, &m->cmd[i], ty, sz); + cmdp += sz; + off += sz; + if(ty == MachoCmdSymtab) { + if(symtab != nil) { + werrstr("multiple symbol tables"); + goto bad; + } + symtab = &m->cmd[i].sym; + macholoadsym(m, symtab); + } + if(ty == MachoCmdDysymtab) { + dsymtab = &m->cmd[i].dsym; + macholoaddsym(m, dsymtab); + } + if((is64 && ty == MachoCmdSegment64) || (!is64 && ty == MachoCmdSegment)) { + if(c != nil) { + werrstr("multiple load commands"); + goto bad; + } + c = &m->cmd[i]; + } + } + + // load text and data segments into memory. + // they are not as small as the load commands, but we'll need + // the memory anyway for the symbol images, so we might + // as well use one large chunk. + if(c == nil) { + werrstr("no load command"); + goto bad; + } + if(symtab == nil) { + // our work is done here - no symbols means nothing can refer to this file + return; + } + + if(c->seg.fileoff+c->seg.filesz >= len) { + werrstr("load segment out of range"); + goto bad; + } + + dat = mal(c->seg.filesz); + if(Bseek(f, m->base + c->seg.fileoff, 0) < 0 || Bread(f, dat, c->seg.filesz) != c->seg.filesz) { + werrstr("cannot load object data: %r"); + goto bad; + } + + for(i=0; i<c->seg.nsect; i++) { + sect = &c->seg.sect[i]; + if(strcmp(sect->segname, "__TEXT") != 0 && strcmp(sect->segname, "__DATA") != 0) + continue; + if(strcmp(sect->name, "__eh_frame") == 0) + continue; + name = smprint("%s(%s/%s)", pn, sect->segname, sect->name); + s = lookup(name, version); + if(s->type != 0) { + werrstr("duplicate %s/%s", sect->segname, sect->name); + goto bad; + } + free(name); + s->p = dat + sect->addr - c->seg.vmaddr; + s->np = sect->size; + s->size = s->np; + + if(strcmp(sect->segname, "__TEXT") == 0) { + if(strcmp(sect->name, "__text") == 0) + s->type = STEXT; + else + s->type = SRODATA; + } else { + if (strcmp(sect->name, "__bss") == 0) { + s->type = SBSS; + s->np = 0; + } else + s->type = SDATA; + } + if(s->type == STEXT) { + if(etextp) + etextp->next = s; + else + textp = s; + etextp = s; + } + sect->sym = s; + } + + // enter sub-symbols into symbol table. + // have to guess sizes from next symbol. + for(i=0; i<symtab->nsym; i++) { + int v; + sym = &symtab->sym[i]; + if(sym->type&N_STAB) + continue; + // TODO: check sym->type against outer->type. + name = sym->name; + if(name[0] == '_' && name[1] != '\0') + name++; + v = 0; + if(!(sym->type&N_EXT)) + v = version; + s = lookup(name, v); + sym->sym = s; + if(sym->sectnum == 0) // undefined + continue; + if(sym->sectnum > c->seg.nsect) { + werrstr("reference to invalid section %d", sym->sectnum); + goto bad; + } + sect = &c->seg.sect[sym->sectnum-1]; + outer = sect->sym; + if(outer == nil) { + werrstr("reference to invalid section %s/%s", sect->segname, sect->name); + continue; + } + s->type = outer->type | SSUB; + s->sub = outer->sub; + outer->sub = s; + s->outer = outer; + s->value = sym->value - sect->addr; + if(i+1 < symtab->nsym) + s->size = (sym+1)->value - sym->value; + else + s->size = sect->addr + sect->size - sym->value; + if(!s->dynexport) { + s->dynimplib = nil; // satisfy dynimport + s->dynimpname = nil; // satisfy dynimport + } + if(outer->type == STEXT) { + Prog *p; + + if(s->text != P) + diag("%s sym#%d: duplicate definition of %s", pn, i, s->name); + // build a TEXT instruction with a unique pc + // just to make the rest of the linker happy. + // TODO: this is too 6l-specific ? + p = prg(); + p->as = ATEXT; + p->from.type = D_EXTERN; + p->from.sym = s; + p->textflag = 7; + p->to.type = D_CONST; + p->link = nil; + p->pc = pc++; + s->text = p; + + etextp->next = s; + etextp = s; + } + sym->sym = s; + } + + // load relocations + for(i=0; i<c->seg.nsect; i++) { + sect = &c->seg.sect[i]; + if((s = sect->sym) == S) + continue; + macholoadrel(m, sect); + if(sect->rel == nil) + continue; + r = mal(sect->nreloc*sizeof r[0]); + rp = r; + rel = sect->rel; + for(j=0; j<sect->nreloc; j++, rel++) { + if(rel->scattered) { + int k; + MachoSect *ks; + + if(thechar != '8') + diag("unexpected scattered relocation"); + + // on 386, rewrite scattered 4/1 relocation into + // the pseudo-pc-relative reference that it is. + // assume that the second in the pair is in this section + // and use that as the pc-relative base. + if(thechar != '8' || rel->type != 4 || j+1 >= sect->nreloc || + !(rel+1)->scattered || (rel+1)->type != 1 || + (rel+1)->value < sect->addr || (rel+1)->value >= sect->addr+sect->size) { + werrstr("unsupported scattered relocation %d/%d", (int)rel->type, (int)(rel+1)->type); + goto bad; + } + rp->siz = rel->length; + rp->off = rel->addr; + + // NOTE(rsc): I haven't worked out why (really when) + // we should ignore the addend on a + // scattered relocation, but it seems that the + // common case is we ignore it. + // It's likely that this is not strictly correct + // and that the math should look something + // like the non-scattered case below. + rp->add = 0; + + // want to make it pc-relative aka relative to rp->off+4 + // but the scatter asks for relative to off = (rel+1)->value - sect->addr. + // adjust rp->add accordingly. + rp->type = D_PCREL; + rp->add += (rp->off+4) - ((rel+1)->value - sect->addr); + + // now consider the desired symbol. + // find the section where it lives. + for(k=0; k<c->seg.nsect; k++) { + ks = &c->seg.sect[k]; + if(ks->addr <= rel->value && rel->value < ks->addr+ks->size) + goto foundk; + } + werrstr("unsupported scattered relocation: invalid address %#ux", rel->addr); + goto bad; + foundk: + if(ks->sym != S) { + rp->sym = ks->sym; + rp->add += rel->value - ks->addr; + } else if(strcmp(ks->segname, "__IMPORT") == 0 && strcmp(ks->name, "__pointers") == 0) { + // handle reference to __IMPORT/__pointers. + // how much worse can this get? + // why are we supporting 386 on the mac anyway? + rp->type = 512 + MACHO_FAKE_GOTPCREL; + // figure out which pointer this is a reference to. + k = ks->res1 + (rel->value - ks->addr) / 4; + // load indirect table for __pointers + // fetch symbol number + if(dsymtab == nil || k < 0 || k >= dsymtab->nindirectsyms || dsymtab->indir == nil) { + werrstr("invalid scattered relocation: indirect symbol reference out of range"); + goto bad; + } + k = dsymtab->indir[k]; + if(k < 0 || k >= symtab->nsym) { + werrstr("invalid scattered relocation: symbol reference out of range"); + goto bad; + } + rp->sym = symtab->sym[k].sym; + } else { + werrstr("unsupported scattered relocation: reference to %s/%s", ks->segname, ks->name); + goto bad; + } + rp++; + // skip #1 of 2 rel; continue skips #2 of 2. + rel++; + j++; + continue; + } + + rp->siz = rel->length; + rp->type = 512 + (rel->type<<1) + rel->pcrel; + rp->off = rel->addr; + + // Handle X86_64_RELOC_SIGNED referencing a section (rel->extrn == 0). + if (thechar == '6' && rel->extrn == 0 && rel->type == 1) { + // Calculate the addend as the offset into the section. + // + // The rip-relative offset stored in the object file is encoded + // as follows: + // + // movsd 0x00000360(%rip),%xmm0 + // + // To get the absolute address of the value this rip-relative address is pointing + // to, we must add the address of the next instruction to it. This is done by + // taking the address of the relocation and adding 4 to it (since the rip-relative + // offset can at most be 32 bits long). To calculate the offset into the section the + // relocation is referencing, we subtract the vaddr of the start of the referenced + // section found in the original object file. + // + // [For future reference, see Darwin's /usr/include/mach-o/x86_64/reloc.h] + secaddr = c->seg.sect[rel->symnum-1].addr; + rp->add = e->e32(s->p+rp->off) + rp->off + 4 - secaddr; + } else + rp->add = e->e32(s->p+rp->off); + + // For i386 Mach-O PC-relative, the addend is written such that + // it *is* the PC being subtracted. Use that to make + // it match our version of PC-relative. + if(rel->pcrel && thechar == '8') + rp->add += rp->off+rp->siz; + if(!rel->extrn) { + if(rel->symnum < 1 || rel->symnum > c->seg.nsect) { + werrstr("invalid relocation: section reference out of range %d vs %d", rel->symnum, c->seg.nsect); + goto bad; + } + rp->sym = c->seg.sect[rel->symnum-1].sym; + if(rp->sym == nil) { + werrstr("invalid relocation: %s", c->seg.sect[rel->symnum-1].name); + goto bad; + } + // References to symbols in other sections + // include that information in the addend. + // We only care about the delta from the + // section base. + if(thechar == '8') + rp->add -= c->seg.sect[rel->symnum-1].addr; + } else { + if(rel->symnum >= symtab->nsym) { + werrstr("invalid relocation: symbol reference out of range"); + goto bad; + } + rp->sym = symtab->sym[rel->symnum].sym; + } + rp++; + } + qsort(r, rp - r, sizeof r[0], rbyoff); + s->r = r; + s->nr = rp - r; + } + return; + +bad: + diag("%s: malformed mach-o file: %r", pn); +} diff --git a/src/cmd/ld/ldpe.c b/src/cmd/ld/ldpe.c new file mode 100644 index 000000000..680557075 --- /dev/null +++ b/src/cmd/ld/ldpe.c @@ -0,0 +1,451 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "l.h" +#include "lib.h" +#include "../ld/pe.h" + +#define IMAGE_SCN_MEM_DISCARDABLE 0x2000000 + +#define IMAGE_SYM_UNDEFINED 0 +#define IMAGE_SYM_ABSOLUTE (-1) +#define IMAGE_SYM_DEBUG (-2) +#define IMAGE_SYM_TYPE_NULL 0 +#define IMAGE_SYM_TYPE_VOID 1 +#define IMAGE_SYM_TYPE_CHAR 2 +#define IMAGE_SYM_TYPE_SHORT 3 +#define IMAGE_SYM_TYPE_INT 4 +#define IMAGE_SYM_TYPE_LONG 5 +#define IMAGE_SYM_TYPE_FLOAT 6 +#define IMAGE_SYM_TYPE_DOUBLE 7 +#define IMAGE_SYM_TYPE_STRUCT 8 +#define IMAGE_SYM_TYPE_UNION 9 +#define IMAGE_SYM_TYPE_ENUM 10 +#define IMAGE_SYM_TYPE_MOE 11 +#define IMAGE_SYM_TYPE_BYTE 12 +#define IMAGE_SYM_TYPE_WORD 13 +#define IMAGE_SYM_TYPE_UINT 14 +#define IMAGE_SYM_TYPE_DWORD 15 +#define IMAGE_SYM_TYPE_PCODE 32768 +#define IMAGE_SYM_DTYPE_NULL 0 +#define IMAGE_SYM_DTYPE_POINTER 0x10 +#define IMAGE_SYM_DTYPE_FUNCTION 0x20 +#define IMAGE_SYM_DTYPE_ARRAY 0x30 +#define IMAGE_SYM_CLASS_END_OF_FUNCTION (-1) +#define IMAGE_SYM_CLASS_NULL 0 +#define IMAGE_SYM_CLASS_AUTOMATIC 1 +#define IMAGE_SYM_CLASS_EXTERNAL 2 +#define IMAGE_SYM_CLASS_STATIC 3 +#define IMAGE_SYM_CLASS_REGISTER 4 +#define IMAGE_SYM_CLASS_EXTERNAL_DEF 5 +#define IMAGE_SYM_CLASS_LABEL 6 +#define IMAGE_SYM_CLASS_UNDEFINED_LABEL 7 +#define IMAGE_SYM_CLASS_MEMBER_OF_STRUCT 8 +#define IMAGE_SYM_CLASS_ARGUMENT 9 +#define IMAGE_SYM_CLASS_STRUCT_TAG 10 +#define IMAGE_SYM_CLASS_MEMBER_OF_UNION 11 +#define IMAGE_SYM_CLASS_UNION_TAG 12 +#define IMAGE_SYM_CLASS_TYPE_DEFINITION 13 +#define IMAGE_SYM_CLASS_UNDEFINED_STATIC 14 +#define IMAGE_SYM_CLASS_ENUM_TAG 15 +#define IMAGE_SYM_CLASS_MEMBER_OF_ENUM 16 +#define IMAGE_SYM_CLASS_REGISTER_PARAM 17 +#define IMAGE_SYM_CLASS_BIT_FIELD 18 +#define IMAGE_SYM_CLASS_FAR_EXTERNAL 68 /* Not in PECOFF v8 spec */ +#define IMAGE_SYM_CLASS_BLOCK 100 +#define IMAGE_SYM_CLASS_FUNCTION 101 +#define IMAGE_SYM_CLASS_END_OF_STRUCT 102 +#define IMAGE_SYM_CLASS_FILE 103 +#define IMAGE_SYM_CLASS_SECTION 104 +#define IMAGE_SYM_CLASS_WEAK_EXTERNAL 105 +#define IMAGE_SYM_CLASS_CLR_TOKEN 107 + +#define IMAGE_REL_I386_ABSOLUTE 0x0000 +#define IMAGE_REL_I386_DIR16 0x0001 +#define IMAGE_REL_I386_REL16 0x0002 +#define IMAGE_REL_I386_DIR32 0x0006 +#define IMAGE_REL_I386_DIR32NB 0x0007 +#define IMAGE_REL_I386_SEG12 0x0009 +#define IMAGE_REL_I386_SECTION 0x000A +#define IMAGE_REL_I386_SECREL 0x000B +#define IMAGE_REL_I386_TOKEN 0x000C +#define IMAGE_REL_I386_SECREL7 0x000D +#define IMAGE_REL_I386_REL32 0x0014 + +#define IMAGE_REL_AMD64_ABSOLUTE 0x0000 +#define IMAGE_REL_AMD64_ADDR64 0x0001 // R_X86_64_64 +#define IMAGE_REL_AMD64_ADDR32 0x0002 // R_X86_64_PC32 +#define IMAGE_REL_AMD64_ADDR32NB 0x0003 +#define IMAGE_REL_AMD64_REL32 0x0004 +#define IMAGE_REL_AMD64_REL32_1 0x0005 +#define IMAGE_REL_AMD64_REL32_2 0x0006 +#define IMAGE_REL_AMD64_REL32_3 0x0007 +#define IMAGE_REL_AMD64_REL32_4 0x0008 +#define IMAGE_REL_AMD64_REL32_5 0x0009 +#define IMAGE_REL_AMD64_SECTION 0x000A +#define IMAGE_REL_AMD64_SECREL 0x000B +#define IMAGE_REL_AMD64_SECREL7 0x000C +#define IMAGE_REL_AMD64_TOKEN 0x000D +#define IMAGE_REL_AMD64_SREL32 0x000E +#define IMAGE_REL_AMD64_PAIR 0x000F +#define IMAGE_REL_AMD64_SSPAN32 0x0010 + +typedef struct PeSym PeSym; +typedef struct PeSect PeSect; +typedef struct PeObj PeObj; + +struct PeSym { + char* name; + uint32 value; + uint16 sectnum; + uint16 type; + uint8 sclass; + uint8 aux; + Sym* sym; +}; + +struct PeSect { + char* name; + uchar* base; + uint64 size; + Sym* sym; + IMAGE_SECTION_HEADER sh; +}; + +struct PeObj { + Biobuf *f; + char *name; + uint32 base; + + PeSect *sect; + uint nsect; + PeSym *pesym; + uint npesym; + + IMAGE_FILE_HEADER fh; + char* snames; +}; + +static int map(PeObj *obj, PeSect *sect); +static int readsym(PeObj *obj, int i, PeSym **sym); + +void +ldpe(Biobuf *f, char *pkg, int64 len, char *pn) +{ + char *name; + int32 base; + int i, j, l, numaux; + PeObj *obj; + PeSect *sect, *rsect; + IMAGE_SECTION_HEADER sh; + uchar symbuf[18]; + Sym *s; + Reloc *r, *rp; + PeSym *sym; + + USED(len); + USED(pkg); + if(debug['v']) + Bprint(&bso, "%5.2f ldpe %s\n", cputime(), pn); + + sect = nil; + version++; + base = Boffset(f); + + obj = mal(sizeof *obj); + obj->f = f; + obj->base = base; + obj->name = pn; + // read header + if(Bread(f, &obj->fh, sizeof obj->fh) != sizeof obj->fh) + goto bad; + // load section list + obj->sect = mal(obj->fh.NumberOfSections*sizeof obj->sect[0]); + obj->nsect = obj->fh.NumberOfSections; + for(i=0; i < obj->fh.NumberOfSections; i++) { + if(Bread(f, &obj->sect[i].sh, sizeof sh) != sizeof sh) + goto bad; + obj->sect[i].size = obj->sect[i].sh.SizeOfRawData; + obj->sect[i].name = (char*)obj->sect[i].sh.Name; + // TODO return error if found .cormeta + } + // load string table + Bseek(f, base+obj->fh.PointerToSymbolTable+18*obj->fh.NumberOfSymbols, 0); + if(Bread(f, &l, sizeof l) != sizeof l) + goto bad; + obj->snames = mal(l); + Bseek(f, base+obj->fh.PointerToSymbolTable+18*obj->fh.NumberOfSymbols, 0); + if(Bread(f, obj->snames, l) != l) + goto bad; + // read symbols + obj->pesym = mal(obj->fh.NumberOfSymbols*sizeof obj->pesym[0]); + obj->npesym = obj->fh.NumberOfSymbols; + Bseek(f, base+obj->fh.PointerToSymbolTable, 0); + for(i=0; i<obj->fh.NumberOfSymbols; i+=numaux+1) { + Bseek(f, base+obj->fh.PointerToSymbolTable+sizeof(symbuf)*i, 0); + if(Bread(f, symbuf, sizeof symbuf) != sizeof symbuf) + goto bad; + + if((symbuf[0] == 0) && (symbuf[1] == 0) && + (symbuf[2] == 0) && (symbuf[3] == 0)) { + l = le32(&symbuf[4]); + obj->pesym[i].name = (char*)&obj->snames[l]; + } else { // sym name length <= 8 + obj->pesym[i].name = mal(9); + strncpy(obj->pesym[i].name, (char*)symbuf, 8); + obj->pesym[i].name[8] = 0; + } + obj->pesym[i].value = le32(&symbuf[8]); + obj->pesym[i].sectnum = le16(&symbuf[12]); + obj->pesym[i].sclass = symbuf[16]; + obj->pesym[i].aux = symbuf[17]; + obj->pesym[i].type = le16(&symbuf[14]); + numaux = obj->pesym[i].aux; + if (numaux < 0) + numaux = 0; + } + // create symbols for mapped sections + for(i=0; i<obj->nsect; i++) { + sect = &obj->sect[i]; + if(sect->sh.Characteristics&IMAGE_SCN_MEM_DISCARDABLE) + continue; + if(map(obj, sect) < 0) + goto bad; + + name = smprint("%s(%s)", pn, sect->name); + s = lookup(name, version); + free(name); + switch(sect->sh.Characteristics&(IMAGE_SCN_CNT_UNINITIALIZED_DATA|IMAGE_SCN_CNT_INITIALIZED_DATA| + IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE|IMAGE_SCN_CNT_CODE|IMAGE_SCN_MEM_EXECUTE)) { + case IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_MEM_READ: //.rdata + s->type = SRODATA; + break; + case IMAGE_SCN_CNT_UNINITIALIZED_DATA|IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE: //.bss + s->type = SBSS; + break; + case IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE: //.data + s->type = SDATA; + break; + case IMAGE_SCN_CNT_CODE|IMAGE_SCN_MEM_EXECUTE|IMAGE_SCN_MEM_READ: //.text + s->type = STEXT; + break; + default: + werrstr("unexpected flags for PE section %s", sect->name); + goto bad; + } + s->p = sect->base; + s->np = sect->size; + s->size = sect->size; + if(s->type == STEXT) { + if(etextp) + etextp->next = s; + else + textp = s; + etextp = s; + } + sect->sym = s; + if(strcmp(sect->name, ".rsrc") == 0) + setpersrc(sect->sym); + } + + // load relocations + for(i=0; i<obj->nsect; i++) { + rsect = &obj->sect[i]; + if(rsect->sym == 0 || rsect->sh.NumberOfRelocations == 0) + continue; + if(rsect->sh.Characteristics&IMAGE_SCN_MEM_DISCARDABLE) + continue; + r = mal(rsect->sh.NumberOfRelocations*sizeof r[0]); + Bseek(f, obj->base+rsect->sh.PointerToRelocations, 0); + for(j=0; j<rsect->sh.NumberOfRelocations; j++) { + rp = &r[j]; + if(Bread(f, symbuf, 10) != 10) + goto bad; + + uint32 rva, symindex; + uint16 type; + rva = le32(&symbuf[0]); + symindex = le32(&symbuf[4]); + type = le16(&symbuf[8]); + if(readsym(obj, symindex, &sym) < 0) + goto bad; + if(sym->sym == nil) { + werrstr("reloc of invalid sym %s idx=%d type=%d", sym->name, symindex, sym->type); + goto bad; + } + rp->sym = sym->sym; + rp->siz = 4; + rp->off = rva; + switch(type) { + default: + diag("%s: unknown relocation type %d;", pn, type); + case IMAGE_REL_I386_REL32: + case IMAGE_REL_AMD64_REL32: + rp->type = D_PCREL; + rp->add = 0; + break; + case IMAGE_REL_I386_DIR32NB: + case IMAGE_REL_I386_DIR32: + rp->type = D_ADDR; + // load addend from image + rp->add = le32(rsect->base+rp->off); + break; + case IMAGE_REL_AMD64_ADDR32: // R_X86_64_PC32 + rp->type = D_PCREL; + rp->add += 4; + break; + case IMAGE_REL_AMD64_ADDR64: // R_X86_64_64 + rp->siz = 8; + rp->type = D_ADDR; + // load addend from image + rp->add = le64(rsect->base+rp->off); + break; + } + } + qsort(r, rsect->sh.NumberOfRelocations, sizeof r[0], rbyoff); + + s = rsect->sym; + s->r = r; + s->nr = rsect->sh.NumberOfRelocations; + } + + // enter sub-symbols into symbol table. + for(i=0; i<obj->npesym; i++) { + if(obj->pesym[i].name == 0) + continue; + if(obj->pesym[i].name[0] == '.') //skip section + continue; + if(obj->pesym[i].sectnum > 0) { + sect = &obj->sect[obj->pesym[i].sectnum-1]; + if(sect->sym == 0) + continue; + } + if(readsym(obj, i, &sym) < 0) + goto bad; + + s = sym->sym; + if(sym->sectnum == 0) {// extern + if(s->type == SDYNIMPORT) + s->plt = -2; // flag for dynimport in PE object files. + if (s->type == SXREF && sym->value > 0) {// global data + s->type = SDATA; + s->size = sym->value; + } + continue; + } else if (sym->sectnum > 0) { + sect = &obj->sect[sym->sectnum-1]; + if(sect->sym == 0) + diag("%s: %s sym == 0!", pn, s->name); + } else { + diag("%s: %s sectnum < 0!", pn, s->name); + } + + if(sect == nil) + return; + s->sub = sect->sym->sub; + sect->sym->sub = s; + s->type = sect->sym->type | SSUB; + s->value = sym->value; + s->size = 4; + s->outer = sect->sym; + if(sect->sym->type == STEXT) { + Prog *p; + + if(s->text != P) + diag("%s: duplicate definition of %s", pn, s->name); + // build a TEXT instruction with a unique pc + // just to make the rest of the linker happy. + p = prg(); + p->as = ATEXT; + p->from.type = D_EXTERN; + p->from.sym = s; + p->textflag = 7; + p->to.type = D_CONST; + p->link = nil; + p->pc = pc++; + s->text = p; + + etextp->next = s; + etextp = s; + } + } + + return; +bad: + diag("%s: malformed pe file: %r", pn); +} + +static int +map(PeObj *obj, PeSect *sect) +{ + if(sect->base != nil) + return 0; + + sect->base = mal(sect->sh.SizeOfRawData); + if(sect->sh.PointerToRawData == 0) // .bss doesn't have data in object file + return 0; + werrstr("short read"); + if(Bseek(obj->f, obj->base+sect->sh.PointerToRawData, 0) < 0 || + Bread(obj->f, sect->base, sect->sh.SizeOfRawData) != sect->sh.SizeOfRawData) + return -1; + + return 0; +} + +static int +readsym(PeObj *obj, int i, PeSym **y) +{ + Sym *s; + PeSym *sym; + char *name, *p; + + if(i >= obj->npesym || i < 0) { + werrstr("invalid pe symbol index"); + return -1; + } + + sym = &obj->pesym[i]; + *y = sym; + + name = sym->name; + if(sym->sclass == IMAGE_SYM_CLASS_STATIC && sym->value == 0) // section + name = obj->sect[sym->sectnum-1].sym->name; + if(strncmp(sym->name, "__imp__", 7) == 0) + name = &sym->name[7]; // __imp__Name => Name + else if(sym->name[0] == '_') + name = &sym->name[1]; // _Name => Name + // remove last @XXX + p = strchr(name, '@'); + if(p) + *p = 0; + + switch(sym->type) { + default: + werrstr("%s: invalid symbol type %d", sym->name, sym->type); + return -1; + case IMAGE_SYM_DTYPE_FUNCTION: + case IMAGE_SYM_DTYPE_NULL: + switch(sym->sclass) { + case IMAGE_SYM_CLASS_EXTERNAL: //global + s = lookup(name, 0); + break; + case IMAGE_SYM_CLASS_NULL: + case IMAGE_SYM_CLASS_STATIC: + s = lookup(name, version); + break; + default: + werrstr("%s: invalid symbol binding %d", sym->name, sym->sclass); + return -1; + } + break; + } + + if(s != nil && s->type == 0 && !(sym->sclass == IMAGE_SYM_CLASS_STATIC && sym->value == 0)) + s->type = SXREF; + if(strncmp(sym->name, "__imp__", 7) == 0) + s->got = -2; // flag for __imp__ + sym->sym = s; + + return 0; +} diff --git a/src/cmd/ld/lib.c b/src/cmd/ld/lib.c new file mode 100644 index 000000000..5d1e6d61b --- /dev/null +++ b/src/cmd/ld/lib.c @@ -0,0 +1,1417 @@ +// 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 "l.h" +#include "lib.h" +#include "../../pkg/runtime/stack.h" + +#include <ar.h> + +int iconv(Fmt*); + +char symname[] = SYMDEF; +char pkgname[] = "__.PKGDEF"; +char* libdir[16]; +int nlibdir = 0; +static int cout = -1; + +char* goroot; +char* goarch; +char* goos; + +void +Lflag(char *arg) +{ + if(nlibdir >= nelem(libdir)-1) { + print("too many -L's: %d\n", nlibdir); + usage(); + } + libdir[nlibdir++] = arg; +} + +void +libinit(void) +{ + 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. + libdir[nlibdir++] = smprint("%s/pkg/%s_%s", goroot, goos, goarch); + + remove(outfile); + cout = create(outfile, 1, 0775); + if(cout < 0) { + diag("cannot create %s", outfile); + errorexit(); + } + + if(INITENTRY == nil) { + INITENTRY = mal(strlen(goarch)+strlen(goos)+10); + sprint(INITENTRY, "_rt0_%s_%s", goarch, goos); + } + lookup(INITENTRY, 0)->type = SXREF; +} + +void +errorexit(void) +{ + if(nerrors) { + if(cout >= 0) + remove(outfile); + 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(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']) + 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 = realloc(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); + if(debug['v']) + Bprint(&bso, "searching for %s.a in %s\n", name, pname); + if(access(pname, AEXIST) >= 0) { + addlibpath("internal", "internal", pname, name); + found = 1; + break; + } + } + if(!found) + Bprint(&bso, "warning: unable to find %s.a\n", name); +} + +void +loadlib(void) +{ + int i; + + loadinternal("runtime"); + if(thechar == '5') + loadinternal("math"); + + for(i=0; i<libraryp; i++) { + if(debug['v']) + Bprint(&bso, "%5.2f autolib: %s (from %s)\n", cputime(), library[i].file, library[i].objref); + objfile(library[i].file, library[i].pkg); + } + + // 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. + if(!havedynamic && HEADTYPE != Hdarwin) + debug['d'] = 1; + + importcycles(); +} + +/* + * look for the next file in an archive. + * adapted from libmach. + */ +int +nextar(Biobuf *bp, int off, struct ar_hdr *a) +{ + int r; + int32 arsize; + + if (off&01) + off++; + Bseek(bp, off, 0); + r = Bread(bp, a, SAR_HDR); + if(r != SAR_HDR) + return 0; + if(strncmp(a->fmag, ARFMAG, sizeof(a->fmag))) + return -1; + arsize = strtol(a->size, 0, 0); + if (arsize&1) + arsize++; + return arsize + SAR_HDR; +} + +void +objfile(char *file, char *pkg) +{ + int32 off, l; + Biobuf *f; + char magbuf[SARMAG]; + char pname[150]; + struct ar_hdr arhdr; + + pkg = smprint("%i", pkg); + + if(debug['v']) + 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, FileObj); + Bterm(f); + return; + } + + /* skip over __.SYMDEF */ + off = Boffset(f); + 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))) { + 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, pkgname, strlen(pkgname))) { + diag("%s: second entry not 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 __.SYMDEF. + */ + 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, ArchiveObj); + } + +out: + Bterm(f); +} + +void +ldobj(Biobuf *f, char *pkg, int64 len, char *pn, int whence) +{ + char *line; + int n, c1, c2, c3, c4; + uint32 magic; + vlong import0, import1, eof; + char *t; + + eof = Boffset(f) + len; + + pn = strdup(pn); + + c1 = Bgetc(f); + c2 = Bgetc(f); + c3 = Bgetc(f); + c4 = Bgetc(f); + Bungetc(f); + Bungetc(f); + Bungetc(f); + Bungetc(f); + + magic = c1<<24 | c2<<16 | c3<<8 | c4; + if(magic == 0x7f454c46) { // \x7F E L F + ldelf(f, pkg, len, pn); + return; + } + if((magic&~1) == 0xfeedface || (magic&~0x01000000) == 0xcefaedfe) { + ldmacho(f, pkg, len, pn); + return; + } + if(c1 == 0x4c && c2 == 0x01 || c1 == 0x64 && c2 == 0x86) { + ldpe(f, pkg, len, pn); + 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] = '\0'; + 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", thechar, pn, thechar, 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); + return; + } + t = smprint("%s %s %s", getgoos(), thestring, getgoversion()); + if(strcmp(line+10, t) != 0 && !debug['f']) { + diag("%s: object is [%s] expected [%s]", pn, line+10, t); + free(t); + 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); + + ldobj1(f, pkg, eof - Boffset(f), pn); + return; + +eof: + diag("truncated object file: %s", pn); +} + +static Sym* +_lookup(char *symb, int v, int creat) +{ + Sym *s; + char *p; + int32 h; + int l, c; + + h = v; + for(p=symb; c = *p; p++) + h = h+h+h + c; + l = (p - symb) + 1; + // 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(memcmp(s->name, symb, l) == 0) + return s; + if(!creat) + return nil; + + s = mal(sizeof(*s)); + if(debug['v'] > 1) + Bprint(&bso, "lookup %s\n", symb); + + s->dynid = -1; + s->plt = -1; + s->got = -1; + s->name = mal(l + 1); + memmove(s->name, symb, l); + + s->hash = hash[h]; + s->type = 0; + s->version = v; + s->value = 0; + s->sig = 0; + s->size = 0; + hash[h] = s; + nsymbol++; + + s->allsym = allsym; + allsym = 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; + + s = lookup(sp, 0); + s->sig = 0; +} + +int32 +Bget4(Biobuf *f) +{ + uchar p[4]; + + if(Bread(f, p, 4) != 4) + return 0; + return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); +} + +void +mywhatsys(void) +{ + goroot = getgoroot(); + goos = getgoos(); + goarch = thestring; // ignore $GOARCH - we know who we are +} + +int +pathchar(void) +{ + return '/'; +} + +static uchar* hunk; +static uint32 nhunk; +#define NHUNK (10UL<<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. + */ +static char* +pathtoprefix(char *s) +{ + static char hex[] = "0123456789abcdef"; + char *p, *r, *w; + int n; + + // check for chars that need escaping + n = 0; + for(r=s; *r; r++) + if(*r <= ' ' || *r == '.' || *r == '%' || *r == '"') + 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 == '%' || *r == '"') { + *w++ = '%'; + *w++ = hex[(*r>>4)&0xF]; + *w++ = hex[*r&0xF]; + } else + *w++ = *r; + } + *w = '\0'; + 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; +} + +void +mangle(char *file) +{ + fprint(2, "%s: mangled input file\n", file); + errorexit(); +} + +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; + *l = sect; + return sect; +} + +void +pclntab(void) +{ + vlong oldpc; + Prog *p; + int32 oldlc, v, s; + Sym *sym; + uchar *bp; + + sym = lookup("pclntab", 0); + sym->type = SPCLNTAB; + sym->reachable = 1; + if(debug['s']) + return; + + oldpc = INITTEXT; + oldlc = 0; + for(cursym = textp; cursym != nil; cursym = cursym->next) { + for(p = cursym->text; p != P; p = p->link) { + if(p->line == oldlc || p->as == ATEXT || p->as == ANOP) { + if(debug['O']) + Bprint(&bso, "%6llux %P\n", + (vlong)p->pc, p); + continue; + } + if(debug['O']) + Bprint(&bso, "\t\t%6d", lcsize); + v = (p->pc - oldpc) / MINLC; + while(v) { + s = 127; + if(v < 127) + s = v; + symgrow(sym, lcsize+1); + bp = sym->p + lcsize; + *bp = s+128; /* 129-255 +pc */ + if(debug['O']) + Bprint(&bso, " pc+%d*%d(%d)", s, MINLC, s+128); + v -= s; + lcsize++; + } + s = p->line - oldlc; + oldlc = p->line; + oldpc = p->pc + MINLC; + if(s > 64 || s < -64) { + symgrow(sym, lcsize+5); + bp = sym->p + lcsize; + *bp++ = 0; /* 0 vv +lc */ + *bp++ = s>>24; + *bp++ = s>>16; + *bp++ = s>>8; + *bp = s; + if(debug['O']) { + if(s > 0) + Bprint(&bso, " lc+%d(%d,%d)\n", + s, 0, s); + else + Bprint(&bso, " lc%d(%d,%d)\n", + s, 0, s); + Bprint(&bso, "%6llux %P\n", + (vlong)p->pc, p); + } + lcsize += 5; + continue; + } + symgrow(sym, lcsize+1); + bp = sym->p + lcsize; + if(s > 0) { + *bp = 0+s; /* 1-64 +lc */ + if(debug['O']) { + Bprint(&bso, " lc+%d(%d)\n", s, 0+s); + Bprint(&bso, "%6llux %P\n", + (vlong)p->pc, p); + } + } else { + *bp = 64-s; /* 65-128 -lc */ + if(debug['O']) { + Bprint(&bso, " lc%d(%d)\n", s, 64-s); + Bprint(&bso, "%6llux %P\n", + (vlong)p->pc, p); + } + } + lcsize++; + } + } + if(lcsize & 1) { + symgrow(sym, lcsize+1); + sym->p[lcsize] = 129; + lcsize++; + } + sym->size = lcsize; + lcsize = 0; + + if(debug['v'] || debug['O']) + Bprint(&bso, "lcsize = %d\n", lcsize); + Bflush(&bso); +} + +#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) +{ + return b[0] | b[1]<<8; +} + +uint32 +le32(uchar *b) +{ + return b[0] | b[1]<<8 | b[2]<<16 | 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 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 +{ + Sym *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 Sym *morestack; +static Sym *newstack; + +enum +{ + HasLinkRegister = (thechar == '5'), + CallSize = (!HasLinkRegister)*PtrSize, // bytes of stack required for a call +}; + +void +dostkcheck(void) +{ + Chain ch; + Sym *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) + continue; + cursym = s; + ch.up = nil; + 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; + 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; + + limit = up->limit; + s = up->sym; + p = s->text; + + // Small optimization: don't repeat work at top. + if(s->stkcheck && limit == StackLimit-CallSize) + return 0; + + if(depth > 100) { + diag("nosplit stack check too deep"); + stkbroke(up, 0); + return -1; + } + + if(p == nil || p->link == nil) { + // external function. + // should never be called directly. + // only diagnose the direct caller. + if(depth == 1) + 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; + 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); + return -1; + } + if(iscall(p)) { + limit -= CallSize; + ch.limit = limit; + if(p->to.type == D_BRANCH) { + // Direct call. + ch.sym = p->to.sym; + if(stkcheck(&ch, depth+1) < 0) + return -1; + } else { + // Indirect call. Assume it is a splitting function, + // so we have to make sure it can call morestack. + limit -= CallSize; + ch.sym = nil; + ch1.limit = limit; + ch1.up = &ch; + ch1.sym = morestack; + if(stkcheck(&ch1, depth+2) < 0) + return -1; + limit += CallSize; + } + limit += CallSize; + } + + } + 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->text->textflag & 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 + (!HasLinkRegister)*PtrSize); + 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 +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 +} + +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; + Fmt fmt; + int i; + char *str; + + s = va_arg(fp->args, Sym*); + if (s == S) { + fmtprint(fp, "<nil>"); + } else { + fmtstrinit(&fmt); + fmtprint(&fmt, "%s @0x%08x [%d]", s->name, s->value, 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; +} + +vlong coutpos; + +void +cflush(void) +{ + int n; + + if(cbpmax < cbp) + cbpmax = cbp; + n = cbpmax - buf.cbuf; + if(n) { + if(write(cout, buf.cbuf, n) != n) { + diag("write error: %r"); + errorexit(); + } + coutpos += n; + } + cbp = buf.cbuf; + cbc = sizeof(buf.cbuf); + cbpmax = cbp; +} + +vlong +cpos(void) +{ + return coutpos + cbp - buf.cbuf; +} + +void +cseek(vlong p) +{ + vlong start; + int delta; + + if(cbpmax < cbp) + cbpmax = cbp; + start = coutpos; + if(start <= p && p <= start+(cbpmax - buf.cbuf)) { +//print("cseek %lld in [%lld,%lld] (%lld)\n", p, start, start+sizeof(buf.cbuf), cpos()); + delta = p - (start + cbp - buf.cbuf); + cbp += delta; + cbc -= delta; +//print("now at %lld\n", cpos()); + return; + } + + cflush(); + seek(cout, p, 0); + coutpos = p; +} + +void +cwrite(void *buf, int n) +{ + cflush(); + if(write(cout, buf, n) != n) { + diag("write error: %r"); + errorexit(); + } + coutpos += n; +} diff --git a/src/cmd/ld/lib.h b/src/cmd/ld/lib.h new file mode 100644 index 000000000..d13eea31e --- /dev/null +++ b/src/cmd/ld/lib.h @@ -0,0 +1,308 @@ +// Derived from Inferno utils/6l/l.h +// http://code.google.com/p/inferno-os/source/browse/utils/6l/l.h +// +// 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. + +enum +{ + Sxxx, + + /* order here is order in output file */ + STEXT, + SMACHOPLT, + STYPE, + SSTRING, + SGOSTRING, + SRODATA, + SSYMTAB, + SPCLNTAB, + SELFROSECT, + SELFSECT, + SDATA, + SMACHO, /* Mach-O __nl_symbol_ptr */ + SMACHOGOT, + SWINDOWS, + SBSS, + + SXREF, + SMACHODYNSTR, + SMACHODYNSYM, + SMACHOINDIRECTPLT, + SMACHOINDIRECTGOT, + SFILE, + SCONST, + SDYNIMPORT, + + SSUB = 1<<8, /* sub-symbol, linked from parent via ->sub list */ + + NHASH = 100003, +}; + +typedef struct Library Library; +struct Library +{ + char *objref; // object where we found the reference + char *srcref; // src file where we found the reference + char *file; // object file + char *pkg; // import path +}; + +// Terrible but standard terminology. +// A segment describes a block of file to load into memory. +// A section further describes the pieces of that block for +// use in debuggers and such. + +typedef struct Segment Segment; +typedef struct Section Section; + +struct Segment +{ + uchar rwx; // permission as usual unix bits (5 = r-x etc) + uvlong vaddr; // virtual address + uvlong len; // length in memory + uvlong fileoff; // file offset + uvlong filelen; // length on disk + Section* sect; +}; + +struct Section +{ + uchar rwx; + char *name; + uvlong vaddr; + uvlong len; + Section *next; // in segment list + Segment *seg; +}; + +extern char symname[]; +extern char *libdir[]; +extern int nlibdir; + +EXTERN char* INITENTRY; +EXTERN char* thestring; +EXTERN Library* library; +EXTERN int libraryp; +EXTERN int nlibrary; +EXTERN Sym* hash[NHASH]; +EXTERN Sym* allsym; +EXTERN Sym* histfrog[MAXHIST]; +EXTERN uchar fnuxi8[8]; +EXTERN uchar fnuxi4[4]; +EXTERN int histfrogp; +EXTERN int histgen; +EXTERN uchar inuxi1[1]; +EXTERN uchar inuxi2[2]; +EXTERN uchar inuxi4[4]; +EXTERN uchar inuxi8[8]; +EXTERN char* outfile; +EXTERN int32 nsymbol; +EXTERN char* thestring; +EXTERN int ndynexp; +EXTERN int havedynamic; + +EXTERN Segment segtext; +EXTERN Segment segdata; +EXTERN Segment segsym; + +void addlib(char *src, char *obj); +void addlibpath(char *srcref, char *objref, char *file, char *pkg); +Section* addsection(Segment*, char*, int); +void copyhistfrog(char *buf, int nbuf); +void addhist(int32 line, int type); +void asmlc(void); +void histtoauto(void); +void collapsefrog(Sym *s); +Sym* lookup(char *symb, int v); +Sym* rlookup(char *symb, int v); +void nuxiinit(void); +int find1(int32 l, int c); +int find2(int32 l, int c); +int32 ieeedtof(Ieee *e); +double ieeedtod(Ieee *e); +void undefsym(Sym *s); +void zerosig(char *sp); +void readundefs(char *f, int t); +int32 Bget4(Biobuf *f); +void loadlib(void); +void errorexit(void); +void mangle(char*); +void objfile(char *file, char *pkg); +void libinit(void); +void pclntab(void); +void symtab(void); +void Lflag(char *arg); +void usage(void); +void adddynrel(Sym*, Reloc*); +void ldobj1(Biobuf *f, char*, int64 len, char *pn); +void ldobj(Biobuf*, char*, int64, char*, int); +void ldelf(Biobuf*, char*, int64, char*); +void ldmacho(Biobuf*, char*, int64, char*); +void ldpe(Biobuf*, char*, int64, char*); +void ldpkg(Biobuf*, char*, int64, char*, int); +void mark(Sym *s); +void mkfwd(void); +char* expandpkg(char*, char*); +void deadcode(void); +Reloc* addrel(Sym*); +void codeblk(int32, int32); +void datblk(int32, int32); +Sym* datsort(Sym*); +void reloc(void); +void relocsym(Sym*); +void savedata(Sym*, Prog*, char*); +void symgrow(Sym*, int32); +vlong addstring(Sym*, char*); +vlong adduint32(Sym*, uint32); +vlong adduint64(Sym*, uint64); +vlong addaddr(Sym*, Sym*); +vlong addaddrplus(Sym*, Sym*, int32); +vlong addpcrelplus(Sym*, Sym*, int32); +vlong addsize(Sym*, Sym*); +vlong adduint8(Sym*, uint8); +vlong adduint16(Sym*, uint16); +void asmsym(void); +void asmelfsym(void); +void asmplan9sym(void); +void strnput(char*, int); +void dodata(void); +void address(void); +void textaddress(void); +void genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)); +vlong datoff(vlong); +void adddynlib(char*); +int archreloc(Reloc*, Sym*, vlong*); +void adddynsym(Sym*); +void addexport(void); +void dostkcheck(void); +void undef(void); +void doweak(void); +void setpersrc(Sym*); + +int pathchar(void); +void* mal(uint32); +void unmal(void*, uint32); +void mywhatsys(void); +int rbyoff(const void*, const void*); + +uint16 le16(uchar*); +uint32 le32(uchar*); +uint64 le64(uchar*); +uint16 be16(uchar*); +uint32 be32(uchar*); +uint64 be64(uchar*); + +typedef struct Endian Endian; +struct Endian +{ + uint16 (*e16)(uchar*); + uint32 (*e32)(uchar*); + uint64 (*e64)(uchar*); +}; + +extern Endian be, le; + +// relocation size bits +enum { + Rbig = 128, + Rlittle = 64, +}; + +/* set by call to mywhatsys() */ +extern char* goroot; +extern char* goarch; +extern char* goos; + +/* whence for ldpkg */ +enum { + FileObj = 0, + ArchiveObj, + Pkgdef +}; + +/* executable header types */ +enum { + Hgarbunix = 0, // garbage unix + Hnoheader, // no header + Hunixcoff, // unix coff + Hrisc, // aif for risc os + Hplan9x32, // plan 9 32-bit format + Hplan9x64, // plan 9 64-bit format + Hmsdoscom, // MS-DOS .COM + Hnetbsd, // NetBSD + Hmsdosexe, // fake MS-DOS .EXE + Hixp1200, // IXP1200 (raw) + Helf, // ELF32 + Hipaq, // ipaq + Hdarwin, // Apple Mach-O + Hlinux, // Linux ELF + Hfreebsd, // FreeBSD ELF + Hwindows, // MS Windows PE + Hopenbsd, // OpenBSD ELF +}; + +typedef struct Header Header; +struct Header { + char *name; + int val; +}; + +EXTERN char* headstring; +extern Header headers[]; + +int headtype(char*); + +int Yconv(Fmt*); + +#pragma varargck type "O" int +#pragma varargck type "Y" Sym* + +// buffered output + +EXTERN Biobuf bso; + +EXTERN struct +{ + char cbuf[MAXIO]; /* output buffer */ +} buf; + +EXTERN int cbc; +EXTERN char* cbp; +EXTERN char* cbpmax; + +#define cput(c)\ + { *cbp++ = c;\ + if(--cbc <= 0)\ + cflush(); } + +void cflush(void); +vlong cpos(void); +void cseek(vlong); +void cwrite(void*, int); +void importcycles(void); +int Zconv(Fmt*); diff --git a/src/cmd/ld/macho.c b/src/cmd/ld/macho.c new file mode 100644 index 000000000..70133d665 --- /dev/null +++ b/src/cmd/ld/macho.c @@ -0,0 +1,518 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Mach-O file writing +// http://developer.apple.com/mac/library/DOCUMENTATION/DeveloperTools/Conceptual/MachORuntime/Reference/reference.html + +#include "l.h" +#include "../ld/dwarf.h" +#include "../ld/lib.h" +#include "../ld/macho.h" + +static int macho64; +static MachoHdr hdr; +static MachoLoad *load; +static MachoSeg seg[16]; +static MachoDebug xdebug[16]; +static int nload, mload, nseg, ndebug, nsect; + +// Amount of space left for adding load commands +// that refer to dynamic libraries. Because these have +// to go in the Mach-O header, we can't just pick a +// "big enough" header size. The initial header is +// one page, the non-dynamic library stuff takes +// up about 1300 bytes; we overestimate that as 2k. +static int load_budget = INITIAL_MACHO_HEADR - 2*1024; + +void +machoinit(void) +{ + switch(thechar) { + // 64-bit architectures + case '6': + macho64 = 1; + break; + + // 32-bit architectures + default: + break; + } +} + +MachoHdr* +getMachoHdr(void) +{ + return &hdr; +} + +MachoLoad* +newMachoLoad(uint32 type, uint32 ndata) +{ + MachoLoad *l; + + if(nload >= mload) { + if(mload == 0) + mload = 1; + else + mload *= 2; + load = realloc(load, mload*sizeof load[0]); + if(load == nil) { + diag("out of memory"); + errorexit(); + } + } + + if(macho64 && (ndata & 1)) + ndata++; + + l = &load[nload++]; + l->type = type; + l->ndata = ndata; + l->data = mal(ndata*4); + return l; +} + +MachoSeg* +newMachoSeg(char *name, int msect) +{ + MachoSeg *s; + + if(nseg >= nelem(seg)) { + diag("too many segs"); + errorexit(); + } + s = &seg[nseg++]; + s->name = name; + s->msect = msect; + s->sect = mal(msect*sizeof s->sect[0]); + return s; +} + +MachoSect* +newMachoSect(MachoSeg *seg, char *name) +{ + MachoSect *s; + + if(seg->nsect >= seg->msect) { + diag("too many sects in segment %s", seg->name); + errorexit(); + } + s = &seg->sect[seg->nsect++]; + s->name = name; + nsect++; + return s; +} + +MachoDebug* +newMachoDebug(void) +{ + if(ndebug >= nelem(xdebug)) { + diag("too many debugs"); + errorexit(); + } + return &xdebug[ndebug++]; +} + + +// Generic linking code. + +static char **dylib; +static int ndylib; + +static vlong linkoff; + +int +machowrite(void) +{ + vlong o1; + int loadsize; + int i, j; + MachoSeg *s; + MachoSect *t; + MachoDebug *d; + MachoLoad *l; + + o1 = cpos(); + + loadsize = 4*4*ndebug; + for(i=0; i<nload; i++) + loadsize += 4*(load[i].ndata+2); + if(macho64) { + loadsize += 18*4*nseg; + loadsize += 20*4*nsect; + } else { + loadsize += 14*4*nseg; + loadsize += 17*4*nsect; + } + + if(macho64) + LPUT(0xfeedfacf); + else + LPUT(0xfeedface); + LPUT(hdr.cpu); + LPUT(hdr.subcpu); + LPUT(2); /* file type - mach executable */ + LPUT(nload+nseg+ndebug); + LPUT(loadsize); + LPUT(1); /* flags - no undefines */ + if(macho64) + LPUT(0); /* reserved */ + + for(i=0; i<nseg; i++) { + s = &seg[i]; + if(macho64) { + LPUT(25); /* segment 64 */ + LPUT(72+80*s->nsect); + strnput(s->name, 16); + VPUT(s->vaddr); + VPUT(s->vsize); + VPUT(s->fileoffset); + VPUT(s->filesize); + LPUT(s->prot1); + LPUT(s->prot2); + LPUT(s->nsect); + LPUT(s->flag); + } else { + LPUT(1); /* segment 32 */ + LPUT(56+68*s->nsect); + strnput(s->name, 16); + LPUT(s->vaddr); + LPUT(s->vsize); + LPUT(s->fileoffset); + LPUT(s->filesize); + LPUT(s->prot1); + LPUT(s->prot2); + LPUT(s->nsect); + LPUT(s->flag); + } + for(j=0; j<s->nsect; j++) { + t = &s->sect[j]; + if(macho64) { + strnput(t->name, 16); + strnput(s->name, 16); + VPUT(t->addr); + VPUT(t->size); + LPUT(t->off); + LPUT(t->align); + LPUT(t->reloc); + LPUT(t->nreloc); + LPUT(t->flag); + LPUT(t->res1); /* reserved */ + LPUT(t->res2); /* reserved */ + LPUT(0); /* reserved */ + } else { + strnput(t->name, 16); + strnput(s->name, 16); + LPUT(t->addr); + LPUT(t->size); + LPUT(t->off); + LPUT(t->align); + LPUT(t->reloc); + LPUT(t->nreloc); + LPUT(t->flag); + LPUT(t->res1); /* reserved */ + LPUT(t->res2); /* reserved */ + } + } + } + + for(i=0; i<nload; i++) { + l = &load[i]; + LPUT(l->type); + LPUT(4*(l->ndata+2)); + for(j=0; j<l->ndata; j++) + LPUT(l->data[j]); + } + + for(i=0; i<ndebug; i++) { + d = &xdebug[i]; + LPUT(3); /* obsolete gdb debug info */ + LPUT(16); /* size of symseg command */ + LPUT(d->fileoffset); + LPUT(d->filesize); + } + + return cpos() - o1; +} + +void +domacho(void) +{ + Sym *s; + + if(debug['d']) + return; + + // empirically, string table must begin with " \x00". + s = lookup(".dynstr", 0); + s->type = SMACHODYNSTR; + s->reachable = 1; + adduint8(s, ' '); + adduint8(s, '\0'); + + s = lookup(".dynsym", 0); + s->type = SMACHODYNSYM; + s->reachable = 1; + + s = lookup(".plt", 0); // will be __symbol_stub + s->type = SMACHOPLT; + s->reachable = 1; + + s = lookup(".got", 0); // will be __nl_symbol_ptr + s->type = SMACHOGOT; + s->reachable = 1; + + s = lookup(".linkedit.plt", 0); // indirect table for .plt + s->type = SMACHOINDIRECTPLT; + s->reachable = 1; + + s = lookup(".linkedit.got", 0); // indirect table for .got + s->type = SMACHOINDIRECTGOT; + s->reachable = 1; +} + +void +machoadddynlib(char *lib) +{ + // Will need to store the library name rounded up + // and 24 bytes of header metadata. If not enough + // space, grab another page of initial space at the + // beginning of the output file. + load_budget -= (strlen(lib)+7)/8*8 + 24; + if(load_budget < 0) { + HEADR += 4096; + INITTEXT += 4096; + load_budget += 4096; + } + + if(ndylib%32 == 0) { + dylib = realloc(dylib, (ndylib+32)*sizeof dylib[0]); + if(dylib == nil) { + diag("out of memory"); + errorexit(); + } + } + dylib[ndylib++] = lib; +} + +void +asmbmacho(void) +{ + vlong v, w; + vlong va; + int a, i; + MachoHdr *mh; + MachoSect *msect; + MachoSeg *ms; + MachoDebug *md; + MachoLoad *ml; + Sym *s; + + /* apple MACH */ + va = INITTEXT - HEADR; + mh = getMachoHdr(); + switch(thechar){ + default: + diag("unknown mach architecture"); + errorexit(); + case '6': + mh->cpu = MACHO_CPU_AMD64; + mh->subcpu = MACHO_SUBCPU_X86; + break; + case '8': + mh->cpu = MACHO_CPU_386; + mh->subcpu = MACHO_SUBCPU_X86; + break; + } + + /* segment for zero page */ + ms = newMachoSeg("__PAGEZERO", 0); + ms->vsize = va; + + /* text */ + v = rnd(HEADR+segtext.len, INITRND); + ms = newMachoSeg("__TEXT", 2); + ms->vaddr = va; + ms->vsize = v; + ms->filesize = v; + ms->prot1 = 7; + ms->prot2 = 5; + + msect = newMachoSect(ms, "__text"); + msect->addr = INITTEXT; + msect->size = segtext.sect->len; + msect->off = INITTEXT - va; + msect->flag = 0x400; /* flag - some instructions */ + + s = lookup(".plt", 0); + if(s->size > 0) { + msect = newMachoSect(ms, "__symbol_stub1"); + msect->addr = symaddr(s); + msect->size = s->size; + msect->off = ms->fileoffset + msect->addr - ms->vaddr; + msect->flag = 0x80000408; /* flag */ + msect->res1 = 0; /* index into indirect symbol table */ + msect->res2 = 6; /* size of stubs */ + } + + /* data */ + w = segdata.len; + ms = newMachoSeg("__DATA", 3); + ms->vaddr = va+v; + ms->vsize = w; + ms->fileoffset = v; + ms->filesize = segdata.filelen; + ms->prot1 = 7; + ms->prot2 = 3; + + msect = newMachoSect(ms, "__data"); + msect->addr = va+v; + msect->off = v; + msect->size = segdata.filelen; + + s = lookup(".got", 0); + if(s->size > 0) { + msect->size = symaddr(s) - msect->addr; + + msect = newMachoSect(ms, "__nl_symbol_ptr"); + msect->addr = symaddr(s); + msect->size = s->size; + msect->off = datoff(msect->addr); + msect->align = 2; + msect->flag = 6; /* section with nonlazy symbol pointers */ + msect->res1 = lookup(".linkedit.plt", 0)->size / 4; /* offset into indirect symbol table */ + } + + msect = newMachoSect(ms, "__bss"); + msect->addr = va+v+segdata.filelen; + msect->size = segdata.len - segdata.filelen; + msect->flag = 1; /* flag - zero fill */ + + switch(thechar) { + default: + diag("unknown macho architecture"); + errorexit(); + case '6': + ml = newMachoLoad(5, 42+2); /* unix thread */ + ml->data[0] = 4; /* thread type */ + ml->data[1] = 42; /* word count */ + ml->data[2+32] = entryvalue(); /* start pc */ + ml->data[2+32+1] = entryvalue()>>16>>16; // hide >>32 for 8l + break; + case '8': + ml = newMachoLoad(5, 16+2); /* unix thread */ + ml->data[0] = 1; /* thread type */ + ml->data[1] = 16; /* word count */ + ml->data[2+10] = entryvalue(); /* start pc */ + break; + } + + if(!debug['d']) { + Sym *s1, *s2, *s3, *s4; + + // must match domacholink below + s1 = lookup(".dynsym", 0); + s2 = lookup(".dynstr", 0); + s3 = lookup(".linkedit.plt", 0); + s4 = lookup(".linkedit.got", 0); + + ms = newMachoSeg("__LINKEDIT", 0); + ms->vaddr = va+v+rnd(segdata.len, INITRND); + ms->vsize = s1->size + s2->size + s3->size + s4->size; + ms->fileoffset = linkoff; + ms->filesize = ms->vsize; + ms->prot1 = 7; + ms->prot2 = 3; + + ml = newMachoLoad(2, 4); /* LC_SYMTAB */ + ml->data[0] = linkoff; /* symoff */ + ml->data[1] = s1->size / (macho64 ? 16 : 12); /* nsyms */ + ml->data[2] = linkoff + s1->size; /* stroff */ + ml->data[3] = s2->size; /* strsize */ + + ml = newMachoLoad(11, 18); /* LC_DYSYMTAB */ + ml->data[0] = 0; /* ilocalsym */ + ml->data[1] = 0; /* nlocalsym */ + ml->data[2] = 0; /* iextdefsym */ + ml->data[3] = ndynexp; /* nextdefsym */ + ml->data[4] = ndynexp; /* iundefsym */ + ml->data[5] = (s1->size / (macho64 ? 16 : 12)) - ndynexp; /* nundefsym */ + ml->data[6] = 0; /* tocoffset */ + ml->data[7] = 0; /* ntoc */ + ml->data[8] = 0; /* modtaboff */ + ml->data[9] = 0; /* nmodtab */ + ml->data[10] = 0; /* extrefsymoff */ + ml->data[11] = 0; /* nextrefsyms */ + ml->data[12] = linkoff + s1->size + s2->size; /* indirectsymoff */ + ml->data[13] = (s3->size + s4->size) / 4; /* nindirectsyms */ + ml->data[14] = 0; /* extreloff */ + ml->data[15] = 0; /* nextrel */ + ml->data[16] = 0; /* locreloff */ + ml->data[17] = 0; /* nlocrel */ + + ml = newMachoLoad(14, 6); /* LC_LOAD_DYLINKER */ + ml->data[0] = 12; /* offset to string */ + strcpy((char*)&ml->data[1], "/usr/lib/dyld"); + + for(i=0; i<ndylib; i++) { + ml = newMachoLoad(12, 4+(strlen(dylib[i])+1+7)/8*2); /* LC_LOAD_DYLIB */ + ml->data[0] = 24; /* offset of string from beginning of load */ + ml->data[1] = 0; /* time stamp */ + ml->data[2] = 0; /* version */ + ml->data[3] = 0; /* compatibility version */ + strcpy((char*)&ml->data[4], dylib[i]); + } + } + + if(!debug['s']) { + Sym *s; + + md = newMachoDebug(); + s = lookup("symtab", 0); + md->fileoffset = datoff(s->value); + md->filesize = s->size; + + md = newMachoDebug(); + s = lookup("pclntab", 0); + md->fileoffset = datoff(s->value); + md->filesize = s->size; + + dwarfaddmachoheaders(); + } + + a = machowrite(); + if(a > HEADR) + diag("HEADR too small: %d > %d", a, HEADR); +} + +vlong +domacholink(void) +{ + int size; + Sym *s1, *s2, *s3, *s4; + + // write data that will be linkedit section + s1 = lookup(".dynsym", 0); + relocsym(s1); + s2 = lookup(".dynstr", 0); + s3 = lookup(".linkedit.plt", 0); + s4 = lookup(".linkedit.got", 0); + + while(s2->size%4) + adduint8(s2, 0); + + size = s1->size + s2->size + s3->size + s4->size; + + if(size > 0) { + linkoff = rnd(HEADR+segtext.len, INITRND) + rnd(segdata.filelen, INITRND); + cseek(linkoff); + + cwrite(s1->p, s1->size); + cwrite(s2->p, s2->size); + cwrite(s3->p, s3->size); + cwrite(s4->p, s4->size); + } + + return rnd(size, INITRND); +} diff --git a/src/cmd/ld/macho.h b/src/cmd/ld/macho.h new file mode 100644 index 000000000..f55104150 --- /dev/null +++ b/src/cmd/ld/macho.h @@ -0,0 +1,94 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +typedef struct MachoHdr MachoHdr; +struct MachoHdr { + uint32 cpu; + uint32 subcpu; +}; + +typedef struct MachoSect MachoSect; +struct MachoSect { + char* name; + uint64 addr; + uint64 size; + uint32 off; + uint32 align; + uint32 reloc; + uint32 nreloc; + uint32 flag; + uint32 res1; + uint32 res2; +}; + +typedef struct MachoSeg MachoSeg; +struct MachoSeg { + char* name; + uint64 vsize; + uint64 vaddr; + uint64 fileoffset; + uint64 filesize; + uint32 prot1; + uint32 prot2; + uint32 nsect; + uint32 msect; + MachoSect *sect; + uint32 flag; +}; + +typedef struct MachoLoad MachoLoad; +struct MachoLoad { + uint32 type; + uint32 ndata; + uint32 *data; +}; + +typedef struct MachoDebug MachoDebug; +struct MachoDebug { + uint32 fileoffset; + uint32 filesize; +}; + +MachoHdr* getMachoHdr(); +MachoSeg* newMachoSeg(char*, int); +MachoSect* newMachoSect(MachoSeg*, char*); +MachoLoad* newMachoLoad(uint32, uint32); +MachoDebug* newMachoDebug(void); +int machowrite(void); +void machoinit(void); + +/* + * Total amount of space to reserve at the start of the file + * for Header, PHeaders, and SHeaders. + * May waste some. + */ +#define INITIAL_MACHO_HEADR 4*1024 + +enum { + MACHO_CPU_AMD64 = (1<<24)|7, + MACHO_CPU_386 = 7, + MACHO_SUBCPU_X86 = 3, + + MACHO32SYMSIZE = 12, + MACHO64SYMSIZE = 16, + + MACHO_X86_64_RELOC_UNSIGNED = 0, + MACHO_X86_64_RELOC_SIGNED = 1, + MACHO_X86_64_RELOC_BRANCH = 2, + MACHO_X86_64_RELOC_GOT_LOAD = 3, + MACHO_X86_64_RELOC_GOT = 4, + MACHO_X86_64_RELOC_SUBTRACTOR = 5, + MACHO_X86_64_RELOC_SIGNED_1 = 6, + MACHO_X86_64_RELOC_SIGNED_2 = 7, + MACHO_X86_64_RELOC_SIGNED_4 = 8, + + MACHO_GENERIC_RELOC_VANILLA = 0, + + MACHO_FAKE_GOTPCREL = 100, +}; + +void domacho(void); +vlong domacholink(void); +void asmbmacho(void); +void machoadddynlib(char*); diff --git a/src/cmd/ld/pe.c b/src/cmd/ld/pe.c new file mode 100644 index 000000000..334c9959f --- /dev/null +++ b/src/cmd/ld/pe.c @@ -0,0 +1,593 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// PE (Portable Executable) file writing +// http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx + +#include "l.h" +#include "../ld/lib.h" +#include "../ld/pe.h" +#include "../ld/dwarf.h" + +// DOS stub that prints out +// "This program cannot be run in DOS mode." +static char dosstub[] = +{ + 0x4d, 0x5a, 0x90, 0x00, 0x03, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x0e, 0x1f, 0xba, 0x0e, 0x00, 0xb4, 0x09, 0xcd, + 0x21, 0xb8, 0x01, 0x4c, 0xcd, 0x21, 0x54, 0x68, + 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, + 0x61, 0x6d, 0x20, 0x63, 0x61, 0x6e, 0x6e, 0x6f, + 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6e, + 0x20, 0x69, 0x6e, 0x20, 0x44, 0x4f, 0x53, 0x20, + 0x6d, 0x6f, 0x64, 0x65, 0x2e, 0x0d, 0x0d, 0x0a, + 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static Sym *rsrcsym; + +static char symnames[256]; +static int nextsymoff; + +int32 PESECTHEADR; +int32 PEFILEHEADR; + +static int pe64; +static int nsect; +static int nextsectoff; +static int nextfileoff; + +static IMAGE_FILE_HEADER fh; +static IMAGE_OPTIONAL_HEADER oh; +static PE64_IMAGE_OPTIONAL_HEADER oh64; +static IMAGE_SECTION_HEADER sh[16]; +static IMAGE_DATA_DIRECTORY* dd; + +#define set(n, v) (pe64 ? (oh64.n = v) : (oh.n = v)) +#define put(v) (pe64 ? vputl(v) : lputl(v)) + +typedef struct Imp Imp; +struct Imp { + Sym* s; + uvlong off; + Imp* next; +}; + +typedef struct Dll Dll; +struct Dll { + char* name; + uvlong nameoff; + uvlong thunkoff; + Imp* ms; + Dll* next; +}; + +static Dll* dr; + +static Sym *dexport[1024]; +static int nexport; + +static IMAGE_SECTION_HEADER* +addpesection(char *name, int sectsize, int filesize) +{ + IMAGE_SECTION_HEADER *h; + + if(nsect == 16) { + diag("too many sections"); + errorexit(); + } + h = &sh[nsect++]; + strncpy((char*)h->Name, name, sizeof(h->Name)); + h->VirtualSize = sectsize; + h->VirtualAddress = nextsectoff; + nextsectoff = rnd(nextsectoff+sectsize, PESECTALIGN); + h->PointerToRawData = nextfileoff; + if(filesize > 0) { + h->SizeOfRawData = rnd(filesize, PEFILEALIGN); + nextfileoff += h->SizeOfRawData; + } + return h; +} + +static void +chksectoff(IMAGE_SECTION_HEADER *h, vlong off) +{ + if(off != h->PointerToRawData) { + diag("%s.PointerToRawData = %#llux, want %#llux", (char *)h->Name, (vlong)h->PointerToRawData, off); + errorexit(); + } +} + +static void +chksectseg(IMAGE_SECTION_HEADER *h, Segment *s) +{ + if(s->vaddr-PEBASE != h->VirtualAddress) { + diag("%s.VirtualAddress = %#llux, want %#llux", (char *)h->Name, (vlong)h->VirtualAddress, (vlong)(s->vaddr-PEBASE)); + errorexit(); + } + if(s->fileoff != h->PointerToRawData) { + diag("%s.PointerToRawData = %#llux, want %#llux", (char *)h->Name, (vlong)h->PointerToRawData, (vlong)(s->fileoff)); + errorexit(); + } +} + +void +peinit(void) +{ + int32 l; + + switch(thechar) { + // 64-bit architectures + case '6': + pe64 = 1; + l = sizeof(oh64); + dd = oh64.DataDirectory; + break; + // 32-bit architectures + default: + l = sizeof(oh); + dd = oh.DataDirectory; + break; + } + + PEFILEHEADR = rnd(sizeof(dosstub)+sizeof(fh)+l+sizeof(sh), PEFILEALIGN); + PESECTHEADR = rnd(PEFILEHEADR, PESECTALIGN); + nextsectoff = PESECTHEADR; + nextfileoff = PEFILEHEADR; +} + +static void +pewrite(void) +{ + cseek(0); + cwrite(dosstub, sizeof dosstub); + strnput("PE", 4); + // TODO: This code should not assume that the + // memory representation is little-endian or + // that the structs are packed identically to + // their file representation. + cwrite(&fh, sizeof fh); + if(pe64) + cwrite(&oh64, sizeof oh64); + else + cwrite(&oh, sizeof oh); + cwrite(sh, nsect * sizeof sh[0]); +} + +static void +strput(char *s) +{ + int n; + + for(n=0; *s; n++) + cput(*s++); + cput('\0'); + n++; + // string must be padded to even size + if(n%2) + cput('\0'); +} + +static Dll* +initdynimport(void) +{ + Imp *m; + Dll *d; + Sym *s, *dynamic; + + dr = nil; + m = nil; + for(s = allsym; s != S; s = s->allsym) { + if(!s->reachable || !s->dynimpname || s->dynexport) + continue; + for(d = dr; d != nil; d = d->next) { + if(strcmp(d->name,s->dynimplib) == 0) { + m = mal(sizeof *m); + break; + } + } + if(d == nil) { + d = mal(sizeof *d); + d->name = s->dynimplib; + d->next = dr; + dr = d; + m = mal(sizeof *m); + } + m->s = s; + m->next = d->ms; + d->ms = m; + } + + dynamic = lookup(".windynamic", 0); + dynamic->reachable = 1; + dynamic->type = SWINDOWS; + for(d = dr; d != nil; d = d->next) { + for(m = d->ms; m != nil; m = m->next) { + m->s->type = SWINDOWS | SSUB; + m->s->sub = dynamic->sub; + dynamic->sub = m->s; + m->s->value = dynamic->size; + dynamic->size += PtrSize; + } + dynamic->size += PtrSize; + } + + return dr; +} + +static void +addimports(IMAGE_SECTION_HEADER *datsect) +{ + IMAGE_SECTION_HEADER *isect; + uvlong n, oftbase, ftbase; + vlong startoff, endoff; + Imp *m; + Dll *d; + Sym* dynamic; + + startoff = cpos(); + dynamic = lookup(".windynamic", 0); + + // skip import descriptor table (will write it later) + n = 0; + for(d = dr; d != nil; d = d->next) + n++; + cseek(startoff + sizeof(IMAGE_IMPORT_DESCRIPTOR) * (n + 1)); + + // write dll names + for(d = dr; d != nil; d = d->next) { + d->nameoff = cpos() - startoff; + strput(d->name); + } + + // write function names + for(d = dr; d != nil; d = d->next) { + for(m = d->ms; m != nil; m = m->next) { + m->off = nextsectoff + cpos() - startoff; + wputl(0); // hint + strput(m->s->dynimpname); + } + } + + // write OriginalFirstThunks + oftbase = cpos() - startoff; + n = cpos(); + for(d = dr; d != nil; d = d->next) { + d->thunkoff = cpos() - n; + for(m = d->ms; m != nil; m = m->next) + put(m->off); + put(0); + } + + // add pe section and pad it at the end + n = cpos() - startoff; + isect = addpesection(".idata", n, n); + isect->Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA| + IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE; + chksectoff(isect, startoff); + strnput("", isect->SizeOfRawData - n); + endoff = cpos(); + + // write FirstThunks (allocated in .data section) + ftbase = dynamic->value - datsect->VirtualAddress - PEBASE; + cseek(datsect->PointerToRawData + ftbase); + for(d = dr; d != nil; d = d->next) { + for(m = d->ms; m != nil; m = m->next) + put(m->off); + put(0); + } + + // finally write import descriptor table + cseek(startoff); + for(d = dr; d != nil; d = d->next) { + lputl(isect->VirtualAddress + oftbase + d->thunkoff); + lputl(0); + lputl(0); + lputl(isect->VirtualAddress + d->nameoff); + lputl(datsect->VirtualAddress + ftbase + d->thunkoff); + } + lputl(0); //end + lputl(0); + lputl(0); + lputl(0); + lputl(0); + + // update data directory + dd[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = isect->VirtualAddress; + dd[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = isect->VirtualSize; + dd[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress = dynamic->value - PEBASE; + dd[IMAGE_DIRECTORY_ENTRY_IAT].Size = dynamic->size; + + cseek(endoff); +} + +static int +scmp(const void *p1, const void *p2) +{ + Sym *s1, *s2; + + s1 = *(Sym**)p1; + s2 = *(Sym**)p2; + return strcmp(s1->dynimpname, s2->dynimpname); +} + +static void +initdynexport(void) +{ + Sym *s; + + nexport = 0; + for(s = allsym; s != S; s = s->allsym) { + if(!s->reachable || !s->dynimpname || !s->dynexport) + continue; + if(nexport+1 > sizeof(dexport)/sizeof(dexport[0])) { + diag("pe dynexport table is full"); + errorexit(); + } + + dexport[nexport] = s; + nexport++; + } + + qsort(dexport, nexport, sizeof dexport[0], scmp); +} + +void +addexports(void) +{ + IMAGE_SECTION_HEADER *sect; + IMAGE_EXPORT_DIRECTORY e; + int size, i, va, va_name, va_addr, va_na, v; + + size = sizeof e + 10*nexport + strlen(outfile) + 1; + for(i=0; i<nexport; i++) + size += strlen(dexport[i]->dynimpname) + 1; + + if (nexport == 0) + return; + + sect = addpesection(".edata", size, size); + sect->Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_MEM_READ; + chksectoff(sect, cpos()); + va = sect->VirtualAddress; + dd[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress = va; + dd[IMAGE_DIRECTORY_ENTRY_EXPORT].Size = sect->VirtualSize; + + va_name = va + sizeof e + nexport*4; + va_addr = va + sizeof e; + va_na = va + sizeof e + nexport*8; + + e.Characteristics = 0; + e.MajorVersion = 0; + e.MinorVersion = 0; + e.NumberOfFunctions = nexport; + e.NumberOfNames = nexport; + e.Name = va + sizeof e + nexport*10; // Program names. + e.Base = 1; + e.AddressOfFunctions = va_addr; + e.AddressOfNames = va_name; + e.AddressOfNameOrdinals = va_na; + // put IMAGE_EXPORT_DIRECTORY + for (i=0; i<sizeof(e); i++) + cput(((char*)&e)[i]); + // put EXPORT Address Table + for(i=0; i<nexport; i++) + lputl(dexport[i]->value - PEBASE); + // put EXPORT Name Pointer Table + v = e.Name + strlen(outfile)+1; + for(i=0; i<nexport; i++) { + lputl(v); + v += strlen(dexport[i]->dynimpname)+1; + } + // put EXPORT Ordinal Table + for(i=0; i<nexport; i++) + wputl(i); + // put Names + strnput(outfile, strlen(outfile)+1); + for(i=0; i<nexport; i++) + strnput(dexport[i]->dynimpname, strlen(dexport[i]->dynimpname)+1); + strnput("", sect->SizeOfRawData - size); +} + +void +dope(void) +{ + Sym *rel; + + /* relocation table */ + rel = lookup(".rel", 0); + rel->reachable = 1; + rel->type = SELFROSECT; + + initdynimport(); + initdynexport(); +} + +/* + * For more than 8 characters section names, name contains a slash (/) that is + * followed by an ASCII representation of a decimal number that is an offset into + * the string table. + * reference: pecoff_v8.docx Page 24. + * <http://www.microsoft.com/whdc/system/platform/firmware/PECOFFdwn.mspx> + */ +IMAGE_SECTION_HEADER* +newPEDWARFSection(char *name, vlong size) +{ + IMAGE_SECTION_HEADER *h; + char s[8]; + + if(size == 0) + return nil; + + if(nextsymoff+strlen(name)+1 > sizeof(symnames)) { + diag("pe string table is full"); + errorexit(); + } + + strcpy(&symnames[nextsymoff], name); + sprint(s, "/%d\0", nextsymoff+4); + nextsymoff += strlen(name); + symnames[nextsymoff] = 0; + nextsymoff ++; + h = addpesection(s, size, size); + h->Characteristics = IMAGE_SCN_MEM_READ| + IMAGE_SCN_MEM_DISCARDABLE; + + return h; +} + +static void +addsymtable(void) +{ + IMAGE_SECTION_HEADER *h; + int i, size; + + if(nextsymoff == 0) + return; + + size = nextsymoff + 4 + 18; + h = addpesection(".symtab", size, size); + h->Characteristics = IMAGE_SCN_MEM_READ| + IMAGE_SCN_MEM_DISCARDABLE; + chksectoff(h, cpos()); + fh.PointerToSymbolTable = cpos(); + fh.NumberOfSymbols = 1; + strnput("", 18); // one empty symbol + // put symbol string table + lputl(size); + for (i=0; i<nextsymoff; i++) + cput(symnames[i]); + strnput("", h->SizeOfRawData - size); +} + +void +setpersrc(Sym *sym) +{ + if(rsrcsym != nil) + diag("too many .rsrc sections"); + + rsrcsym = sym; +} + +void +addpersrc(void) +{ + IMAGE_SECTION_HEADER *h; + uchar *p; + uint32 val; + Reloc *r; + + if(rsrcsym == nil) + return; + + h = addpesection(".rsrc", rsrcsym->size, rsrcsym->size); + h->Characteristics = IMAGE_SCN_MEM_READ| + IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_INITIALIZED_DATA; + chksectoff(h, cpos()); + // relocation + for(r=rsrcsym->r; r<rsrcsym->r+rsrcsym->nr; r++) { + p = rsrcsym->p + r->off; + val = h->VirtualAddress + r->add; + // 32-bit little-endian + p[0] = val; + p[1] = val>>8; + p[2] = val>>16; + p[3] = val>>24; + } + cwrite(rsrcsym->p, rsrcsym->size); + strnput("", h->SizeOfRawData - rsrcsym->size); + + // update data directory + dd[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress = h->VirtualAddress; + dd[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = h->VirtualSize; +} + +void +asmbpe(void) +{ + IMAGE_SECTION_HEADER *t, *d; + + switch(thechar) { + default: + diag("unknown PE architecture"); + errorexit(); + case '6': + fh.Machine = IMAGE_FILE_MACHINE_AMD64; + break; + case '8': + fh.Machine = IMAGE_FILE_MACHINE_I386; + break; + } + + t = addpesection(".text", segtext.len, segtext.len); + t->Characteristics = IMAGE_SCN_CNT_CODE| + IMAGE_SCN_CNT_INITIALIZED_DATA| + IMAGE_SCN_MEM_EXECUTE|IMAGE_SCN_MEM_READ; + chksectseg(t, &segtext); + + d = addpesection(".data", segdata.len, segdata.filelen); + d->Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA| + IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE; + chksectseg(d, &segdata); + + if(!debug['s']) + dwarfaddpeheaders(); + + cseek(nextfileoff); + addimports(d); + addexports(); + addsymtable(); + addpersrc(); + + fh.NumberOfSections = nsect; + fh.TimeDateStamp = time(0); + fh.Characteristics = IMAGE_FILE_RELOCS_STRIPPED| + IMAGE_FILE_EXECUTABLE_IMAGE|IMAGE_FILE_DEBUG_STRIPPED; + if (pe64) { + fh.SizeOfOptionalHeader = sizeof(oh64); + fh.Characteristics |= IMAGE_FILE_LARGE_ADDRESS_AWARE; + set(Magic, 0x20b); // PE32+ + } else { + fh.SizeOfOptionalHeader = sizeof(oh); + fh.Characteristics |= IMAGE_FILE_32BIT_MACHINE; + set(Magic, 0x10b); // PE32 + oh.BaseOfData = d->VirtualAddress; + } + set(MajorLinkerVersion, 1); + set(MinorLinkerVersion, 0); + set(SizeOfCode, t->SizeOfRawData); + set(SizeOfInitializedData, d->SizeOfRawData); + set(SizeOfUninitializedData, 0); + set(AddressOfEntryPoint, entryvalue()-PEBASE); + set(BaseOfCode, t->VirtualAddress); + set(ImageBase, PEBASE); + set(SectionAlignment, PESECTALIGN); + set(FileAlignment, PEFILEALIGN); + set(MajorOperatingSystemVersion, 4); + set(MinorOperatingSystemVersion, 0); + set(MajorImageVersion, 1); + set(MinorImageVersion, 0); + set(MajorSubsystemVersion, 4); + set(MinorSubsystemVersion, 0); + set(SizeOfImage, nextsectoff); + set(SizeOfHeaders, PEFILEHEADR); + if(strcmp(headstring, "windowsgui") == 0) + set(Subsystem, IMAGE_SUBSYSTEM_WINDOWS_GUI); + else + set(Subsystem, IMAGE_SUBSYSTEM_WINDOWS_CUI); + set(SizeOfStackReserve, 0x0040000); + set(SizeOfStackCommit, 0x00001000); + set(SizeOfHeapReserve, 0x00100000); + set(SizeOfHeapCommit, 0x00001000); + set(NumberOfRvaAndSizes, 16); + + pewrite(); +} diff --git a/src/cmd/ld/pe.h b/src/cmd/ld/pe.h new file mode 100644 index 000000000..7aa938829 --- /dev/null +++ b/src/cmd/ld/pe.h @@ -0,0 +1,179 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +typedef struct { + uint16 Machine; + uint16 NumberOfSections; + uint32 TimeDateStamp; + uint32 PointerToSymbolTable; + uint32 NumberOfSymbols; + uint16 SizeOfOptionalHeader; + uint16 Characteristics; +} IMAGE_FILE_HEADER; + +typedef struct { + uint32 VirtualAddress; + uint32 Size; +} IMAGE_DATA_DIRECTORY; + +typedef struct { + uint16 Magic; + uint8 MajorLinkerVersion; + uint8 MinorLinkerVersion; + uint32 SizeOfCode; + uint32 SizeOfInitializedData; + uint32 SizeOfUninitializedData; + uint32 AddressOfEntryPoint; + uint32 BaseOfCode; + uint32 BaseOfData; + uint32 ImageBase; + uint32 SectionAlignment; + uint32 FileAlignment; + uint16 MajorOperatingSystemVersion; + uint16 MinorOperatingSystemVersion; + uint16 MajorImageVersion; + uint16 MinorImageVersion; + uint16 MajorSubsystemVersion; + uint16 MinorSubsystemVersion; + uint32 Win32VersionValue; + uint32 SizeOfImage; + uint32 SizeOfHeaders; + uint32 CheckSum; + uint16 Subsystem; + uint16 DllCharacteristics; + uint32 SizeOfStackReserve; + uint32 SizeOfStackCommit; + uint32 SizeOfHeapReserve; + uint32 SizeOfHeapCommit; + uint32 LoaderFlags; + uint32 NumberOfRvaAndSizes; + IMAGE_DATA_DIRECTORY DataDirectory[16]; +} IMAGE_OPTIONAL_HEADER; + +typedef struct { + uint8 Name[8]; + uint32 VirtualSize; + uint32 VirtualAddress; + uint32 SizeOfRawData; + uint32 PointerToRawData; + uint32 PointerToRelocations; + uint32 PointerToLineNumbers; + uint16 NumberOfRelocations; + uint16 NumberOfLineNumbers; + uint32 Characteristics; +} IMAGE_SECTION_HEADER; + +typedef struct { + uint32 OriginalFirstThunk; + uint32 TimeDateStamp; + uint32 ForwarderChain; + uint32 Name; + uint32 FirstThunk; +} IMAGE_IMPORT_DESCRIPTOR; + +typedef struct _IMAGE_EXPORT_DIRECTORY { + uint32 Characteristics; + uint32 TimeDateStamp; + uint16 MajorVersion; + uint16 MinorVersion; + uint32 Name; + uint32 Base; + uint32 NumberOfFunctions; + uint32 NumberOfNames; + uint32 AddressOfFunctions; + uint32 AddressOfNames; + uint32 AddressOfNameOrdinals; +} IMAGE_EXPORT_DIRECTORY; + +#define PEBASE 0x00400000 +// SectionAlignment must be greater than or equal to FileAlignment. +// The default is the page size for the architecture. +#define PESECTALIGN 0x1000 +// FileAlignment should be a power of 2 between 512 and 64 K, inclusive. +// The default is 512. If the SectionAlignment is less than +// the architecture's page size, then FileAlignment must match SectionAlignment. +#define PEFILEALIGN (2<<8) +extern int32 PESECTHEADR; +extern int32 PEFILEHEADR; + +enum { + IMAGE_FILE_MACHINE_I386 = 0x14c, + IMAGE_FILE_MACHINE_AMD64 = 0x8664, + + IMAGE_FILE_RELOCS_STRIPPED = 0x0001, + IMAGE_FILE_EXECUTABLE_IMAGE = 0x0002, + IMAGE_FILE_LARGE_ADDRESS_AWARE = 0x0020, + IMAGE_FILE_32BIT_MACHINE = 0x0100, + IMAGE_FILE_DEBUG_STRIPPED = 0x0200, + + IMAGE_SCN_CNT_CODE = 0x00000020, + IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040, + IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080, + IMAGE_SCN_MEM_EXECUTE = 0x20000000, + IMAGE_SCN_MEM_READ = 0x40000000, + IMAGE_SCN_MEM_WRITE = 0x80000000, + IMAGE_SCN_MEM_DISCARDABLE = 0x2000000, + + IMAGE_DIRECTORY_ENTRY_EXPORT = 0, + IMAGE_DIRECTORY_ENTRY_IMPORT = 1, + IMAGE_DIRECTORY_ENTRY_RESOURCE = 2, + IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3, + IMAGE_DIRECTORY_ENTRY_SECURITY = 4, + IMAGE_DIRECTORY_ENTRY_BASERELOC = 5, + IMAGE_DIRECTORY_ENTRY_DEBUG = 6, + IMAGE_DIRECTORY_ENTRY_COPYRIGHT = 7, + IMAGE_DIRECTORY_ENTRY_ARCHITECTURE = 7, + IMAGE_DIRECTORY_ENTRY_GLOBALPTR = 8, + IMAGE_DIRECTORY_ENTRY_TLS = 9, + IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10, + IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT = 11, + IMAGE_DIRECTORY_ENTRY_IAT = 12, + IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT = 13, + IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = 14, + + IMAGE_SUBSYSTEM_WINDOWS_GUI = 2, + IMAGE_SUBSYSTEM_WINDOWS_CUI = 3, +}; + +void peinit(void); +void asmbpe(void); +void dope(void); + +IMAGE_SECTION_HEADER* newPEDWARFSection(char *name, vlong size); + +// X64 +typedef struct { + uint16 Magic; + uint8 MajorLinkerVersion; + uint8 MinorLinkerVersion; + uint32 SizeOfCode; + uint32 SizeOfInitializedData; + uint32 SizeOfUninitializedData; + uint32 AddressOfEntryPoint; + uint32 BaseOfCode; + uint64 ImageBase; + uint32 SectionAlignment; + uint32 FileAlignment; + uint16 MajorOperatingSystemVersion; + uint16 MinorOperatingSystemVersion; + uint16 MajorImageVersion; + uint16 MinorImageVersion; + uint16 MajorSubsystemVersion; + uint16 MinorSubsystemVersion; + uint32 Win32VersionValue; + uint32 SizeOfImage; + uint32 SizeOfHeaders; + uint32 CheckSum; + uint16 Subsystem; + uint16 DllCharacteristics; + uint64 SizeOfStackReserve; + uint64 SizeOfStackCommit; + uint64 SizeOfHeapReserve; + uint64 SizeOfHeapCommit; + uint32 LoaderFlags; + uint32 NumberOfRvaAndSizes; + IMAGE_DATA_DIRECTORY DataDirectory[16]; +} PE64_IMAGE_OPTIONAL_HEADER; + +void setpersrc(Sym *sym); diff --git a/src/cmd/ld/symtab.c b/src/cmd/ld/symtab.c new file mode 100644 index 000000000..60e146b35 --- /dev/null +++ b/src/cmd/ld/symtab.c @@ -0,0 +1,378 @@ +// Inferno utils/6l/span.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. + +// Symbol table. + +#include "l.h" +#include "../ld/lib.h" +#include "../ld/elf.h" + +char *elfstrdat; +int elfstrsize; +int maxelfstr; +int elftextsh; + +int +putelfstr(char *s) +{ + int off, n; + + if(elfstrsize == 0 && s[0] != 0) { + // first entry must be empty string + putelfstr(""); + } + + n = strlen(s)+1; + if(elfstrsize+n > maxelfstr) { + maxelfstr = 2*(elfstrsize+n+(1<<20)); + elfstrdat = realloc(elfstrdat, maxelfstr); + } + off = elfstrsize; + elfstrsize += n; + memmove(elfstrdat+off, s, n); + return off; +} + +void +putelfsyment(int off, vlong addr, vlong size, int info, int shndx) +{ + switch(thechar) { + case '6': + LPUT(off); + cput(info); + cput(0); + WPUT(shndx); + VPUT(addr); + VPUT(size); + symsize += ELF64SYMSIZE; + break; + default: + LPUT(off); + LPUT(addr); + LPUT(size); + cput(info); + cput(0); + WPUT(shndx); + symsize += ELF32SYMSIZE; + break; + } +} + +void +putelfsym(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go) +{ + int bind, type, shndx, off; + + USED(go); + switch(t) { + default: + return; + case 'T': + type = STT_FUNC; + shndx = elftextsh + 0; + break; + case 'D': + type = STT_OBJECT; + if((x->type&~SSUB) == SRODATA) + shndx = elftextsh + 1; + else + shndx = elftextsh + 2; + break; + case 'B': + type = STT_OBJECT; + shndx = elftextsh + 3; + break; + } + bind = ver ? STB_LOCAL : STB_GLOBAL; + off = putelfstr(s); + putelfsyment(off, addr, size, (bind<<4)|(type&0xf), shndx); +} + +void +asmelfsym(void) +{ + // the first symbol entry is reserved + putelfsyment(0, 0, 0, (STB_LOCAL<<4)|STT_NOTYPE, 0); + genasmsym(putelfsym); +} + +void +putplan9sym(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go) +{ + int i; + + USED(go); + USED(ver); + USED(size); + USED(x); + switch(t) { + case 'T': + case 'L': + case 'D': + case 'B': + if(ver) + t += 'a' - 'A'; + case 'a': + case 'p': + case 'f': + case 'z': + case 'Z': + case 'm': + lputb(addr); + cput(t+0x80); /* 0x80 is variable length */ + + if(t == 'z' || t == 'Z') { + cput(s[0]); + for(i=1; s[i] != 0 || s[i+1] != 0; i += 2) { + cput(s[i]); + cput(s[i+1]); + } + cput(0); + cput(0); + i++; + } else { + /* skip the '<' in filenames */ + if(t == 'f') + s++; + for(i=0; s[i]; i++) + cput(s[i]); + cput(0); + } + symsize += 4 + 1 + i + 1; + break; + default: + return; + }; +} + +void +asmplan9sym(void) +{ + genasmsym(putplan9sym); +} + +static Sym *symt; + +static void +scput(int b) +{ + uchar *p; + + symgrow(symt, symt->size+1); + p = symt->p + symt->size; + *p = b; + symt->size++; +} + +static void +slputb(int32 v) +{ + uchar *p; + + symgrow(symt, symt->size+4); + p = symt->p + symt->size; + *p++ = v>>24; + *p++ = v>>16; + *p++ = v>>8; + *p = v; + symt->size += 4; +} + +void +wputl(ushort w) +{ + cput(w); + cput(w>>8); +} + +void +wputb(ushort w) +{ + cput(w>>8); + cput(w); +} + +void +lputb(int32 l) +{ + cput(l>>24); + cput(l>>16); + cput(l>>8); + cput(l); +} + +void +lputl(int32 l) +{ + cput(l); + cput(l>>8); + cput(l>>16); + cput(l>>24); +} + +void +vputb(uint64 v) +{ + lputb(v>>32); + lputb(v); +} + +void +vputl(uint64 v) +{ + lputl(v); + lputl(v >> 32); +} + +void +putsymb(Sym *s, char *name, int t, vlong v, vlong size, int ver, Sym *typ) +{ + int i, f, l; + Reloc *rel; + + USED(size); + if(t == 'f') + name++; + l = 4; +// if(!debug['8']) +// l = 8; + if(s != nil) { + rel = addrel(symt); + rel->siz = l + Rbig; + rel->sym = s; + rel->type = D_ADDR; + rel->off = symt->size; + v = 0; + } + if(l == 8) + slputb(v>>32); + slputb(v); + if(ver) + t += 'a' - 'A'; + scput(t+0x80); /* 0x80 is variable length */ + + if(t == 'Z' || t == 'z') { + scput(name[0]); + for(i=1; name[i] != 0 || name[i+1] != 0; i += 2) { + scput(name[i]); + scput(name[i+1]); + } + scput(0); + scput(0); + } + else { + for(i=0; name[i]; i++) + scput(name[i]); + scput(0); + } + if(typ) { + if(!typ->reachable) + diag("unreachable type %s", typ->name); + rel = addrel(symt); + rel->siz = l; + rel->sym = typ; + rel->type = D_ADDR; + rel->off = symt->size; + } + if(l == 8) + slputb(0); + slputb(0); + + if(debug['n']) { + if(t == 'z' || t == 'Z') { + Bprint(&bso, "%c %.8llux ", t, v); + for(i=1; name[i] != 0 || name[i+1] != 0; i+=2) { + f = ((name[i]&0xff) << 8) | (name[i+1]&0xff); + Bprint(&bso, "/%x", f); + } + Bprint(&bso, "\n"); + return; + } + if(ver) + Bprint(&bso, "%c %.8llux %s<%d> %s\n", t, v, s->name, ver, typ ? typ->name : ""); + else + Bprint(&bso, "%c %.8llux %s %s\n", t, v, s->name, typ ? typ->name : ""); + } +} + +void +symtab(void) +{ + Sym *s; + + // Define these so that they'll get put into the symbol table. + // data.c:/^address will provide the actual values. + xdefine("text", STEXT, 0); + xdefine("etext", STEXT, 0); + xdefine("rodata", SRODATA, 0); + xdefine("erodata", SRODATA, 0); + xdefine("data", SBSS, 0); + xdefine("edata", SBSS, 0); + xdefine("end", SBSS, 0); + xdefine("epclntab", SRODATA, 0); + xdefine("esymtab", SRODATA, 0); + + // pseudo-symbols to mark locations of type, string, and go string data. + s = lookup("type.*", 0); + s->type = STYPE; + s->size = 0; + s->reachable = 1; + + s = lookup("go.string.*", 0); + s->type = SGOSTRING; + s->size = 0; + s->reachable = 1; + + symt = lookup("symtab", 0); + symt->type = SSYMTAB; + symt->size = 0; + symt->reachable = 1; + + // assign specific types so that they sort together. + // within a type they sort by size, so the .* symbols + // just defined above will be first. + // hide the specific symbols. + for(s = allsym; s != S; s = s->allsym) { + if(!s->reachable || s->special || s->type != SRODATA) + continue; + if(strncmp(s->name, "type.", 5) == 0) { + s->type = STYPE; + s->hide = 1; + } + if(strncmp(s->name, "go.string.", 10) == 0) { + s->type = SGOSTRING; + s->hide = 1; + } + } + + if(debug['s']) + return; + genasmsym(putsymb); +} |