diff options
author | Ondřej Surý <ondrej@sury.org> | 2011-01-17 12:40:45 +0100 |
---|---|---|
committer | Ondřej Surý <ondrej@sury.org> | 2011-01-17 12:40:45 +0100 |
commit | 3e45412327a2654a77944249962b3652e6142299 (patch) | |
tree | bc3bf69452afa055423cbe0c5cfa8ca357df6ccf /src/cmd/ld | |
parent | c533680039762cacbc37db8dc7eed074c3e497be (diff) | |
download | golang-upstream/2011.01.12.tar.gz |
Imported Upstream version 2011.01.12upstream/2011.01.12
Diffstat (limited to 'src/cmd/ld')
-rw-r--r-- | src/cmd/ld/data.c | 914 | ||||
-rw-r--r-- | src/cmd/ld/dwarf.c | 2547 | ||||
-rw-r--r-- | src/cmd/ld/dwarf.h | 29 | ||||
-rw-r--r-- | src/cmd/ld/dwarf_defs.h | 503 | ||||
-rw-r--r-- | src/cmd/ld/elf.c | 124 | ||||
-rw-r--r-- | src/cmd/ld/elf.h | 8 | ||||
-rw-r--r-- | src/cmd/ld/go.c | 182 | ||||
-rw-r--r-- | src/cmd/ld/ldelf.c | 816 | ||||
-rw-r--r-- | src/cmd/ld/ldmacho.c | 794 | ||||
-rw-r--r-- | src/cmd/ld/lib.c | 546 | ||||
-rw-r--r-- | src/cmd/ld/lib.h | 97 | ||||
-rw-r--r-- | src/cmd/ld/macho.c | 394 | ||||
-rw-r--r-- | src/cmd/ld/macho.h | 19 | ||||
-rw-r--r-- | src/cmd/ld/pe.c | 307 | ||||
-rw-r--r-- | src/cmd/ld/pe.h | 14 | ||||
-rw-r--r-- | src/cmd/ld/symtab.c | 259 |
16 files changed, 6916 insertions, 637 deletions
diff --git a/src/cmd/ld/data.c b/src/cmd/ld/data.c new file mode 100644 index 000000000..210f10ab5 --- /dev/null +++ b/src/cmd/ld/data.c @@ -0,0 +1,914 @@ +// 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); + +/* + * 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; + + 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; + + 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) +{ + int32 off, siz, i, fl; + uchar *cast; + vlong o; + Reloc *r; + + off = p->from.offset; + siz = p->datasize; + 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, seek(cout, 0, 1)); + + 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, 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, "%.6ux\t", p->pc); + q = sym->p + p->pc - sym->value; + n = epc - p->pc; + Bprint(&bso, "%-20.*I | %P\n", 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, seek(cout, 0, 1)); + + 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, 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)", addr); + Bprint(&bso, "%-20s %.8ux|\n", "", 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; +} + +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 h, 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(h=0; h<NHASH; h++) { + for(s=hash[h]; s!=S; s=s->hash){ + 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) // TODO: necessary? + 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", 06); + sect->vaddr = 0; + datsize = 0; + s = datap; + for(; s != nil && s->type < SDATA; s = s->next) { + s->type = SRODATA; + t = rnd(s->size, 4); + s->size = t; + s->value = datsize; + datsize += t; + } + sect->len = datsize - sect->vaddr; + + /* data */ + datsize = 0; + sect = addsection(&segdata, ".data", 06); + sect->vaddr = 0; + 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 & 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 & 1) + ; + else if(t & 2) + datsize = rnd(datsize, 2); + else if(t & 4) + datsize = rnd(datsize, 4); + else + datsize = rnd(datsize, 8); + s->size = t; + 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, *bss; + 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 += s->len; + segtext.len = va - INITTEXT; + va = rnd(va, INITRND); + } + segtext.filelen = segtext.len; + + segdata.rwx = 06; + segdata.vaddr = va; + segdata.fileoff = va - segtext.vaddr + segtext.fileoff; + if(thechar == '8' && HEADTYPE == 10) // Windows PE + segdata.fileoff = segtext.fileoff + rnd(segtext.len, PEFILEALIGN); + if(thechar == '8' && HEADTYPE == 2) { // Plan 9 + segdata.vaddr = va = rnd(va, 4096); + segdata.fileoff = segtext.fileoff + segtext.filelen; + } + for(s=segdata.sect; s != nil; s=s->next) { + s->vaddr = va; + va += s->len; + segdata.len = va - segdata.vaddr; + } + segdata.filelen = segdata.sect->len; // assume .data is first + + text = segtext.sect; + rodata = segtext.sect->next; + data = segdata.sect; + bss = segdata.sect->next; + + for(sym = datap; sym != nil; sym = sym->next) { + cursym = sym; + if(sym->type < SDATA) + sym->value += rodata->vaddr; + else + sym->value += data->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("data", SBSS, data->vaddr); + xdefine("edata", SBSS, data->vaddr + data->len); + xdefine("end", SBSS, segdata.vaddr + segdata.len); + + sym = lookup("pclntab", 0); + xdefine("epclntab", SRODATA, sym->value + sym->size); + sym = lookup("symtab", 0); + xdefine("esymtab", SRODATA, sym->value + sym->size); +} diff --git a/src/cmd/ld/dwarf.c b/src/cmd/ld/dwarf.c new file mode 100644 index 000000000..506c6e5db --- /dev/null +++ b/src/cmd/ld/dwarf.c @@ -0,0 +1,2547 @@ +// 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 +// - (upstream) type info for C parts of runtime +// - 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" + +/* + * 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; +} + +static void +delattr(DWDie *die, uint8 attr) +{ + DWAttr **a; + + a = &die->attr; + while (*a != nil) + if ((*a)->atr == attr) + *a = (*a)->link; + else + a = &((*a)->link); +} + +// 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); + 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("null dwarf 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("Unsupported atribute 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, + KindFloat, + KindFloat32, + KindFloat64, + KindComplex, + KindComplex64, + KindComplex128, + KindArray, + KindChan, + KindFunc, + KindInterface, + KindMap, + KindPtr, + KindSlice, + KindString, + KindStruct, + KindUnsafePointer, + + KindNoPointers = 1<<7, +}; + +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 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("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(s, 5*PtrSize + 8)->sym; // 0x1c / 0x30 +} + +static vlong +decodetype_arraylen(Sym *s) +{ + return decode_inuxi(s->p + 6*PtrSize + 8, PtrSize); +} + +// Type.PtrType.elem +static Sym* +decodetype_ptrelem(Sym *s) +{ + return decode_reloc(s, 5*PtrSize + 8)->sym; // 0x1c / 0x30 +} + +// Type.MapType.key, elem +static Sym* +decodetype_mapkey(Sym *s) +{ + return decode_reloc(s, 5*PtrSize + 8)->sym; // 0x1c / 0x30 +} +static Sym* +decodetype_mapvalue(Sym *s) +{ + return decode_reloc(s, 6*PtrSize + 8)->sym; // 0x20 / 0x38 +} + +// Type.ChanType.elem +static Sym* +decodetype_chanelem(Sym *s) +{ + return decode_reloc(s, 5*PtrSize + 8)->sym; // 0x1c / 0x30 +} + +// Type.FuncType.dotdotdot +static int +decodetype_funcdotdotdot(Sym *s) +{ + return s->p[5*PtrSize + 8]; +} + +// Type.FuncType.in.len +static int +decodetype_funcincount(Sym *s) +{ + return decode_inuxi(s->p + 7*PtrSize + 8, 4); +} + +static int +decodetype_funcoutcount(Sym *s) +{ + return decode_inuxi(s->p + 8*PtrSize + 16, 4); +} + +static Sym* +decodetype_funcintype(Sym *s, int i) +{ + Reloc *r; + + r = decode_reloc(s, 6*PtrSize + 8); + return decode_reloc(r->sym, r->add + i * PtrSize)->sym; +} + +static Sym* +decodetype_funcouttype(Sym *s, int i) +{ + Reloc *r; + + r = decode_reloc(s, 7*PtrSize + 16); + return decode_reloc(r->sym, r->add + i * PtrSize)->sym; +} + +// Type.StructType.fields.Slice::len +static int +decodetype_structfieldcount(Sym *s) +{ + return decode_inuxi(s->p + 6*PtrSize + 8, 4); // 0x20 / 0x38 +} + +// Type.StructType.fields[]-> name, typ and offset. sizeof(structField) = 5*PtrSize +static char* +decodetype_structfieldname(Sym *s, int i) +{ + Reloc* r; + + r = decode_reloc(s, 6*PtrSize + 0x10 + i*5*PtrSize); // go.string."foo" 0x28 / 0x40 + if (r == nil) // embedded structs have a nil name. + return nil; + r = decode_reloc(r->sym, 0); // string."foo" + if (r == nil) // shouldn't happen. + return nil; + return (char*)r->sym->p; // the c-string +} + +static Sym* +decodetype_structfieldtype(Sym *s, int i) +{ + return decode_reloc(s, 8*PtrSize + 0x10 + i*5*PtrSize)->sym; // 0x30 / 0x50 +} + +static vlong +decodetype_structfieldoffs(Sym *s, int i) +{ + return decode_inuxi(s->p + 10*PtrSize + 0x10 + i*5*PtrSize, 4); // 0x38 / 0x60 +} + +// InterfaceTYpe.methods.len +static vlong +decodetype_ifacemethodcount(Sym *s) +{ + return decode_inuxi(s->p + 6*PtrSize + 8, 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 + +// 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("Type name doesn't start with \".type\": %s", gotype->name); + return find_or_diag(&dwtypes, "<unspecified>"); + } + name = gotype->name + 5; // Altenatively decode from Type.string + + die = find(&dwtypes, name); + if (die != nil) + return die; + + if (0 && debug['v'] > 2) { + print("new type: %s @0x%08x [%d]", gotype->name, gotype->value, gotype->size); + for (i = 0; i < gotype->size; i++) { + if (!(i%8)) print("\n\t%04x ", i); + print("%02x ", gotype->p[i]); + } + print("\n"); + for (i = 0; i < gotype->nr; i++) { + print("\t0x%02x[%x] %d %s[%llx]\n", + gotype->r[i].off, + gotype->r[i].siz, + gotype->r[i].type, + gotype->r[i].sym->name, + (vlong)gotype->r[i].add); + } + } + + 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 KindFloat: + 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 KindComplex: + 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))); + } + die = defptrto(die); + 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("type.runtime.eface", 0); + else + s = lookup("type.runtime.iface", 0); + 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("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 it's 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("type.runtime.string_", 0)); + 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("type.runtime.slice",0)); + 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, *keytype, *valtype, *fld; + int hashsize, keysize, valsize, datsize, valsize_in_hash, datavo; + DWAttr *a; + + hash = defgotype(lookup("type.runtime.hash",0)); + hash_subtable = defgotype(lookup("type.runtime.hash_subtable",0)); + hash_entry = defgotype(lookup("type.runtime.hash_entry",0)); + + if (hash == nil || hash_subtable == nil || hash_entry == nil) + return; + + dwh = (DWDie*)getattr(find_or_diag(hash_entry, "hash"), DW_AT_type)->data; + if (dwh == nil) + return; + + hashsize = getattr(dwh, 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)); + copychildren(dwhe, hash_entry); + substitutetype(dwhe, "key", keytype); + if (valsize > MaxValsize) + valtype = defptrto(valtype); + substitutetype(dwhe, "val", valtype); + fld = find_or_diag(dwhe, "val"); + delattr(fld, DW_AT_data_member_location); + newmemberoffsetattr(fld, hashsize + datavo); + newattr(dwhe, DW_AT_byte_size, DW_CLS_CONSTANT, hashsize + datsize, NULL); + + // 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, NULL); + + // 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, NULL); + + newrefattr(die, DW_AT_type, defptrto(dwh)); + } +} + +static void +synthesizechantypes(DWDie *die) +{ + DWDie *sudog, *waitq, *link, *hchan, + *dws, *dww, *dwl, *dwh, *elemtype; + DWAttr *a; + int elemsize, linksize, sudogsize; + + sudog = defgotype(lookup("type.runtime.sudoG",0)); + waitq = defgotype(lookup("type.runtime.waitQ",0)); + link = defgotype(lookup("type.runtime.link",0)); + hchan = defgotype(lookup("type.runtime.hChan",0)); + if (sudog == nil || waitq == nil || link == nil || hchan == nil) + return; + + sudogsize = getattr(sudog, DW_AT_byte_size)->value; + linksize = getattr(link, 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, NULL)); + copychildren(dws, sudog); + substitutetype(dws, "elem", elemtype); + newattr(dws, DW_AT_byte_size, DW_CLS_CONSTANT, + sudogsize + (elemsize > 8 ? elemsize - 8 : 0), NULL); + + // waitq<T> + dww = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, + mkinternaltypename("waitq", getattr(elemtype, DW_AT_name)->data, NULL)); + 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, NULL); + + // link<T> + dwl = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, + mkinternaltypename("link", getattr(elemtype, DW_AT_name)->data, NULL)); + copychildren(dwl, link); + substitutetype(dwl, "link", defptrto(dwl)); + substitutetype(dwl, "elem", elemtype); + newattr(dwl, DW_AT_byte_size, DW_CLS_CONSTANT, + linksize + (elemsize > 8 ? elemsize - 8 : 0), NULL); + + // hchan<T> + dwh = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, + mkinternaltypename("hchan", getattr(elemtype, DW_AT_name)->data, NULL)); + copychildren(dwh, hchan); + substitutetype(dwh, "senddataq", defptrto(dwl)); + substitutetype(dwh, "recvdataq", defptrto(dwl)); + substitutetype(dwh, "recvq", dww); + substitutetype(dwh, "sendq", dww); + substitutetype(dwh, "free", dws); + newattr(dwh, DW_AT_byte_size, DW_CLS_CONSTANT, + getattr(hchan, DW_AT_byte_size)->value, NULL); + + 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; + + if (strncmp(s, "go.string.", 10) == 0) + return; + if (strncmp(s, "string.", 7) == 0) + return; + if (strncmp(s, "type._.", 7) == 0) + return; + + if (strncmp(s, "type.", 5) == 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("corrupt z entry"); + return 0; + } + f = ftab[o]; + if (f == nil) { + diag("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() +{ + 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("corrupt z stack"); + errorexit(); + } + if (includetop >= nelem(includestack)) { + diag("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("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 it's 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) +{ + 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); + cflush(); + + here = cpos(); + seek(cout, unitstart, 0); + LPUT(here - unitstart - sizeof(int32)); + cflush(); + seek(cout, here, 0); + } +} + +static void +writelines(void) +{ + Prog *q; + Sym *s; + Auto *a; + vlong unitstart, 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; + epc = pc = 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); + 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 later. + WPUT(3); // dwarf version (appendix F) + LPUT(11); // header_length (*), starting here. + + 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) + cput(0); // file_names (empty) (emitted by DW_LNE's below) + // header_length ends here. + + for (i=1; i < histfilesize; i++) { + cput(0); // start extended opcode + uleb128put(1 + strlen(histfile[i]) + 4); + cput(DW_LNE_define_file); + strnput(histfile[i], strlen(histfile[i]) + 4); + // 4 zeros: the string termination + 3 fields. + } + + epc = pc = s->text->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("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("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, NULL); + 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); + 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("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; + cflush(); + + // Emit the FDE header for real, Section 6.4.1. + seek(cout, fdeo, 0); + LPUT(fdesize); + LPUT(0); + addrput(p->pc); + addrput(s->size); + + cflush(); + seek(cout, fdeo + 4 + fdesize, 0); + } + + 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); + + cflush(); + here = cpos(); + seek(cout, unitstart, 0); + LPUT(here - unitstart - 4); // exclude the length field. + cflush(); + seek(cout, here, 0); + } + +} + +/* + * 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); + + cflush(); + here = cpos(); + seek(cout, sectionstart, 0); + LPUT(here - sectionstart - 4); // exclude the length field. + cflush(); + seek(cout, here, 0); + + } + + 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; +} + +/* + * 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; + + // 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("type.runtime.commonType",0)); + defgotype(lookup("type.runtime.InterfaceType",0)); + defgotype(lookup("type.runtime.itab",0)); + + genasmsym(defdwsymb); + + writeabbrev(); + writelines(); + writeframes(); + + 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(); + gdbscripto = arangeso = pubtypeso = pubnameso = infoe = cpos(); + + if (fwdcount > 0) { + if (debug['v']) + Bprint(&bso, "%5.2f dwarf pass 2.\n", cputime()); + seek(cout, infoo, 0); + writeinfo(); + if (fwdcount > 0) { + diag("unresolved references after first dwarf info pass"); + errorexit(); + } + if (infoe != cpos()) { + diag("inconsistent second dwarf info pass"); + errorexit(); + } + } + infosize = infoe - infoo; + + pubnameso = writepub(ispubname); + pubtypeso = writepub(ispubtype); + arangeso = writearanges(); + gdbscripto = writegdbscript(); + + pubnamessize = pubtypeso - pubnameso; + pubtypessize = arangeso - pubtypeso; + arangessize = gdbscripto - arangeso; + gdbscriptsize = cpos() - gdbscripto; +} + +/* + * Elf. + */ +enum +{ + ElfStrDebugAbbrev, + ElfStrDebugAranges, + ElfStrDebugFrame, + ElfStrDebugInfo, + ElfStrDebugLine, + ElfStrDebugLoc, + ElfStrDebugMacinfo, + ElfStrDebugPubNames, + ElfStrDebugPubTypes, + ElfStrDebugRanges, + ElfStrDebugStr, + ElfStrGDBScripts, + NElfStrDbg +}; + +vlong elfstrdbg[NElfStrDbg]; + +void +dwarfaddshstrings(Sym *shstrtab) +{ + 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; + + 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; + + // 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; + } +} diff --git a/src/cmd/ld/dwarf.h b/src/cmd/ld/dwarf.h new file mode 100644 index 000000000..7881213c2 --- /dev/null +++ b/src/cmd/ld/dwarf.h @@ -0,0 +1,29 @@ +// 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); 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 index c5d58576d..d5b0b0311 100644 --- a/src/cmd/ld/elf.c +++ b/src/cmd/ld/elf.c @@ -21,6 +21,16 @@ 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. @@ -122,6 +132,18 @@ elfwriteshdrs(void) 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) { @@ -142,8 +164,7 @@ newElfPhdr(void) { ElfPhdr *e; - e = malloc(sizeof *e); - memset(e, 0, sizeof *e); + e = mal(sizeof *e); if (hdr.phnum >= NSECT) diag("too many phdrs"); else @@ -167,8 +188,7 @@ newElfShdr(vlong name) { ElfShdr *e; - e = malloc(sizeof *e); - memset(e, 0, sizeof *e); + e = mal(sizeof *e); e->name = name; if (hdr.shnum >= NSECT) { diag("too many shdrs"); @@ -294,7 +314,7 @@ elfwriteinterp(void) n = strlen(interp)+1; seek(cout, ELFRESERVE-n, 0); - write(cout, interp, n); + ewrite(cout, interp, n); return n; } @@ -310,17 +330,25 @@ elfinterp(ElfShdr *sh, uint64 startva, char *p) sh->size = n; } +extern int nelfsym; + void -elfdynhash(int nsym) +elfdynhash(void) { Sym *s, *sy; int i, h, nbucket, b; uchar *pc; uint32 hc, g; uint32 *chain, *buckets; + int nsym; + char *name; + + if(!iself) + return; + nsym = nelfsym; s = lookup(".hash", 0); - s->type = SELFDATA; // TODO: rodata + s->type = SELFDATA; s->reachable = 1; i = nsym; @@ -331,17 +359,24 @@ elfdynhash(int nsym) } chain = malloc(nsym * sizeof(uint32)); - memset(chain, 0, nsym * sizeof(uint32)); buckets = malloc(nbucket * sizeof(uint32)); + if(chain == nil || buckets == nil) { + cursym = nil; + diag("out of memory"); + errorexit(); + } + memset(chain, 0, nsym * sizeof(uint32)); memset(buckets, 0, nbucket * sizeof(uint32)); - i = 1; for(h = 0; h<NHASH; h++) { - for(sy=hash[h]; sy!=S; sy=sy->link) { - if (!sy->reachable || (sy->type != STEXT && sy->type != SDATA && sy->type != SBSS) || sy->dynimpname == nil) + for(sy=hash[h]; sy!=S; sy=sy->hash) { + if (sy->dynid <= 0) continue; hc = 0; - for(pc = (uchar*)sy->dynimpname; *pc; pc++) { + name = sy->dynimpname; + if(name == nil) + name = sy->name; + for(pc = (uchar*)name; *pc; pc++) { hc = (hc<<4) + *pc; g = hc & 0xf0000000; hc ^= g >> 24; @@ -349,9 +384,8 @@ elfdynhash(int nsym) } b = hc % nbucket; - chain[i] = buckets[b]; - buckets[b] = i; - i++; + chain[sy->dynid] = buckets[b]; + buckets[b] = sy->dynid; } } @@ -364,4 +398,64 @@ elfdynhash(int nsym) free(chain); free(buckets); + + elfwritedynent(lookup(".dynamic", 0), 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: + 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 index 9b5fdb17e..df15cb115 100644 --- a/src/cmd/ld/elf.h +++ b/src/cmd/ld/elf.h @@ -964,7 +964,10 @@ extern int numelfshdr; extern int iself; int elfwriteinterp(void); void elfinterp(ElfShdr*, uint64, char*); -void elfdynhash(int); +void elfdynhash(void); +ElfPhdr* elfphload(Segment*); +ElfShdr* elfshbits(Section*); +void elfsetstring(char*, int); /* * Total amount of space to reserve at the start of the file @@ -972,5 +975,4 @@ void elfdynhash(int); * May waste some. * On FreeBSD, cannot be larger than a page. */ -#define ELFRESERVE 2048 - +#define ELFRESERVE 3072 diff --git a/src/cmd/ld/go.c b/src/cmd/ld/go.c index 015f34db2..8966b2a1f 100644 --- a/src/cmd/ld/go.c +++ b/src/cmd/ld/go.c @@ -66,12 +66,11 @@ ilookup(char *name) } static void loadpkgdata(char*, char*, char*, int); -static void loaddynimport(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 int ndynexp; static Sym **dynexp; void @@ -194,7 +193,7 @@ ldpkg(Biobuf *f, char *pkg, int64 len, char *filename, int whence) errorexit(); return; } - loaddynimport(filename, p0 + 1, p1 - (p0+1)); + loaddynimport(filename, pkg, p0 + 1, p1 - (p0+1)); } // look for dynexp section @@ -266,6 +265,10 @@ expandpkg(char *t0, char *pkg) // 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); @@ -282,7 +285,7 @@ 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; + int n, inquote; // skip white space p = *pp; @@ -319,8 +322,19 @@ loop: // name: a.b followed by space name = p; - while(p < ep && *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'; @@ -397,7 +411,7 @@ parsemethod(char **pp, char *ep, char **methp) } static void -loaddynimport(char *file, char *p, int n) +loaddynimport(char *file, char *pkg, char *p, int n) { char *pend, *next, *name, *def, *p0, *lib; Sym *s; @@ -431,10 +445,21 @@ loaddynimport(char *file, char *p, int n) // successful parse: now can edit the line *strchr(name, ' ') = 0; *strchr(def, ' ') = 0; + + if(strcmp(name, "_") == 0 && strcmp(def, "_") == 0) { + // allow #pragma dynimport _ _ "foo.so" + // to force a link of foo.so. + adddynlib(lib); + continue; + } + name = expandpkg(name, pkg); s = lookup(name, 0); - s->dynimplib = lib; - s->dynimpname = def; + if(s->type == 0 || s->type == SXREF) { + s->dynimplib = lib; + s->dynimpname = def; + s->type = SDYNIMPORT; + } } return; @@ -499,38 +524,19 @@ err: static int markdepth; static void -markdata(Prog *p, Sym *s) -{ - markdepth++; - if(p != P && debug['v'] > 1) - Bprint(&bso, "%d markdata %s\n", markdepth, s->name); - for(; p != P; p=p->dlink) - if(p->to.sym) - mark(p->to.sym); - markdepth--; -} - -static void -marktext(Prog *p) +marktext(Sym *s) { Auto *a; + Prog *p; - if(p == P) + if(s == S) return; - if(p->as != ATEXT) { - diag("marktext: %P", p); - return; - } - for(a=p->to.autom; a; a=a->link) - mark(a->gotype); markdepth++; if(debug['v'] > 1) - Bprint(&bso, "%d marktext %s\n", markdepth, p->from.sym->name); - for(a=p->to.autom; a; a=a->link) + Bprint(&bso, "%d marktext %s\n", markdepth, s->name); + for(a=s->autom; a; a=a->link) mark(a->gotype); - for(p=p->link; p != P; p=p->link) { - if(p->as == ATEXT || p->as == ADATA || p->as == AGLOBL) - break; + for(p=s->text; p != P; p=p->link) { if(p->from.sym) mark(p->from.sym); if(p->to.sym) @@ -542,45 +548,21 @@ marktext(Prog *p) void mark(Sym *s) { + int i; + if(s == S || s->reachable) return; s->reachable = 1; if(s->text) - marktext(s->text); - if(s->data) - markdata(s->data, s); + marktext(s); + for(i=0; i<s->nr; i++) + mark(s->r[i].sym); if(s->gotype) mark(s->gotype); -} - -static void -sweeplist(Prog **first, Prog **last) -{ - int reachable; - Prog *p, *q; - - reachable = 1; - q = P; - for(p=*first; p != P; p=p->link) { - switch(p->as) { - case ATEXT: - case ADATA: - case AGLOBL: - reachable = p->from.sym->reachable; - } - if(reachable) { - if(q == P) - *first = p; - else - q->link = p; - q = p; - } - } - if(q == P) - *first = P; - else - q->link = P; - *last = q; + if(s->sub) + mark(s->sub); + if(s->outer) + mark(s->outer); } static char* @@ -602,10 +584,43 @@ morename[] = "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()); @@ -616,7 +631,38 @@ deadcode(void) 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; +} - // remove dead data - sweeplist(&datap, &edatap); +void +addexport(void) +{ + int i; + + for(i=0; i<ndynexp; i++) + adddynsym(dynexp[i]); } diff --git a/src/cmd/ld/ldelf.c b/src/cmd/ld/ldelf.c new file mode 100644 index 000000000..44bbe68ee --- /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, *dp; + ElfHdrBytes *hdr; + ElfObj *obj; + ElfSect *sect, *rsect; + ElfSym sym; + Endian *e; + Reloc *r, *rp; + Sym *s; + + 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"); + return; + } + + switch(thechar) { + default: + diag("%s: elf %s unimplemented", 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(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; + dp = sect->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..7e38db0e4 --- /dev/null +++ b/src/cmd/ld/ldmacho.c @@ -0,0 +1,794 @@ +/* +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; + 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; + + 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", 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 { + 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; + + 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/lib.c b/src/cmd/ld/lib.c index 1af9f7a41..ae77247c3 100644 --- a/src/cmd/ld/lib.c +++ b/src/cmd/ld/lib.c @@ -1,5 +1,6 @@ -// Derived from Inferno utils/6l/obj.c +// 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) @@ -118,7 +119,7 @@ addlib(char *src, char *obj) } for(; i<histfrogp; i++) { - snprint(comp, sizeof comp, histfrog[i]->name+1); + snprint(comp, sizeof comp, "%s", histfrog[i]->name+1); for(;;) { p = strstr(comp, "$O"); if(p == 0) @@ -146,6 +147,22 @@ addlib(char *src, char *obj) 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. @@ -159,8 +176,8 @@ addlib(char *src, char *obj) cleanname(pname); /* runtime.a -> runtime */ - if(strlen(name) > 2 && name[strlen(name)-2] == '.') - name[strlen(name)-2] = '\0'; + if(p != nil) + *p = '\0'; if(debug['v']) Bprint(&bso, "%5.2f addlib: %s %s pulls in %s\n", cputime(), obj, src, pname); @@ -186,9 +203,9 @@ addlibpath(char *srcref, char *objref, char *file, char *pkg) if(strcmp(file, library[i].file) == 0) return; - if(debug['v']) + if(debug['v'] > 1) Bprint(&bso, "%5.2f addlibpath: srcref: %s objref: %s file: %s pkg: %s\n", - cputime(), srcref, objref, file, pkg); + cputime(), srcref, objref, file, pkg); if(libraryp == nlibrary){ nlibrary = 50 + 2*libraryp; @@ -219,8 +236,6 @@ loadlib(void) { char pname[1024]; int i, found; - int32 h; - Sym *s; found = 0; for(i=0; i<nlibdir; i++) { @@ -233,47 +248,51 @@ loadlib(void) break; } } - if(!found) Bprint(&bso, "warning: unable to find runtime.a\n"); + if(!found) + Bprint(&bso, "warning: unable to find runtime.a\n"); -loop: - xrefresolv = 0; 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); } +} - if(xrefresolv) - for(h=0; h<nelem(hash); h++) - for(s = hash[h]; s != S; s = s->link) - if(s->type == SXREF) - goto loop; - +/* + * 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, esym, cnt, l; - int work; + int32 off, l; Biobuf *f; - Sym *s; char magbuf[SARMAG]; - char name[100], pname[150]; + char pname[150]; struct ar_hdr arhdr; - char *e, *start, *stop, *x; pkg = smprint("%i", pkg); - if(file[0] == '-' && file[1] == 'l') { // TODO: fix this - if(debug['9']) - sprint(name, "/%s/lib/lib", thestring); - else - sprint(name, "/usr/%clib/lib", thechar); - strcat(name, file+2); - strcat(name, ".a"); - file = name; - } if(debug['v']) Bprint(&bso, "%5.2f ldobj: %s (%s)\n", cputime(), file, pkg); Bflush(&bso); @@ -291,9 +310,10 @@ objfile(char *file, char *pkg) Bterm(f); return; } - - l = Bread(f, &arhdr, SAR_HDR); - if(l != SAR_HDR) { + + /* 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; } @@ -301,88 +321,52 @@ objfile(char *file, char *pkg) diag("%s: first entry not symbol header", file); goto out; } - - esym = SARMAG + SAR_HDR + atolwhex(arhdr.size); - off = SARMAG + SAR_HDR; - - if(debug['u']) { - struct ar_hdr pkghdr; - int n; - - // Read next ar header to check for package safe bit. - Bseek(f, esym+(esym&1), 0); - l = Bread(f, &pkghdr, SAR_HDR); - if(l != SAR_HDR) { - diag("%s: short read on second archive header", file); - goto out; - } - if(strncmp(pkghdr.name, pkgname, strlen(pkgname))) { - diag("%s: second entry not package header", file); - goto out; - } - n = atolwhex(pkghdr.size); - ldpkg(f, pkg, n, file, Pkgdef); + 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); /* - * just bang the whole symbol file into memory + * 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. */ - Bseek(f, off, 0); - cnt = esym - off; - start = mal(cnt + 10); - cnt = Bread(f, start, cnt); - if(cnt <= 0){ - Bterm(f); - return; - } - stop = &start[cnt]; - memset(stop, 0, 10); - - work = 1; - while(work) { - if(debug['v']) - Bprint(&bso, "%5.2f library pass: %s\n", cputime(), file); - Bflush(&bso); - work = 0; - for(e = start; e < stop; e = strchr(e+5, 0) + 1) { - x = expandpkg(e+5, pkg); - s = lookup(x, 0); - if(x != e+5) - free(x); - if(s->type != SXREF) - continue; - sprint(pname, "%s(%s)", file, s->name); - if(debug['v']) - Bprint(&bso, "%5.2f library: %s\n", cputime(), pname); - Bflush(&bso); - l = e[1] & 0xff; - l |= (e[2] & 0xff) << 8; - l |= (e[3] & 0xff) << 16; - l |= (e[4] & 0xff) << 24; - Bseek(f, l, 0); - l = Bread(f, &arhdr, SAR_HDR); - if(l != SAR_HDR) - goto bad; - if(strncmp(arhdr.fmag, ARFMAG, sizeof(arhdr.fmag))) - goto bad; - l = SARNAME; - while(l > 0 && arhdr.name[l-1] == ' ') - l--; - sprint(pname, "%s(%.*s)", file, l, arhdr.name); - l = atolwhex(arhdr.size); - ldobj(f, pkg, l, pname, ArchiveObj); - if(s->type == SXREF) { - diag("%s: failed to load: %s", file, s->name); - errorexit(); - } - work = 1; - xrefresolv = 1; + 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); } - return; -bad: - diag("%s: bad or out of date archive", file); out: Bterm(f); } @@ -390,32 +374,38 @@ out: void ldobj(Biobuf *f, char *pkg, int64 len, char *pn, int whence) { - static int files; - static char **filen; - char **nfilen, *line; - int i, n, c1, c2, c3; + char *line; + int n, c1, c2, c3, c4; + uint32 magic; vlong import0, import1, eof; char src[1024]; eof = Boffset(f) + len; src[0] = '\0'; - // don't load individual object more than once. - // happens with import of .6 files because of loop in xresolv. - // doesn't happen with .a because SYMDEF is consulted - // first to decide whether each individual object file is needed. - for(i=0; i<files; i++) - if(strcmp(filen[i], pn) == 0) - return; + pn = strdup(pn); + + USED(c4); + USED(magic); - if((files&15) == 0){ - nfilen = malloc((files+16)*sizeof(char*)); - memmove(nfilen, filen, files*sizeof(char*)); - free(filen); - filen = nfilen; + 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; } - pn = strdup(pn); - filen[files++] = pn; /* check the header */ line = Brdline(f, '\n'); @@ -478,7 +468,7 @@ lookup(char *symb, int v) // 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->link) + for(s = hash[h]; s != S; s = s->hash) if(s->version == v) if(memcmp(s->name, symb, l) == 0) return s; @@ -487,14 +477,18 @@ lookup(char *symb, int v) 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->link = hash[h]; + s->hash = hash[h]; s->type = 0; s->version = v; s->value = 0; s->sig = 0; + s->size = 0; hash[h] = s; nsymbol++; return s; @@ -607,8 +601,13 @@ nuxiinit(void) if(i < 1) inuxi1[i] = c; inuxi4[i] = c; - inuxi8[i] = c; - inuxi8[i+4] = c+4; + 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; @@ -732,21 +731,6 @@ ieeedtod(Ieee *ieeep) } void -undefsym(Sym *s) -{ - int n; - - n = imports; - if(s->value != 0) - diag("value != 0 on SXREF"); - if(n >= 1<<Rindex) - diag("import index %d out of range", n); - s->value = n<<Roffset; - s->type = SUNDEF; - imports++; -} - -void zerosig(char *sp) { Sym *s; @@ -755,47 +739,6 @@ zerosig(char *sp) s->sig = 0; } -void -readundefs(char *f, int t) -{ - int i, n; - Sym *s; - Biobuf *b; - char *l, buf[256], *fields[64]; - - if(f == nil) - return; - b = Bopen(f, OREAD); - if(b == nil){ - diag("could not open %s: %r", f); - errorexit(); - } - while((l = Brdline(b, '\n')) != nil){ - n = Blinelen(b); - if(n >= sizeof(buf)){ - diag("%s: line too long", f); - errorexit(); - } - memmove(buf, l, n); - buf[n-1] = '\0'; - n = getfields(buf, fields, nelem(fields), 1, " \t\r\n"); - if(n == nelem(fields)){ - diag("%s: bad format", f); - errorexit(); - } - for(i = 0; i < n; i++){ - s = lookup(fields[i], 0); - s->type = SXREF; - s->subtype = t; - if(t == SIMPORT) - nimports++; - else - nexports++; - } - } - Bterm(b); -} - int32 Bget4(Biobuf *f) { @@ -829,15 +772,22 @@ mal(uint32 n) { void *v; - while(n & 7) - n++; + 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; } @@ -849,6 +799,16 @@ mal(uint32 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. @@ -901,3 +861,211 @@ iconv(Fmt *fp) return 0; } +void +mangle(char *file) +{ + fprint(2, "%s: mangled input file\n", file); + errorexit(); +} + +Section* +addsection(Segment *seg, char *name, int rwx) +{ + 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 +ewrite(int fd, void *buf, int n) +{ + if(write(fd, buf, n) < 0) { + diag("write error: %r"); + errorexit(); + } +} + +void +pclntab(void) +{ + vlong oldpc; + Prog *p; + int32 oldlc, v, s; + Sym *sym; + uchar *bp; + + sym = lookup("pclntab", 0); + sym->type = SRODATA; + 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], *last; + + 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; + last = nil; + 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 }; diff --git a/src/cmd/ld/lib.h b/src/cmd/ld/lib.h index 652d845fb..bcf297116 100644 --- a/src/cmd/ld/lib.h +++ b/src/cmd/ld/lib.h @@ -40,6 +40,34 @@ struct Library 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; @@ -64,11 +92,18 @@ EXTERN uchar inuxi8[8]; EXTERN char* outfile; EXTERN int32 nsymbol; EXTERN char* thestring; +EXTERN int ndynexp; + +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); @@ -83,20 +118,82 @@ 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 ldpkg(Biobuf*, char*, int64, char*, int); void mark(Sym *s); +void mkfwd(void); char* expandpkg(char*, char*); void deadcode(void); +void ewrite(int, void*, int); +Reloc* addrel(Sym*); +void codeblk(int32, int32); +void datblk(int32, int32); +Sym* datsort(Sym*); +void reloc(void); +void relocsym(Sym*); +void savedata(Sym*, Prog*); +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 asmelfsym64(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); 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; diff --git a/src/cmd/ld/macho.c b/src/cmd/ld/macho.c index 24400cf14..402e0ec63 100644 --- a/src/cmd/ld/macho.c +++ b/src/cmd/ld/macho.c @@ -6,6 +6,7 @@ // 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" @@ -46,6 +47,10 @@ newMachoLoad(uint32 type, uint32 ndata) diag("too many loads"); errorexit(); } + + if(macho64 && (ndata & 1)) + ndata++; + l = &load[nload++]; l->type = type; l->ndata = ndata; @@ -97,22 +102,6 @@ newMachoDebug(void) // Generic linking code. -static uchar *linkdata; -static uint32 nlinkdata; -static uint32 mlinkdata; - -static uchar *strtab; -static uint32 nstrtab; -static uint32 mstrtab; - -struct Expsym -{ - int off; - Sym* s; -} *expsym; -static int nexpsym; -static int nimpsym; - static char **dylib; static int ndylib; @@ -129,7 +118,7 @@ machowrite(void) MachoDebug *d; MachoLoad *l; - o1 = Boffset(&bso); + o1 = cpos(); loadsize = 4*4*ndebug; for(i=0; i<nload; i++) @@ -194,8 +183,8 @@ machowrite(void) LPUT(t->reloc); LPUT(t->nreloc); LPUT(t->flag); - LPUT(0); /* reserved */ - LPUT(0); /* reserved */ + LPUT(t->res1); /* reserved */ + LPUT(t->res2); /* reserved */ LPUT(0); /* reserved */ } else { strnput(t->name, 16); @@ -207,8 +196,8 @@ machowrite(void) LPUT(t->reloc); LPUT(t->nreloc); LPUT(t->flag); - LPUT(0); /* reserved */ - LPUT(0); /* reserved */ + LPUT(t->res1); /* reserved */ + LPUT(t->res2); /* reserved */ } } } @@ -229,224 +218,71 @@ machowrite(void) LPUT(d->filesize); } - return Boffset(&bso) - o1; -} - -static void* -grow(uchar **dat, uint32 *ndat, uint32 *mdat, uint32 n) -{ - uchar *p; - uint32 old; - - if(*ndat+n > *mdat) { - old = *mdat; - *mdat = (*ndat+n)*2 + 128; - *dat = realloc(*dat, *mdat); - if(*dat == 0) { - diag("out of memory"); - errorexit(); - } - memset(*dat+old, 0, *mdat-old); - } - p = *dat + *ndat; - *ndat += n; - return p; -} - -static int -needlib(char *name) -{ - char *p; - Sym *s; - - /* reuse hash code in symbol table */ - p = smprint(".machoload.%s", name); - s = lookup(p, 0); - if(s->type == 0) { - s->type = 100; // avoid SDATA, etc. - return 1; - } - return 0; + return cpos() - o1; } void domacho(void) { - int h, ptrsize, t; - char *p; - uchar *dat; - uint32 x; Sym *s; - Sym **impsym; - ptrsize = 4; - if(macho64) - ptrsize = 8; + if(debug['d']) + return; // empirically, string table must begin with " \x00". - if(!debug['d']) - *(char*)grow(&strtab, &nstrtab, &mstrtab, 2) = ' '; - - impsym = nil; - for(h=0; h<NHASH; h++) { - for(s=hash[h]; s!=S; s=s->link) { - if(!s->reachable || (s->type != STEXT && s->type != SDATA && s->type != SBSS) || s->dynimpname == nil) - continue; - if(debug['d']) { - diag("cannot use dynamic loading and -d"); - errorexit(); - } - if(!s->dynexport) { - if(nimpsym%32 == 0) { - impsym = realloc(impsym, (nimpsym+32)*sizeof impsym[0]); - if(impsym == nil) { - diag("out of memory"); - errorexit(); - } - } - impsym[nimpsym++] = s; - continue; - } - - /* symbol table entry - darwin still puts _ prefixes on all C symbols */ - x = nstrtab; - p = grow(&strtab, &nstrtab, &mstrtab, 1+strlen(s->dynimpname)+1); - *p++ = '_'; - strcpy(p, s->dynimpname); - - dat = grow(&linkdata, &nlinkdata, &mlinkdata, 8+ptrsize); - dat[0] = x; - dat[1] = x>>8; - dat[2] = x>>16; - dat[3] = x>>24; - - dat[4] = 0x0f; // type: N_SECT | N_EXT - external, defined in sect - switch(s->type) { - default: - case STEXT: - t = 1; - break; - case SDATA: - t = 2; - break; - case SBSS: - t = 4; - break; - } - dat[5] = t; // sect: section number - - if (nexpsym%32 == 0) { - expsym = realloc(expsym, (nexpsym+32)*sizeof expsym[0]); - if (expsym == nil) { - diag("out of memory"); - errorexit(); - } - } - expsym[nexpsym].off = nlinkdata - ptrsize; - expsym[nexpsym++].s = s; - } - } - - for(h=0; h<nimpsym; h++) { - s = impsym[h]; - s->type = SMACHO; - s->value = (nexpsym+h) * ptrsize; - - /* symbol table entry - darwin still puts _ prefixes on all C symbols */ - x = nstrtab; - p = grow(&strtab, &nstrtab, &mstrtab, 1+strlen(s->dynimpname)+1); - *p++ = '_'; - strcpy(p, s->dynimpname); - - dat = grow(&linkdata, &nlinkdata, &mlinkdata, 8+ptrsize); - dat[0] = x; - dat[1] = x>>8; - dat[2] = x>>16; - dat[3] = x>>24; - - dat[4] = 0x01; // type: N_EXT - external symbol - - if(needlib(s->dynimplib)) { - if(ndylib%32 == 0) { - dylib = realloc(dylib, (ndylib+32)*sizeof dylib[0]); - if(dylib == nil) { - diag("out of memory"); - errorexit(); - } - } - dylib[ndylib++] = s->dynimplib; - } - } - free(impsym); - - /* - * list of symbol table indexes. - * we don't take advantage of the opportunity - * to order the symbol table differently from - * this list, so it is boring: 0 1 2 3 4 ... - */ - for(x=0; x<nexpsym+nimpsym; x++) { - dat = grow(&linkdata, &nlinkdata, &mlinkdata, 4); - dat[0] = x; - dat[1] = x>>8; - dat[2] = x>>16; - dat[3] = x>>24; - } - - dynptrsize = (nexpsym+nimpsym) * ptrsize; + 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; } -vlong -domacholink(void) +void +machoadddynlib(char *lib) { - int i; - uchar *p; - Sym *s; - uint64 val; - - linkoff = 0; - if(nlinkdata > 0) { - linkoff = rnd(HEADR+textsize, INITRND) + rnd(datsize, INITRND); - seek(cout, linkoff, 0); - - for(i = 0; i<nexpsym; ++i) { - s = expsym[i].s; - val = s->value; - if(s->type == SUNDEF) - diag("export of undefined symbol %s", s->name); - if (s->type != STEXT) - val += INITDAT; - p = linkdata+expsym[i].off; - p[0] = val; - p[1] = val >> 8; - p[2] = val >> 16; - p[3] = val >> 24; - if (macho64) { - p[4] = val >> 32; - p[5] = val >> 40; - p[6] = val >> 48; - p[7] = val >> 56; - } + if(ndylib%32 == 0) { + dylib = realloc(dylib, (ndylib+32)*sizeof dylib[0]); + if(dylib == nil) { + diag("out of memory"); + errorexit(); } - - write(cout, linkdata, nlinkdata); - write(cout, strtab, nstrtab); } - return rnd(nlinkdata+nstrtab, INITRND); + dylib[ndylib++] = lib; } void -asmbmacho(vlong symdatva, vlong symo) +asmbmacho(void) { vlong v, w; vlong va; - int a, i, ptrsize; + int a, i; char *pkgroot; MachoHdr *mh; MachoSect *msect; MachoSeg *ms; MachoDebug *md; MachoLoad *ml; + Sym *s; /* apple MACH */ va = INITTEXT - HEADR; @@ -458,12 +294,10 @@ asmbmacho(vlong symdatva, vlong symo) case '6': mh->cpu = MACHO_CPU_AMD64; mh->subcpu = MACHO_SUBCPU_X86; - ptrsize = 8; break; case '8': mh->cpu = MACHO_CPU_386; mh->subcpu = MACHO_SUBCPU_X86; - ptrsize = 4; break; } @@ -472,8 +306,8 @@ asmbmacho(vlong symdatva, vlong symo) ms->vsize = va; /* text */ - v = rnd(HEADR+textsize, INITRND); - ms = newMachoSeg("__TEXT", 1); + v = rnd(HEADR+segtext.len, INITRND); + ms = newMachoSeg("__TEXT", 2); ms->vaddr = va; ms->vsize = v; ms->filesize = v; @@ -482,45 +316,50 @@ asmbmacho(vlong symdatva, vlong symo) msect = newMachoSect(ms, "__text"); msect->addr = INITTEXT; - msect->size = textsize; + 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 = datsize+dynptrsize+bsssize; - ms = newMachoSeg("__DATA", 2+(dynptrsize>0)); + w = segdata.len; + ms = newMachoSeg("__DATA", 3); ms->vaddr = va+v; ms->vsize = w; ms->fileoffset = v; - ms->filesize = datsize; + ms->filesize = segdata.filelen; ms->prot1 = 7; ms->prot2 = 3; msect = newMachoSect(ms, "__data"); msect->addr = va+v; - msect->size = datsize; + msect->size = symaddr(lookup(".got", 0)) - msect->addr; msect->off = v; - if(dynptrsize > 0) { + s = lookup(".got", 0); + if(s->size > 0) { msect = newMachoSect(ms, "__nl_symbol_ptr"); - msect->addr = va+v+datsize; - msect->size = dynptrsize; + msect->addr = symaddr(s); + msect->size = s->size; + msect->off = datoff(msect->addr); msect->align = 2; msect->flag = 6; /* section with nonlazy symbol pointers */ - /* - * The reserved1 field is supposed to be the index of - * the first entry in the list of symbol table indexes - * in isymtab for the symbols we need. We only use - * pointers, so we need the entire list, so the index - * here should be 0, which luckily is what the Mach-O - * writing code emits by default for this not really reserved field. - msect->reserved1 = 0; - first indirect symbol table entry we need - */ + msect->res1 = lookup(".linkedit.plt", 0)->size / 4; /* offset into indirect symbol table */ } msect = newMachoSect(ms, "__bss"); - msect->addr = va+v+datsize+dynptrsize; - msect->size = bsssize; + msect->addr = va+v+segdata.filelen; + msect->size = segdata.len - segdata.filelen; msect->flag = 1; /* flag - zero fill */ switch(thechar) { @@ -532,7 +371,7 @@ asmbmacho(vlong symdatva, vlong symo) 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()>>32; + ml->data[2+32+1] = entryvalue()>>16>>16; // hide >>32 for 8l break; case '8': ml = newMachoLoad(5, 16+2); /* unix thread */ @@ -543,39 +382,43 @@ asmbmacho(vlong symdatva, vlong symo) } if(!debug['d']) { - int nsym; + Sym *s1, *s2, *s3, *s4; - nsym = dynptrsize/ptrsize; + // 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(datsize+dynptrsize+bsssize, INITRND); - ms->vsize = nlinkdata+nstrtab; + ms->vaddr = va+v+rnd(segdata.len, INITRND); + ms->vsize = s1->size + s2->size + s3->size + s4->size; ms->fileoffset = linkoff; - ms->filesize = nlinkdata+nstrtab; + ms->filesize = ms->vsize; ms->prot1 = 7; ms->prot2 = 3; ml = newMachoLoad(2, 4); /* LC_SYMTAB */ ml->data[0] = linkoff; /* symoff */ - ml->data[1] = nsym; /* nsyms */ - ml->data[2] = linkoff + nlinkdata; /* stroff */ - ml->data[3] = nstrtab; /* strsize */ + 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] = nexpsym; /* nextdefsym */ - ml->data[4] = nexpsym; /* iundefsym */ - ml->data[5] = nimpsym; /* nundefsym */ + 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 + nlinkdata - nsym*4; /* indirectsymoff */ - ml->data[13] = nsym; /* nindirectsyms */ + 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 */ @@ -602,24 +445,53 @@ asmbmacho(vlong symdatva, vlong symo) } if(!debug['s']) { - ms = newMachoSeg("__SYMDAT", 1); - ms->vaddr = symdatva; - ms->vsize = 8+symsize+lcsize; - ms->fileoffset = symo; - ms->filesize = 8+symsize+lcsize; - ms->prot1 = 7; - ms->prot2 = 5; + Sym *s; md = newMachoDebug(); - md->fileoffset = symo+8; - md->filesize = symsize; + s = lookup("symtab", 0); + md->fileoffset = datoff(s->value); + md->filesize = s->size; md = newMachoDebug(); - md->fileoffset = symo+8+symsize; - md->filesize = lcsize; + s = lookup("pclntab", 0); + md->fileoffset = datoff(s->value); + md->filesize = s->size; + + dwarfaddmachoheaders(); } a = machowrite(); if(a > MACHORESERVE) diag("MACHORESERVE too small: %d > %d", a, MACHORESERVE); } + +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); + seek(cout, linkoff, 0); + + ewrite(cout, s1->p, s1->size); + ewrite(cout, s2->p, s2->size); + ewrite(cout, s3->p, s3->size); + ewrite(cout, s4->p, s4->size); + } + + return rnd(size, INITRND); +} diff --git a/src/cmd/ld/macho.h b/src/cmd/ld/macho.h index a96b2a383..4cc7edc80 100644 --- a/src/cmd/ld/macho.h +++ b/src/cmd/ld/macho.h @@ -18,6 +18,8 @@ struct MachoSect { uint32 reloc; uint32 nreloc; uint32 flag; + uint32 res1; + uint32 res2; }; typedef struct MachoSeg MachoSeg; @@ -70,8 +72,23 @@ enum { 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(vlong, vlong); +void asmbmacho(void); +void machoadddynlib(char*); diff --git a/src/cmd/ld/pe.c b/src/cmd/ld/pe.c index d3439abfb..82c6941f2 100644 --- a/src/cmd/ld/pe.c +++ b/src/cmd/ld/pe.c @@ -33,18 +33,39 @@ static char dosstub[] = 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +int32 PESECTHEADR; +int32 PEFILEHEADR; + static int pe64; static int nsect; -static int sect_virt_begin; -static int sect_raw_begin = PERESERVE; +static int nextsectoff; +static int nextfileoff; static IMAGE_FILE_HEADER fh; static IMAGE_OPTIONAL_HEADER oh; static IMAGE_SECTION_HEADER sh[16]; -static IMAGE_SECTION_HEADER *textsect, *datsect, *bsssect; + +typedef struct Imp Imp; +struct Imp { + Sym* s; + long va; + long vb; + Imp* next; +}; + +typedef struct Dll Dll; +struct Dll { + char* name; + int count; + Imp* ms; + Dll* next; +}; + +static Dll* dr; +static int ndll, nimp, nsize; static IMAGE_SECTION_HEADER* -new_section(char *name, int size, int noraw) +addpesection(char *name, int sectsize, int filesize, Segment *s) { IMAGE_SECTION_HEADER *h; @@ -54,15 +75,23 @@ new_section(char *name, int size, int noraw) } h = &sh[nsect++]; strncpy((char*)h->Name, name, sizeof(h->Name)); - h->VirtualSize = size; - if(!sect_virt_begin) - sect_virt_begin = 0x1000; - h->VirtualAddress = sect_virt_begin; - sect_virt_begin = rnd(sect_virt_begin+size, 0x1000); - if(!noraw) { - h->SizeOfRawData = rnd(size, PEALIGN); - h->PointerToRawData = sect_raw_begin; - sect_raw_begin += h->SizeOfRawData; + 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; + } + if(s) { + if(s->vaddr-PEBASE != h->VirtualAddress) { + diag("%s.VirtualAddress = %#llux, want %#llux", name, (vlong)h->VirtualAddress, (vlong)(s->vaddr-PEBASE)); + errorexit(); + } + if(s->fileoff != h->PointerToRawData) { + diag("%s.PointerToRawData = %#llux, want %#llux", name, (vlong)h->PointerToRawData, (vlong)(s->fileoff)); + errorexit(); + } } return h; } @@ -79,6 +108,11 @@ peinit(void) default: break; } + + PEFILEHEADR = rnd(sizeof(dosstub)+sizeof(fh)+sizeof(oh)+sizeof(sh), PEFILEALIGN); + PESECTHEADR = rnd(PEFILEHEADR, PESECTALIGN); + nextsectoff = PESECTHEADR; + nextfileoff = PEFILEHEADR; } static void @@ -86,7 +120,8 @@ pewrite(void) { int i, j; - write(cout, dosstub, sizeof dosstub); + seek(cout, 0, 0); + ewrite(cout, dosstub, sizeof dosstub); strnput("PE", 4); for (i=0; i<sizeof(fh); i++) @@ -98,24 +133,6 @@ pewrite(void) cput(((char*)&sh[i])[j]); } -void -dope(void) -{ - textsect = new_section(".text", textsize, 0); - textsect->Characteristics = IMAGE_SCN_CNT_CODE| - IMAGE_SCN_CNT_INITIALIZED_DATA| - IMAGE_SCN_MEM_EXECUTE|IMAGE_SCN_MEM_READ; - - datsect = new_section(".data", datsize, 0); - datsect->Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA| - IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE; - INITDAT = PEBASE+datsect->VirtualAddress; - - bsssect = new_section(".bss", bsssize, 1); - bsssect->Characteristics = IMAGE_SCN_CNT_UNINITIALIZED_DATA| - IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE; -} - static void strput(char *s) { @@ -124,73 +141,167 @@ strput(char *s) cput('\0'); } -static void -add_import_table(void) +static Dll* +initdynimport(void) { - IMAGE_IMPORT_DESCRIPTOR ds[2], *d; - char *dllname = "kernel32.dll"; - struct { - char *name; - uint32 thunk; - } *f, fs[] = { - { "GetProcAddress", 0 }, - { "LoadLibraryExA", 0 }, - { 0, 0 } - }; - - uint32 size = 0; - memset(ds, 0, sizeof(ds)); - size += sizeof(ds); - ds[0].Name = size; - size += strlen(dllname) + 1; - for(f=fs; f->name; f++) { - f->thunk = size; - size += sizeof(uint16) + strlen(f->name) + 1; + Imp *m; + Dll *d; + Sym *s; + int i; + Sym *dynamic; + + dr = nil; + ndll = 0; + nimp = 0; + nsize = 0; + + for(i=0; i<NHASH; i++) + for(s = hash[i]; s != S; s = s->hash) { + if(!s->reachable || !s->dynimpname) + continue; + nimp++; + for(d = dr; d != nil; d = d->next) { + if(strcmp(d->name,s->dynimplib) == 0) { + m = mal(sizeof *m); + m->s = s; + m->next = d->ms; + d->ms = m; + d->count++; + nsize += strlen(s->dynimpname)+2+1; + break; + } + } + if(d == nil) { + d = mal(sizeof *d); + d->name = s->dynimplib; + d->count = 1; + d->next = dr; + dr = d; + m = mal(sizeof *m); + m->s = s; + m->next = 0; + d->ms = m; + ndll++; + nsize += strlen(s->dynimpname)+2+1; + nsize += strlen(s->dynimplib)+1; + } + } + + nsize += 20*ndll + 20; + nsize += 4*nimp + 4*ndll; + + 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 += 4; + } + dynamic->size += 4; } - ds[0].FirstThunk = size; - for(f=fs; f->name; f++) - size += sizeof(fs[0].thunk); + + return dr; +} +static void +addimports(vlong fileoff, IMAGE_SECTION_HEADER *datsect) +{ IMAGE_SECTION_HEADER *isect; - isect = new_section(".idata", size, 0); + uint32 va; + int noff, aoff, o, last_fn, last_name_off, iat_off; + Imp *m; + Dll *d; + Sym* dynamic; + + isect = addpesection(".idata", nsize, nsize, 0); isect->Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA| IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE; - - uint32 va = isect->VirtualAddress; + va = isect->VirtualAddress; oh.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = va; oh.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = isect->VirtualSize; - ds[0].Name += va; - ds[0].FirstThunk += va; - for(f=fs; f->name; f++) - f->thunk += va; + seek(cout, fileoff, 0); - vlong off = seek(cout, 0, 1); - seek(cout, 0, 2); - for(d=ds; ; d++) { - lputl(d->OriginalFirstThunk); - lputl(d->TimeDateStamp); - lputl(d->ForwarderChain); - lputl(d->Name); - lputl(d->FirstThunk); - if(!d->Name) - break; + dynamic = lookup(".windynamic", 0); + iat_off = dynamic->value - PEBASE; // FirstThunk allocated in .data + oh.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress = iat_off; + oh.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size = dynamic->size; + + noff = va + 20*ndll + 20; + aoff = noff + 4*nimp + 4*ndll; + last_fn = 0; + last_name_off = aoff; + for(d = dr; d != nil; d = d->next) { + lputl(noff); + lputl(0); + lputl(0); + lputl(last_name_off); + lputl(iat_off); + last_fn = d->count; + noff += 4*last_fn + 4; + aoff += 4*last_fn + 4; + iat_off += 4*last_fn + 4; + last_name_off += strlen(d->name)+1; } - strput(dllname); - for(f=fs; f->name; f++) { - wputl(0); - strput(f->name); + lputl(0); //end + lputl(0); + lputl(0); + lputl(0); + lputl(0); + + // put OriginalFirstThunk + o = last_name_off; + for(d = dr; d != nil; d = d->next) { + for(m = d->ms; m != nil; m = m->next) { + lputl(o); + o += 2 + strlen(m->s->dynimpname) + 1; + } + lputl(0); + } + // put names + for(d = dr; d != nil; d = d->next) { + strput(d->name); + } + // put hint+name + for(d = dr; d != nil; d = d->next) { + for(m = d->ms; m != nil; m = m->next) { + wputl(0); + strput(m->s->dynimpname); + } + } + + strnput("", isect->SizeOfRawData - nsize); + cflush(); + + // put FirstThunk + o = last_name_off; + seek(cout, datsect->PointerToRawData + dynamic->value - PEBASE - datsect->VirtualAddress, 0); + for(d = dr; d != nil; d = d->next) { + for(m = d->ms; m != nil; m = m->next) { + lputl(o); + o += 2 + strlen(m->s->dynimpname) + 1; + } + lputl(0); } - for(f=fs; f->name; f++) - lputl(f->thunk); - strnput("", isect->SizeOfRawData - size); cflush(); - seek(cout, off, 0); + seek(cout, 0, 2); +} + +void +dope(void) +{ + initdynimport(); } void asmbpe(void) { + IMAGE_SECTION_HEADER *t, *d; + switch(thechar) { default: diag("unknown PE architecture"); @@ -203,14 +314,16 @@ asmbpe(void) break; } - if(!debug['s']) { - IMAGE_SECTION_HEADER *symsect; - symsect = new_section(".symdat", 8+symsize+lcsize, 0); - symsect->Characteristics = IMAGE_SCN_MEM_READ| - IMAGE_SCN_CNT_INITIALIZED_DATA; - } + t = addpesection(".text", segtext.len, segtext.len, &segtext); + t->Characteristics = IMAGE_SCN_CNT_CODE| + IMAGE_SCN_CNT_INITIALIZED_DATA| + IMAGE_SCN_MEM_EXECUTE|IMAGE_SCN_MEM_READ; + + d = addpesection(".data", segdata.len, segdata.filelen, &segdata); + d->Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA| + IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE; - add_import_table(); + addimports(nextfileoff, d); fh.NumberOfSections = nsect; fh.TimeDateStamp = time(0); @@ -223,24 +336,24 @@ asmbpe(void) oh.Magic = 0x10b; // PE32 oh.MajorLinkerVersion = 1; oh.MinorLinkerVersion = 0; - oh.SizeOfCode = textsect->SizeOfRawData; - oh.SizeOfInitializedData = datsect->SizeOfRawData; - oh.SizeOfUninitializedData = bsssect->SizeOfRawData; + oh.SizeOfCode = t->SizeOfRawData; + oh.SizeOfInitializedData = d->SizeOfRawData; + oh.SizeOfUninitializedData = 0; oh.AddressOfEntryPoint = entryvalue()-PEBASE; - oh.BaseOfCode = textsect->VirtualAddress; - oh.BaseOfData = datsect->VirtualAddress; + oh.BaseOfCode = t->VirtualAddress; + oh.BaseOfData = d->VirtualAddress; oh.ImageBase = PEBASE; - oh.SectionAlignment = 0x00001000; - oh.FileAlignment = PEALIGN; + oh.SectionAlignment = PESECTALIGN; + oh.FileAlignment = PEFILEALIGN; oh.MajorOperatingSystemVersion = 4; oh.MinorOperatingSystemVersion = 0; oh.MajorImageVersion = 1; oh.MinorImageVersion = 0; oh.MajorSubsystemVersion = 4; oh.MinorSubsystemVersion = 0; - oh.SizeOfImage = sect_virt_begin; - oh.SizeOfHeaders = PERESERVE; + oh.SizeOfImage = nextsectoff; + oh.SizeOfHeaders = PEFILEHEADR; oh.Subsystem = 3; // WINDOWS_CUI oh.SizeOfStackReserve = 0x00200000; oh.SizeOfStackCommit = 0x00001000; diff --git a/src/cmd/ld/pe.h b/src/cmd/ld/pe.h index b64dd97c0..f8161cc4a 100644 --- a/src/cmd/ld/pe.h +++ b/src/cmd/ld/pe.h @@ -72,9 +72,16 @@ typedef struct { uint32 FirstThunk; } IMAGE_IMPORT_DESCRIPTOR; -#define PERESERVE 0x400 -#define PEALIGN 0x200 #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, @@ -112,5 +119,6 @@ enum { }; void peinit(void); -void dope(void); void asmbpe(void); +void dope(void); + diff --git a/src/cmd/ld/symtab.c b/src/cmd/ld/symtab.c new file mode 100644 index 000000000..26e4def64 --- /dev/null +++ b/src/cmd/ld/symtab.c @@ -0,0 +1,259 @@ +// 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 +putelfsym64(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go) +{ + int bind, type, shndx, stroff; + + bind = STB_GLOBAL; + switch(t) { + default: + return; + case 'T': + type = STT_FUNC; + shndx = elftextsh + 0; + break; + case 'D': + type = STT_OBJECT; + shndx = elftextsh + 1; + break; + case 'B': + type = STT_OBJECT; + shndx = elftextsh + 2; + break; + } + + stroff = putelfstr(s); + LPUT(stroff); // string + cput((bind<<4)|(type&0xF)); + cput(0); + WPUT(shndx); + VPUT(addr); + VPUT(size); +} + +void +asmelfsym64(void) +{ + genasmsym(putelfsym64); +} + +void +putelfsym32(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go) +{ + int bind, type, shndx, stroff; + + bind = STB_GLOBAL; + switch(t) { + default: + return; + case 'T': + type = STT_FUNC; + shndx = elftextsh + 0; + break; + case 'D': + type = STT_OBJECT; + shndx = elftextsh + 1; + break; + case 'B': + type = STT_OBJECT; + shndx = elftextsh + 2; + break; + } + + stroff = putelfstr(s); + LPUT(stroff); // string + LPUT(addr); + LPUT(size); + cput((bind<<4)|(type&0xF)); + cput(0); + WPUT(shndx); +} + +void +asmelfsym32(void) +{ + genasmsym(putelfsym32); +} + + +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 +putsymb(Sym *s, char *name, int t, vlong v, vlong size, int ver, Sym *typ) +{ + int i, f, l; + Reloc *rel; + + 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); + i++; + } + 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, ver, typ ? typ->name : ""); + else + Bprint(&bso, "%c %.8llux %s %s\n", t, v, s, typ ? typ->name : ""); + } +} + +void +symtab(void) +{ + // 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); + + symt = lookup("symtab", 0); + symt->type = SRODATA; + symt->size = 0; + symt->reachable = 1; + + genasmsym(putsymb); +} |