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