summaryrefslogtreecommitdiff
path: root/src/cmd/ld
diff options
context:
space:
mode:
authorOndřej Surý <ondrej@sury.org>2011-01-17 12:40:45 +0100
committerOndřej Surý <ondrej@sury.org>2011-01-17 12:40:45 +0100
commit3e45412327a2654a77944249962b3652e6142299 (patch)
treebc3bf69452afa055423cbe0c5cfa8ca357df6ccf /src/cmd/ld
parentc533680039762cacbc37db8dc7eed074c3e497be (diff)
downloadgolang-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.c914
-rw-r--r--src/cmd/ld/dwarf.c2547
-rw-r--r--src/cmd/ld/dwarf.h29
-rw-r--r--src/cmd/ld/dwarf_defs.h503
-rw-r--r--src/cmd/ld/elf.c124
-rw-r--r--src/cmd/ld/elf.h8
-rw-r--r--src/cmd/ld/go.c182
-rw-r--r--src/cmd/ld/ldelf.c816
-rw-r--r--src/cmd/ld/ldmacho.c794
-rw-r--r--src/cmd/ld/lib.c546
-rw-r--r--src/cmd/ld/lib.h97
-rw-r--r--src/cmd/ld/macho.c394
-rw-r--r--src/cmd/ld/macho.h19
-rw-r--r--src/cmd/ld/pe.c307
-rw-r--r--src/cmd/ld/pe.h14
-rw-r--r--src/cmd/ld/symtab.c259
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 = &le;
+ 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 = &le;
+ }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);
+}