summaryrefslogtreecommitdiff
path: root/src/cmd/ld
diff options
context:
space:
mode:
authorMichael Stapelberg <stapelberg@debian.org>2013-03-04 21:27:36 +0100
committerMichael Stapelberg <michael@stapelberg.de>2013-03-04 21:27:36 +0100
commit04b08da9af0c450d645ab7389d1467308cfc2db8 (patch)
treedb247935fa4f2f94408edc3acd5d0d4f997aa0d8 /src/cmd/ld
parent917c5fb8ec48e22459d77e3849e6d388f93d3260 (diff)
downloadgolang-upstream/1.1_hg20130304.tar.gz
Imported Upstream version 1.1~hg20130304upstream/1.1_hg20130304
Diffstat (limited to 'src/cmd/ld')
-rw-r--r--src/cmd/ld/data.c620
-rw-r--r--src/cmd/ld/decodesym.c215
-rw-r--r--src/cmd/ld/doc.go21
-rw-r--r--src/cmd/ld/dwarf.c252
-rw-r--r--src/cmd/ld/elf.c954
-rw-r--r--src/cmd/ld/elf.h43
-rw-r--r--src/cmd/ld/go.c380
-rw-r--r--src/cmd/ld/ldelf.c237
-rw-r--r--src/cmd/ld/ldmacho.c66
-rw-r--r--src/cmd/ld/ldpe.c44
-rw-r--r--src/cmd/ld/lib.c252
-rw-r--r--src/cmd/ld/lib.h71
-rw-r--r--src/cmd/ld/macho.h2
-rw-r--r--src/cmd/ld/pe.c6
-rw-r--r--src/cmd/ld/symtab.c240
15 files changed, 2572 insertions, 831 deletions
diff --git a/src/cmd/ld/data.c b/src/cmd/ld/data.c
index 786c10b64..6c6b1be43 100644
--- a/src/cmd/ld/data.c
+++ b/src/cmd/ld/data.c
@@ -34,6 +34,7 @@
#include "../ld/lib.h"
#include "../ld/elf.h"
#include "../ld/pe.h"
+#include "../../pkg/runtime/mgc0.h"
void dynreloc(void);
static vlong addaddrplus4(Sym *s, Sym *t, int32 add);
@@ -57,70 +58,73 @@ datcmp(Sym *s1, Sym *s2)
}
Sym*
-datsort(Sym *l)
+listsort(Sym *l, int (*cmp)(Sym*, Sym*), int off)
{
Sym *l1, *l2, *le;
+ #define NEXT(l) (*(Sym**)((char*)(l)+off))
- if(l == 0 || l->next == 0)
+ if(l == 0 || NEXT(l) == 0)
return l;
l1 = l;
l2 = l;
for(;;) {
- l2 = l2->next;
+ l2 = NEXT(l2);
if(l2 == 0)
break;
- l2 = l2->next;
+ l2 = NEXT(l2);
if(l2 == 0)
break;
- l1 = l1->next;
+ l1 = NEXT(l1);
}
- l2 = l1->next;
- l1->next = 0;
- l1 = datsort(l);
- l2 = datsort(l2);
+ l2 = NEXT(l1);
+ NEXT(l1) = 0;
+ l1 = listsort(l, cmp, off);
+ l2 = listsort(l2, cmp, off);
/* set up lead element */
- if(datcmp(l1, l2) < 0) {
+ if(cmp(l1, l2) < 0) {
l = l1;
- l1 = l1->next;
+ l1 = NEXT(l1);
} else {
l = l2;
- l2 = l2->next;
+ l2 = NEXT(l2);
}
le = l;
for(;;) {
if(l1 == 0) {
while(l2) {
- le->next = l2;
+ NEXT(le) = l2;
le = l2;
- l2 = l2->next;
+ l2 = NEXT(l2);
}
- le->next = 0;
+ NEXT(le) = 0;
break;
}
if(l2 == 0) {
while(l1) {
- le->next = l1;
+ NEXT(le) = l1;
le = l1;
- l1 = l1->next;
+ l1 = NEXT(l1);
}
break;
}
- if(datcmp(l1, l2) < 0) {
- le->next = l1;
+ if(cmp(l1, l2) < 0) {
+ NEXT(le) = l1;
le = l1;
- l1 = l1->next;
+ l1 = NEXT(l1);
} else {
- le->next = l2;
+ NEXT(le) = l2;
le = l2;
- l2 = l2->next;
+ l2 = NEXT(l2);
}
}
- le->next = 0;
+ NEXT(le) = 0;
return l;
+
+ #undef NEXT
}
Reloc*
@@ -145,21 +149,22 @@ void
relocsym(Sym *s)
{
Reloc *r;
+ Sym *rs;
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);
+ if(off < 0 || off+siz > s->np) {
+ diag("%s: invalid relocation %d+%d not in [%d,%d)", s->name, off, siz, 0, s->np);
continue;
}
- if(r->sym != S && (r->sym->type == 0 || r->sym->type == SXREF)) {
+ if(r->sym != S && (r->sym->type & SMASK == 0 || r->sym->type & SMASK == SXREF)) {
diag("%s: not defined", r->sym->name);
continue;
}
@@ -175,18 +180,35 @@ relocsym(Sym *s)
switch(r->type) {
default:
o = 0;
- if(archreloc(r, s, &o) < 0)
+ if(isobj || archreloc(r, s, &o) < 0)
diag("unknown reloc %d", r->type);
break;
case D_ADDR:
o = symaddr(r->sym) + r->add;
+ if(isobj && r->sym->type != SCONST) {
+ if(thechar == '6')
+ o = 0;
+ else {
+ // set up addend for eventual relocation via outer symbol
+ rs = r->sym;
+ while(rs->outer != nil)
+ rs = rs->outer;
+ o -= symaddr(rs);
+ }
+ }
break;
case D_PCREL:
- // r->sym can be null when CALL $(constant) is transformed from absoulte PC to relative PC call.
+ // r->sym can be null when CALL $(constant) is transformed from absolute PC to relative PC call.
o = 0;
if(r->sym)
o += symaddr(r->sym);
o += r->add - (s->value + r->off + r->siz);
+ if(isobj && r->sym->type != SCONST) {
+ if(thechar == '6')
+ o = 0;
+ else
+ o = r->add - r->siz;
+ }
break;
case D_SIZE:
o = r->sym->size + r->add;
@@ -197,20 +219,6 @@ relocsym(Sym *s)
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;
@@ -222,7 +230,7 @@ relocsym(Sym *s)
for(i=0; i<8; i++)
s->p[off+i] = cast[inuxi8[i]];
break;
- }
+ }
}
}
@@ -230,7 +238,7 @@ void
reloc(void)
{
Sym *s;
-
+
if(debug['v'])
Bprint(&bso, "%5.2f reloc\n", cputime());
Bflush(&bso);
@@ -245,10 +253,12 @@ void
dynrelocsym(Sym *s)
{
Reloc *r;
+ Sym *rel;
+ Sym *got;
if(HEADTYPE == Hwindows) {
Sym *rel, *targ;
-
+
rel = lookup(".rel", 0);
if(s == rel)
return;
@@ -258,7 +268,7 @@ dynrelocsym(Sym *s)
targ->plt = rel->size;
r->sym = rel;
r->add = targ->plt;
-
+
// jmp *addr
if(thechar == '8') {
adduint8(rel, 0xff);
@@ -281,16 +291,30 @@ dynrelocsym(Sym *s)
return;
}
- for(r=s->r; r<s->r+s->nr; r++)
- if(r->sym->type == SDYNIMPORT || r->type >= 256)
+ got = rel = nil;
+ if(flag_shared) {
+ rel = lookuprel();
+ got = lookup(".got", 0);
+ }
+ s->rel_ro = 0;
+ for(r=s->r; r<s->r+s->nr; r++) {
+ if(r->sym != S && r->sym->type == SDYNIMPORT || r->type >= 256)
adddynrel(s, r);
+ if(flag_shared && r->sym != S && (r->sym->dynimpname == nil || r->sym->dynexport) && r->type == D_ADDR
+ && (s == got || s->type == SDATA || s->type == SGOSTRING || s->type == STYPE || s->type == SRODATA)) {
+ // Create address based RELATIVE relocation
+ adddynrela(rel, s, r);
+ if(s->type < SNOPTRDATA)
+ s->rel_ro = 1;
+ }
+ }
}
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)
@@ -363,12 +387,12 @@ savedata(Sym *s, Prog *p, char *pn)
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;
@@ -449,12 +473,12 @@ blk(Sym *start, int32 addr, int32 size)
errorexit();
}
}
-
+
for(; addr < eaddr; addr++)
cput(0);
cflush();
}
-
+
void
codeblk(int32 addr, int32 size)
{
@@ -497,7 +521,7 @@ codeblk(int32 addr, int32 size)
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;
@@ -509,7 +533,7 @@ codeblk(int32 addr, int32 size)
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)
@@ -531,7 +555,7 @@ codeblk(int32 addr, int32 size)
}
Bflush(&bso);
}
-
+
void
datblk(int32 addr, int32 size)
{
@@ -579,7 +603,7 @@ datblk(int32 addr, int32 size)
void
strnput(char *s, int n)
{
- for(; *s && n > 0; s++) {
+ for(; n > 0 && *s; s++) {
cput(*s);
n--;
}
@@ -594,18 +618,22 @@ addstrdata(char *name, char *value)
{
Sym *s, *sp;
char *p;
-
+
p = smprint("%s.str", name);
sp = lookup(p, 0);
free(p);
addstring(sp, value);
s = lookup(name, 0);
+ s->size = 0;
s->dupok = 1;
addaddr(s, sp);
adduint32(s, strlen(value));
if(PtrSize == 8)
adduint32(s, 0); // round struct to pointer width
+
+ // in case reachability has already been computed
+ sp->reachable = s->reachable;
}
vlong
@@ -628,41 +656,51 @@ addstring(Sym *s, char *str)
}
vlong
-adduintxx(Sym *s, uint64 v, int wid)
+setuintxx(Sym *s, vlong off, uint64 v, int wid)
{
- int32 i, r, fl;
+ int32 i, 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);
+ if(s->size < off+wid) {
+ s->size = off+wid;
+ symgrow(s, s->size);
+ }
fl = v;
cast = (uchar*)&fl;
switch(wid) {
case 1:
- s->p[r] = cast[inuxi1[0]];
+ s->p[off] = cast[inuxi1[0]];
break;
case 2:
for(i=0; i<2; i++)
- s->p[r+i] = cast[inuxi2[i]];
+ s->p[off+i] = cast[inuxi2[i]];
break;
case 4:
for(i=0; i<4; i++)
- s->p[r+i] = cast[inuxi4[i]];
+ s->p[off+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]];
+ s->p[off+i] = cast[inuxi8[i]];
break;
}
- return r;
+ return off;
+}
+
+vlong
+adduintxx(Sym *s, uint64 v, int wid)
+{
+ int32 off;
+
+ off = s->size;
+ setuintxx(s, off, v, wid);
+ return off;
}
vlong
@@ -689,6 +727,30 @@ adduint64(Sym *s, uint64 v)
return adduintxx(s, v, 8);
}
+void
+setuint8(Sym *s, vlong r, uint8 v)
+{
+ setuintxx(s, r, v, 1);
+}
+
+void
+setuint16(Sym *s, vlong r, uint16 v)
+{
+ setuintxx(s, r, v, 2);
+}
+
+void
+setuint32(Sym *s, vlong r, uint32 v)
+{
+ setuintxx(s, r, v, 4);
+}
+
+void
+setuint64(Sym *s, vlong r, uint64 v)
+{
+ setuintxx(s, r, v, 8);
+}
+
vlong
addaddrplus(Sym *s, Sym *t, int32 add)
{
@@ -736,7 +798,7 @@ addpcrelplus(Sym *s, Sym *t, int32 add)
{
vlong i;
Reloc *r;
-
+
if(s->type == 0)
s->type = SDATA;
s->reachable = 1;
@@ -759,6 +821,33 @@ addaddr(Sym *s, Sym *t)
}
vlong
+setaddrplus(Sym *s, vlong off, Sym *t, int32 add)
+{
+ Reloc *r;
+
+ if(s->type == 0)
+ s->type = SDATA;
+ s->reachable = 1;
+ if(off+PtrSize > s->size) {
+ s->size = off + PtrSize;
+ symgrow(s, s->size);
+ }
+ r = addrel(s);
+ r->sym = t;
+ r->off = off;
+ r->siz = PtrSize;
+ r->type = D_ADDR;
+ r->add = add;
+ return off;
+}
+
+vlong
+setaddr(Sym *s, vlong off, Sym *t)
+{
+ return setaddrplus(s, off, t, 0);
+}
+
+vlong
addsize(Sym *s, Sym *t)
{
vlong i;
@@ -793,17 +882,91 @@ dosymtype(void)
}
}
+static int32
+alignsymsize(int32 s)
+{
+ if(s >= 8)
+ s = rnd(s, 8);
+ else if(s >= PtrSize)
+ s = rnd(s, PtrSize);
+ else if(s > 2)
+ s = rnd(s, 4);
+ return s;
+}
+
+static int32
+aligndatsize(int32 datsize, Sym *s)
+{
+ int32 t;
+
+ if(s->align != 0) {
+ datsize = rnd(datsize, s->align);
+ } else {
+ t = alignsymsize(s->size);
+ if(t & 1) {
+ ;
+ } else if(t & 2)
+ datsize = rnd(datsize, 2);
+ else if(t & 4)
+ datsize = rnd(datsize, 4);
+ else
+ datsize = rnd(datsize, 8);
+ }
+ return datsize;
+}
+
+static void
+gcaddsym(Sym *gc, Sym *s, int32 off)
+{
+ int32 a;
+ Sym *gotype;
+
+ if(s->size < PtrSize)
+ return;
+ if(strcmp(s->name, ".string") == 0)
+ return;
+
+ gotype = s->gotype;
+ if(gotype != nil) {
+ //print("gcaddsym: %s %d %s\n", s->name, s->size, gotype->name);
+ adduintxx(gc, GC_CALL, PtrSize);
+ adduintxx(gc, off, PtrSize);
+ addpcrelplus(gc, decodetype_gc(gotype), 3*PtrSize+4);
+ if(PtrSize == 8)
+ adduintxx(gc, 0, 4);
+ } else {
+ //print("gcaddsym: %s %d <unknown type>\n", s->name, s->size);
+ for(a = -off&(PtrSize-1); a+PtrSize<=s->size; a+=PtrSize) {
+ adduintxx(gc, GC_APTR, PtrSize);
+ adduintxx(gc, off+a, PtrSize);
+ }
+ }
+}
+
void
dodata(void)
{
int32 t, datsize;
- Section *sect, *noptr;
+ Section *sect;
Sym *s, *last, **l;
+ Sym *gcdata1, *gcbss1;
if(debug['v'])
Bprint(&bso, "%5.2f dodata\n", cputime());
Bflush(&bso);
+ // define garbage collection symbols
+ gcdata1 = lookup("gcdata1", 0);
+ gcdata1->type = SGCDATA;
+ gcdata1->reachable = 1;
+ gcbss1 = lookup("gcbss1", 0);
+ gcbss1->type = SGCBSS;
+ gcbss1->reachable = 1;
+
+ // size of .data and .bss section. the zero value is later replaced by the actual size of the section.
+ adduintxx(gcdata1, 0, PtrSize);
+ adduintxx(gcbss1, 0, PtrSize);
+
last = nil;
datap = nil;
@@ -834,7 +997,7 @@ dodata(void)
* 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)
@@ -844,19 +1007,150 @@ dodata(void)
}
*l = nil;
- datap = datsort(datap);
+ if(flag_shared) {
+ for(s=datap; s != nil; s = s->next) {
+ if(s->rel_ro)
+ s->type = SDATARELRO;
+ }
+ }
+ datap = listsort(datap, datcmp, offsetof(Sym, next));
/*
- * allocate data sections. list is sorted by type,
+ * allocate sections. list is sorted by type,
* so we can just walk it for each piece we want to emit.
+ * segdata is processed before segtext, because we need
+ * to see all symbols in the .data and .bss sections in order
+ * to generate garbage collection information.
*/
+ /* begin segdata */
+
+ /* skip symbols belonging to segtext */
+ s = datap;
+ for(; s != nil && s->type < SELFSECT; s = s->next)
+ ;
+
+ /* writable ELF sections */
+ datsize = 0;
+ for(; s != nil && s->type < SNOPTRDATA; s = s->next) {
+ sect = addsection(&segdata, s->name, 06);
+ if(s->align != 0)
+ datsize = rnd(datsize, s->align);
+ sect->vaddr = datsize;
+ s->sect = sect;
+ s->type = SDATA;
+ s->value = datsize;
+ datsize += rnd(s->size, PtrSize);
+ sect->len = datsize - sect->vaddr;
+ }
+
+ /* pointer-free data */
+ sect = addsection(&segdata, ".noptrdata", 06);
+ sect->vaddr = datsize;
+ lookup("noptrdata", 0)->sect = sect;
+ lookup("enoptrdata", 0)->sect = sect;
+ for(; s != nil && s->type < SDATARELRO; s = s->next) {
+ s->sect = sect;
+ s->type = SDATA;
+ t = alignsymsize(s->size);
+ datsize = aligndatsize(datsize, s);
+ s->value = datsize;
+ datsize += t;
+ }
+ sect->len = datsize - sect->vaddr;
+ datsize = rnd(datsize, PtrSize);
+
+ /* dynamic relocated rodata */
+ if(flag_shared) {
+ sect = addsection(&segdata, ".data.rel.ro", 06);
+ sect->vaddr = datsize;
+ lookup("datarelro", 0)->sect = sect;
+ lookup("edatarelro", 0)->sect = sect;
+ for(; s != nil && s->type == SDATARELRO; s = s->next) {
+ if(s->align != 0)
+ datsize = rnd(datsize, s->align);
+ s->sect = sect;
+ s->type = SDATA;
+ s->value = datsize;
+ datsize += rnd(s->size, PtrSize);
+ }
+ sect->len = datsize - sect->vaddr;
+ datsize = rnd(datsize, PtrSize);
+ }
+
+ /* data */
+ sect = addsection(&segdata, ".data", 06);
+ sect->vaddr = datsize;
+ lookup("data", 0)->sect = sect;
+ lookup("edata", 0)->sect = sect;
+ for(; s != nil && s->type < SBSS; s = s->next) {
+ if(s->type == SDATARELRO) {
+ cursym = s;
+ diag("unexpected symbol type %d", s->type);
+ }
+ s->sect = sect;
+ s->type = SDATA;
+ t = alignsymsize(s->size);
+ datsize = aligndatsize(datsize, s);
+ s->value = datsize;
+ gcaddsym(gcdata1, s, datsize - sect->vaddr); // gc
+ datsize += t;
+ }
+ sect->len = datsize - sect->vaddr;
+ datsize = rnd(datsize, PtrSize);
+
+ adduintxx(gcdata1, GC_END, PtrSize);
+ setuintxx(gcdata1, 0, sect->len, PtrSize);
+
+ /* bss */
+ sect = addsection(&segdata, ".bss", 06);
+ sect->vaddr = datsize;
+ lookup("bss", 0)->sect = sect;
+ lookup("ebss", 0)->sect = sect;
+ for(; s != nil && s->type < SNOPTRBSS; s = s->next) {
+ s->sect = sect;
+ t = alignsymsize(s->size);
+ datsize = aligndatsize(datsize, s);
+ s->value = datsize;
+ gcaddsym(gcbss1, s, datsize - sect->vaddr); // gc
+ datsize += t;
+ }
+ sect->len = datsize - sect->vaddr;
+ datsize = rnd(datsize, PtrSize);
+
+ adduintxx(gcbss1, GC_END, PtrSize);
+ setuintxx(gcbss1, 0, sect->len, PtrSize);
+
+ /* pointer-free bss */
+ sect = addsection(&segdata, ".noptrbss", 06);
+ sect->vaddr = datsize;
+ lookup("noptrbss", 0)->sect = sect;
+ lookup("enoptrbss", 0)->sect = sect;
+ for(; s != nil; s = s->next) {
+ if(s->type > SNOPTRBSS) {
+ cursym = s;
+ diag("unexpected symbol type %d", s->type);
+ }
+ s->sect = sect;
+ t = alignsymsize(s->size);
+ datsize = aligndatsize(datsize, s);
+ s->value = datsize;
+ datsize += t;
+ }
+ sect->len = datsize - sect->vaddr;
+ lookup("end", 0)->sect = sect;
+
+ /* we finished segdata, begin segtext */
+
/* read-only data */
sect = addsection(&segtext, ".rodata", 04);
sect->vaddr = 0;
+ lookup("rodata", 0)->sect = sect;
+ lookup("erodata", 0)->sect = sect;
datsize = 0;
s = datap;
- for(; s != nil && s->type < SSYMTAB; s = s->next) {
+ for(; s != nil && s->type < STYPELINK; s = s->next) {
+ s->sect = sect;
if(s->align != 0)
datsize = rnd(datsize, s->align);
s->type = SRODATA;
@@ -864,11 +1158,57 @@ dodata(void)
datsize += rnd(s->size, PtrSize);
}
sect->len = datsize - sect->vaddr;
+ datsize = rnd(datsize, PtrSize);
+
+ /* type */
+ sect = addsection(&segtext, ".typelink", 04);
+ sect->vaddr = datsize;
+ lookup("typelink", 0)->sect = sect;
+ lookup("etypelink", 0)->sect = sect;
+ for(; s != nil && s->type == STYPELINK; s = s->next) {
+ s->sect = sect;
+ s->type = SRODATA;
+ s->value = datsize;
+ datsize += s->size;
+ }
+ sect->len = datsize - sect->vaddr;
+ datsize = rnd(datsize, PtrSize);
+
+ /* gcdata */
+ sect = addsection(&segtext, ".gcdata", 04);
+ sect->vaddr = datsize;
+ lookup("gcdata", 0)->sect = sect;
+ lookup("egcdata", 0)->sect = sect;
+ for(; s != nil && s->type == SGCDATA; s = s->next) {
+ s->sect = sect;
+ s->type = SRODATA;
+ s->value = datsize;
+ datsize += s->size;
+ }
+ sect->len = datsize - sect->vaddr;
+ datsize = rnd(datsize, PtrSize);
+
+ /* gcbss */
+ sect = addsection(&segtext, ".gcbss", 04);
+ sect->vaddr = datsize;
+ lookup("gcbss", 0)->sect = sect;
+ lookup("egcbss", 0)->sect = sect;
+ for(; s != nil && s->type == SGCBSS; s = s->next) {
+ s->sect = sect;
+ s->type = SRODATA;
+ s->value = datsize;
+ datsize += s->size;
+ }
+ sect->len = datsize - sect->vaddr;
+ datsize = rnd(datsize, PtrSize);
/* gosymtab */
sect = addsection(&segtext, ".gosymtab", 04);
sect->vaddr = datsize;
+ lookup("symtab", 0)->sect = sect;
+ lookup("esymtab", 0)->sect = sect;
for(; s != nil && s->type < SPCLNTAB; s = s->next) {
+ s->sect = sect;
s->type = SRODATA;
s->value = datsize;
datsize += s->size;
@@ -879,7 +1219,10 @@ dodata(void)
/* gopclntab */
sect = addsection(&segtext, ".gopclntab", 04);
sect->vaddr = datsize;
+ lookup("pclntab", 0)->sect = sect;
+ lookup("epclntab", 0)->sect = sect;
for(; s != nil && s->type < SELFROSECT; s = s->next) {
+ s->sect = sect;
s->type = SRODATA;
s->value = datsize;
datsize += s->size;
@@ -893,101 +1236,12 @@ dodata(void)
if(s->align != 0)
datsize = rnd(datsize, s->align);
sect->vaddr = datsize;
+ s->sect = sect;
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 < SNOPTRDATA; s = s->next) {
- sect = addsection(&segdata, s->name, 06);
- if(s->align != 0)
- datsize = rnd(datsize, s->align);
- sect->vaddr = datsize;
- s->type = SDATA;
- s->value = datsize;
- datsize += rnd(s->size, PtrSize);
- sect->len = datsize - sect->vaddr;
- }
-
- /* pointer-free data, then data */
- sect = addsection(&segdata, ".noptrdata", 06);
- sect->vaddr = datsize;
- noptr = sect;
- for(; ; s = s->next) {
- if((s == nil || s->type >= SDATA) && sect == noptr) {
- // finish noptrdata, start data
- datsize = rnd(datsize, 8);
- sect->len = datsize - sect->vaddr;
- sect = addsection(&segdata, ".data", 06);
- sect->vaddr = datsize;
- }
- if(s == nil || s->type >= SBSS) {
- // finish data
- sect->len = datsize - sect->vaddr;
- break;
- }
- s->type = SDATA;
- t = s->size;
- if(t >= PtrSize)
- t = rnd(t, PtrSize);
- else if(t > 2)
- t = rnd(t, 4);
- if(s->align != 0)
- datsize = rnd(datsize, s->align);
- else 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;
- }
-
- /* bss, then pointer-free bss */
- noptr = nil;
- sect = addsection(&segdata, ".bss", 06);
- sect->vaddr = datsize;
- for(; ; s = s->next) {
- if((s == nil || s->type >= SNOPTRBSS) && noptr == nil) {
- // finish bss, start noptrbss
- datsize = rnd(datsize, 8);
- sect->len = datsize - sect->vaddr;
- sect = addsection(&segdata, ".noptrbss", 06);
- sect->vaddr = datsize;
- noptr = sect;
- }
- if(s == nil) {
- sect->len = datsize - sect->vaddr;
- break;
- }
- if(s->type > SNOPTRBSS) {
- 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(s->align != 0)
- datsize = rnd(datsize, s->align);
- else 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;
- }
}
// assign addresses to text
@@ -1002,16 +1256,21 @@ textaddress(void)
addsection(&segtext, ".text", 05);
// Assign PCs in text segment.
- // Could parallelize, by assigning to text
+ // Could parallelize, by assigning to text
// and then letting threads copy down, but probably not worth it.
sect = segtext.sect;
+ lookup("text", 0)->sect = sect;
+ lookup("etext", 0)->sect = sect;
va = INITTEXT;
sect->vaddr = va;
for(sym = textp; sym != nil; sym = sym->next) {
+ sym->sect = sect;
if(sym->type & SSUB)
continue;
if(sym->align != 0)
va = rnd(va, sym->align);
+ else if(sym->text != P)
+ va = rnd(va, FuncAlign);
sym->value = 0;
for(sub = sym; sub != S; sub = sub->sub) {
sub->value += va;
@@ -1023,7 +1282,7 @@ textaddress(void)
}
va += sym->size;
}
-
+
// Align end of code so that rodata starts aligned.
// 128 bytes is likely overkill but definitely cheap.
va = rnd(va, 128);
@@ -1035,7 +1294,8 @@ textaddress(void)
void
address(void)
{
- Section *s, *text, *data, *rodata, *symtab, *pclntab, *noptr, *bss, *noptrbss;
+ Section *s, *text, *data, *rodata, *symtab, *pclntab, *noptr, *bss, *noptrbss, *datarelro;
+ Section *gcdata, *gcbss, *typelink;
Sym *sym, *sub;
uvlong va;
@@ -1058,12 +1318,13 @@ address(void)
segdata.filelen = 0;
if(HEADTYPE == Hwindows)
segdata.fileoff = segtext.fileoff + rnd(segtext.len, PEFILEALIGN);
- if(HEADTYPE == Hplan9x32)
+ if(HEADTYPE == Hplan9x64 || HEADTYPE == Hplan9x32)
segdata.fileoff = segtext.fileoff + segtext.filelen;
data = nil;
noptr = nil;
bss = nil;
noptrbss = nil;
+ datarelro = nil;
for(s=segdata.sect; s != nil; s=s->next) {
s->vaddr = va;
va += s->len;
@@ -1077,12 +1338,17 @@ address(void)
bss = s;
if(strcmp(s->name, ".noptrbss") == 0)
noptrbss = s;
+ if(strcmp(s->name, ".data.rel.ro") == 0)
+ datarelro = s;
}
segdata.filelen -= bss->len + noptrbss->len; // deduct .bss
text = segtext.sect;
rodata = text->next;
- symtab = rodata->next;
+ typelink = rodata->next;
+ gcdata = typelink->next;
+ gcbss = gcdata->next;
+ symtab = gcbss->next;
pclntab = symtab->next;
for(sym = datap; sym != nil; sym = sym->next) {
@@ -1094,11 +1360,21 @@ address(void)
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("typelink", SRODATA, typelink->vaddr);
+ xdefine("etypelink", SRODATA, typelink->vaddr + typelink->len);
+ if(datarelro != nil) {
+ xdefine("datarelro", SRODATA, datarelro->vaddr);
+ xdefine("edatarelro", SRODATA, datarelro->vaddr + datarelro->len);
+ }
+ xdefine("gcdata", SGCDATA, gcdata->vaddr);
+ xdefine("egcdata", SGCDATA, gcdata->vaddr + gcdata->len);
+ xdefine("gcbss", SGCBSS, gcbss->vaddr);
+ xdefine("egcbss", SGCBSS, gcbss->vaddr + gcbss->len);
xdefine("symtab", SRODATA, symtab->vaddr);
xdefine("esymtab", SRODATA, symtab->vaddr + symtab->len);
xdefine("pclntab", SRODATA, pclntab->vaddr);
diff --git a/src/cmd/ld/decodesym.c b/src/cmd/ld/decodesym.c
new file mode 100644
index 000000000..ab3f4fbd5
--- /dev/null
+++ b/src/cmd/ld/decodesym.c
@@ -0,0 +1,215 @@
+// Copyright 2012 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 "../../pkg/runtime/typekind.h"
+
+// 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.
+
+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
+uint8
+decodetype_kind(Sym *s)
+{
+ return s->p[1*PtrSize + 7] & ~KindNoPointers; // 0x13 / 0x1f
+}
+
+// Type.commonType.size
+vlong
+decodetype_size(Sym *s)
+{
+ return decode_inuxi(s->p, PtrSize); // 0x8 / 0x10
+}
+
+// Type.commonType.gc
+Sym*
+decodetype_gc(Sym *s)
+{
+ return decode_reloc_sym(s, 1*PtrSize + 8 + 1*PtrSize);
+}
+
+// Type.ArrayType.elem and Type.SliceType.Elem
+Sym*
+decodetype_arrayelem(Sym *s)
+{
+ return decode_reloc_sym(s, CommonSize); // 0x1c / 0x30
+}
+
+vlong
+decodetype_arraylen(Sym *s)
+{
+ return decode_inuxi(s->p + CommonSize+PtrSize, PtrSize);
+}
+
+// Type.PtrType.elem
+Sym*
+decodetype_ptrelem(Sym *s)
+{
+ return decode_reloc_sym(s, CommonSize); // 0x1c / 0x30
+}
+
+// Type.MapType.key, elem
+Sym*
+decodetype_mapkey(Sym *s)
+{
+ return decode_reloc_sym(s, CommonSize); // 0x1c / 0x30
+}
+Sym*
+decodetype_mapvalue(Sym *s)
+{
+ return decode_reloc_sym(s, CommonSize+PtrSize); // 0x20 / 0x38
+}
+
+// Type.ChanType.elem
+Sym*
+decodetype_chanelem(Sym *s)
+{
+ return decode_reloc_sym(s, CommonSize); // 0x1c / 0x30
+}
+
+// Type.FuncType.dotdotdot
+int
+decodetype_funcdotdotdot(Sym *s)
+{
+ return s->p[CommonSize];
+}
+
+// Type.FuncType.in.len
+int
+decodetype_funcincount(Sym *s)
+{
+ return decode_inuxi(s->p + CommonSize+2*PtrSize, IntSize);
+}
+
+int
+decodetype_funcoutcount(Sym *s)
+{
+ return decode_inuxi(s->p + CommonSize+3*PtrSize + 2*IntSize, IntSize);
+}
+
+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);
+}
+
+Sym*
+decodetype_funcouttype(Sym *s, int i)
+{
+ Reloc *r;
+
+ r = decode_reloc(s, CommonSize + 2*PtrSize + 2*IntSize);
+ if (r == nil)
+ return nil;
+ return decode_reloc_sym(r->sym, r->add + i * PtrSize);
+}
+
+// Type.StructType.fields.Slice::len
+int
+decodetype_structfieldcount(Sym *s)
+{
+ return decode_inuxi(s->p + CommonSize + PtrSize, IntSize);
+}
+
+enum {
+ StructFieldSize = 5*PtrSize
+};
+// Type.StructType.fields[]-> name, typ and offset.
+char*
+decodetype_structfieldname(Sym *s, int i)
+{
+ Reloc *r;
+
+ // go.string."foo" 0x28 / 0x40
+ s = decode_reloc_sym(s, CommonSize + PtrSize + 2*IntSize + 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
+}
+
+Sym*
+decodetype_structfieldtype(Sym *s, int i)
+{
+ return decode_reloc_sym(s, CommonSize + PtrSize + 2*IntSize + i*StructFieldSize + 2*PtrSize);
+}
+
+vlong
+decodetype_structfieldoffs(Sym *s, int i)
+{
+ return decode_inuxi(s->p + CommonSize + PtrSize + 2*IntSize + i*StructFieldSize + 4*PtrSize, IntSize);
+}
+
+// InterfaceTYpe.methods.len
+vlong
+decodetype_ifacemethodcount(Sym *s)
+{
+ return decode_inuxi(s->p + CommonSize + PtrSize, IntSize);
+}
diff --git a/src/cmd/ld/doc.go b/src/cmd/ld/doc.go
index e99e50466..bad4e540f 100644
--- a/src/cmd/ld/doc.go
+++ b/src/cmd/ld/doc.go
@@ -2,11 +2,13 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build ignore
+
/*
Ld is the portable code for a modified version of the Plan 9 linker. The original is documented at
- http://plan9.bell-labs.com/magic/man2html/1/2l
+ http://plan9.bell-labs.com/magic/man2html/1/8l
It reads object files (.5, .6, or .8 files) and writes a binary named for the
architecture (5.out, 6.out, 8.out) by default (if $GOOS is windows, a .exe suffix
@@ -33,7 +35,7 @@ Options new in this version:
Write Apple Mach-O binaries (default when $GOOS is darwin)
-Hlinux
Write Linux ELF binaries (default when $GOOS is linux)
- -Hfreebsd (only in 6l/8l)
+ -Hfreebsd
Write FreeBSD ELF binaries (default when $GOOS is freebsd)
-Hnetbsd (only in 6l/8l)
Write NetBSD ELF binaries (default when $GOOS is netbsd)
@@ -56,5 +58,18 @@ Options new in this version:
Set the value of an otherwise uninitialized string variable.
The symbol name should be of the form importpath.name,
as displayed in the symbol table printed by "go tool nm".
+ -race
+ Link with race detection libraries.
+ -B value
+ Add a NT_GNU_BUILD_ID note when using ELF. The value
+ should start with 0x and be an even number of hex digits.
+ -Z
+ Zero stack on function entry. This is expensive but it might
+ be useful in cases where you are suffering from false positives
+ during garbage collection and are willing to trade the CPU time
+ for getting rid of the false positives.
+ NOTE: it only eliminates false positives caused by other function
+ calls, not false positives caused by dead temporaries stored in
+ the current function call.
*/
-package documentation
+package main
diff --git a/src/cmd/ld/dwarf.c b/src/cmd/ld/dwarf.c
index 57e5a4283..d6a357e49 100644
--- a/src/cmd/ld/dwarf.c
+++ b/src/cmd/ld/dwarf.c
@@ -19,6 +19,7 @@
#include "../ld/elf.h"
#include "../ld/macho.h"
#include "../ld/pe.h"
+#include "../../pkg/runtime/typekind.h"
/*
* Offsets and sizes of the debug_* sections in the cout file.
@@ -740,241 +741,6 @@ newabslocexprattr(DWDie *die, vlong addr)
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+ 5*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 {
@@ -1531,6 +1297,10 @@ decodez(char *s)
return 0;
r = malloc(len + 1);
+ if(r == nil) {
+ diag("out of memory");
+ errorexit();
+ }
rb = r;
re = rb + len + 1;
@@ -1709,6 +1479,10 @@ inithist(Auto *a)
continue;
if (linehist == 0 || linehist->absline != absline) {
Linehist* lh = malloc(sizeof *lh);
+ if(lh == nil) {
+ diag("out of memory");
+ errorexit();
+ }
lh->link = linehist;
lh->absline = absline;
linehist = lh;
@@ -2162,7 +1936,8 @@ writeinfo(void)
* because we need die->offs and infoo/infosize;
*/
static int
-ispubname(DWDie *die) {
+ispubname(DWDie *die)
+{
DWAttr *a;
switch(die->abbrev) {
@@ -2175,7 +1950,8 @@ ispubname(DWDie *die) {
}
static int
-ispubtype(DWDie *die) {
+ispubtype(DWDie *die)
+{
return die->abbrev >= DW_ABRV_NULLTYPE;
}
@@ -2316,7 +2092,7 @@ dwarfemitdebugsections(void)
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.rtype"));
defgotype(lookup_or_diag("type.runtime.interfaceType"));
defgotype(lookup_or_diag("type.runtime.itab"));
diff --git a/src/cmd/ld/elf.c b/src/cmd/ld/elf.c
index de9e6b854..630906653 100644
--- a/src/cmd/ld/elf.c
+++ b/src/cmd/ld/elf.c
@@ -11,7 +11,7 @@
* 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
+#define NSECT 48
int iself;
@@ -31,6 +31,8 @@ struct Elfstring
static Elfstring elfstr[100];
static int nelfstr;
+static char buildinfo[32];
+
/*
Initialize the global variable that describes the ELF header. It will be updated as
we write section and prog headers.
@@ -52,6 +54,11 @@ elfinit(void)
break;
// 32-bit architectures
+ case '5':
+ // we only use EABI on linux/arm
+ if(HEADTYPE == Hlinux)
+ hdr.flags = 0x5000002; // has entry point, Version5 EABI
+ // fallthrough
default:
hdr.phoff = ELF32HDRSIZE; /* Must be be ELF32HDRSIZE: first PHdr must follow ELF header */
hdr.shoff = ELF32HDRSIZE; /* Will move as we add PHeaders */
@@ -190,19 +197,13 @@ newElfPhdr(void)
}
ElfShdr*
-newElfShstrtab(vlong name)
-{
- hdr.shstrndx = hdr.shnum;
- return newElfShdr(name);
-}
-
-ElfShdr*
newElfShdr(vlong name)
{
ElfShdr *e;
e = mal(sizeof *e);
e->name = name;
+ e->shnum = hdr.shnum;
if (hdr.shnum >= NSECT) {
diag("too many shdrs");
} else {
@@ -332,26 +333,53 @@ elfinterp(ElfShdr *sh, uint64 startva, uint64 resoff, char *p)
}
int
-elfwriteinterp(vlong stridx)
+elfwriteinterp(void)
{
- ElfShdr *sh = nil;
- int i;
-
- for(i = 0; i < hdr.shnum; i++)
- if(shdr[i]->name == stridx)
- sh = shdr[i];
- if(sh == nil || interp == nil)
- return 0;
-
+ ElfShdr *sh;
+
+ sh = elfshname(".interp");
cseek(sh->off);
cwrite(interp, sh->size);
return sh->size;
}
-// Defined in NetBSD's sys/exec_elf.h
-#define ELF_NOTE_TYPE_NETBSD_TAG 1
+int
+elfnote(ElfShdr *sh, uint64 startva, uint64 resoff, int sz)
+{
+ uint64 n;
+
+ n = sizeof(Elf_Note) + sz + resoff % 4;
+
+ sh->type = SHT_NOTE;
+ sh->flags = SHF_ALLOC;
+ sh->addralign = 4;
+ sh->addr = startva + resoff - n;
+ sh->off = resoff - n;
+ sh->size = n;
+
+ return n;
+}
+
+ElfShdr *
+elfwritenotehdr(char *str, uint32 namesz, uint32 descsz, uint32 tag)
+{
+ ElfShdr *sh;
+
+ sh = elfshname(str);
+
+ // Write Elf_Note header.
+ cseek(sh->off);
+ LPUT(namesz);
+ LPUT(descsz);
+ LPUT(tag);
+
+ return sh;
+}
+
+// NetBSD Signature (as per sys/exec_elf.h)
#define ELF_NOTE_NETBSD_NAMESZ 7
#define ELF_NOTE_NETBSD_DESCSZ 4
+#define ELF_NOTE_NETBSD_TAG 1
#define ELF_NOTE_NETBSD_NAME "NetBSD\0\0"
#define ELF_NOTE_NETBSD_VERSION 599000000 /* NetBSD 5.99 */
@@ -360,37 +388,131 @@ elfnetbsdsig(ElfShdr *sh, uint64 startva, uint64 resoff)
{
int n;
- n = sizeof(Elf_Note) + ELF_NOTE_NETBSD_NAMESZ + ELF_NOTE_NETBSD_DESCSZ + 1;
- n += resoff % 4;
- sh->addr = startva + resoff - n;
- sh->off = resoff - n;
- sh->size = n;
-
- return n;
+ n = ELF_NOTE_NETBSD_NAMESZ + ELF_NOTE_NETBSD_DESCSZ + 1;
+ return elfnote(sh, startva, resoff, n);
}
int
-elfwritenetbsdsig(vlong stridx) {
- ElfShdr *sh = nil;
- int i;
+elfwritenetbsdsig(void)
+{
+ ElfShdr *sh;
- for(i = 0; i < hdr.shnum; i++)
- if(shdr[i]->name == stridx)
- sh = shdr[i];
+ // Write Elf_Note header.
+ sh = elfwritenotehdr(".note.netbsd.ident", ELF_NOTE_NETBSD_NAMESZ, ELF_NOTE_NETBSD_DESCSZ, ELF_NOTE_NETBSD_TAG);
if(sh == nil)
return 0;
- // Write Elf_Note header followed by NetBSD string.
- cseek(sh->off);
- LPUT(ELF_NOTE_NETBSD_NAMESZ);
- LPUT(ELF_NOTE_NETBSD_DESCSZ);
- LPUT(ELF_NOTE_TYPE_NETBSD_TAG);
- cwrite(ELF_NOTE_NETBSD_NAME, 8);
+ // Followed by NetBSD string and version.
+ cwrite(ELF_NOTE_NETBSD_NAME, ELF_NOTE_NETBSD_NAMESZ + 1);
LPUT(ELF_NOTE_NETBSD_VERSION);
return sh->size;
}
+// OpenBSD Signature
+#define ELF_NOTE_OPENBSD_NAMESZ 8
+#define ELF_NOTE_OPENBSD_DESCSZ 4
+#define ELF_NOTE_OPENBSD_TAG 1
+#define ELF_NOTE_OPENBSD_NAME "OpenBSD\0"
+#define ELF_NOTE_OPENBSD_VERSION 0
+
+int
+elfopenbsdsig(ElfShdr *sh, uint64 startva, uint64 resoff)
+{
+ int n;
+
+ n = ELF_NOTE_OPENBSD_NAMESZ + ELF_NOTE_OPENBSD_DESCSZ;
+ return elfnote(sh, startva, resoff, n);
+}
+
+int
+elfwriteopenbsdsig(void)
+{
+ ElfShdr *sh;
+
+ // Write Elf_Note header.
+ sh = elfwritenotehdr(".note.openbsd.ident", ELF_NOTE_OPENBSD_NAMESZ, ELF_NOTE_OPENBSD_DESCSZ, ELF_NOTE_OPENBSD_TAG);
+ if(sh == nil)
+ return 0;
+
+ // Followed by OpenBSD string and version.
+ cwrite(ELF_NOTE_OPENBSD_NAME, ELF_NOTE_OPENBSD_NAMESZ);
+ LPUT(ELF_NOTE_OPENBSD_VERSION);
+
+ return sh->size;
+}
+
+void
+addbuildinfo(char *val)
+{
+ char *ov;
+ int i, b, j;
+
+ if(val[0] != '0' || val[1] != 'x') {
+ fprint(2, "%s: -B argument must start with 0x: %s\n", argv0, val);
+ exits("usage");
+ }
+ ov = val;
+ val += 2;
+ i = 0;
+ while(*val != '\0') {
+ if(val[1] == '\0') {
+ fprint(2, "%s: -B argument must have even number of digits: %s\n", argv0, ov);
+ exits("usage");
+ }
+ b = 0;
+ for(j = 0; j < 2; j++, val++) {
+ b *= 16;
+ if(*val >= '0' && *val <= '9')
+ b += *val - '0';
+ else if(*val >= 'a' && *val <= 'f')
+ b += *val - 'a' + 10;
+ else if(*val >= 'A' && *val <= 'F')
+ b += *val - 'A' + 10;
+ else {
+ fprint(2, "%s: -B argument contains invalid hex digit %c: %s\n", argv0, *val, ov);
+ exits("usage");
+ }
+ }
+ if(i >= nelem(buildinfo)) {
+ fprint(2, "%s: -B option too long (max %d digits): %s\n", argv0, (int)nelem(buildinfo), ov);
+ exits("usage");
+ }
+ buildinfo[i++] = b;
+ }
+ buildinfolen = i;
+}
+
+// Build info note
+#define ELF_NOTE_BUILDINFO_NAMESZ 4
+#define ELF_NOTE_BUILDINFO_TAG 3
+#define ELF_NOTE_BUILDINFO_NAME "GNU\0"
+
+int
+elfbuildinfo(ElfShdr *sh, uint64 startva, uint64 resoff)
+{
+ int n;
+
+ n = ELF_NOTE_BUILDINFO_NAMESZ + rnd(buildinfolen, 4);
+ return elfnote(sh, startva, resoff, n);
+}
+
+int
+elfwritebuildinfo(void)
+{
+ ElfShdr *sh;
+
+ sh = elfwritenotehdr(".note.gnu.build-id", ELF_NOTE_BUILDINFO_NAMESZ, buildinfolen, ELF_NOTE_BUILDINFO_TAG);
+ if(sh == nil)
+ return 0;
+
+ cwrite(ELF_NOTE_BUILDINFO_NAME, ELF_NOTE_BUILDINFO_NAMESZ);
+ cwrite(buildinfo, buildinfolen);
+ cwrite("\0\0\0", rnd(buildinfolen, 4) - buildinfolen);
+
+ return sh->size;
+}
+
extern int nelfsym;
int elfverneed;
@@ -584,18 +706,18 @@ elfphload(Segment *seg)
}
ElfShdr*
-elfshbits(Section *sect)
+elfshname(char *name)
{
int i, off;
ElfShdr *sh;
for(i=0; i<nelfstr; i++) {
- if(strcmp(sect->name, elfstr[i].s) == 0) {
+ if(strcmp(name, elfstr[i].s) == 0) {
off = elfstr[i].off;
goto found;
}
}
- diag("cannot find elf name %s", sect->name);
+ diag("cannot find elf name %s", name);
errorexit();
return nil;
@@ -605,8 +727,30 @@ found:
if(sh->name == off)
return sh;
}
-
+
sh = newElfShdr(off);
+ return sh;
+}
+
+ElfShdr*
+elfshalloc(Section *sect)
+{
+ ElfShdr *sh;
+
+ sh = elfshname(sect->name);
+ sect->elfsect = sh;
+ return sh;
+}
+
+ElfShdr*
+elfshbits(Section *sect)
+{
+ ElfShdr *sh;
+
+ sh = elfshalloc(sect);
+ if(sh->type > 0)
+ return sh;
+
if(sect->vaddr < sect->seg->vaddr + sect->seg->filelen)
sh->type = SHT_PROGBITS;
else
@@ -616,10 +760,732 @@ found:
sh->flags |= SHF_EXECINSTR;
if(sect->rwx & 2)
sh->flags |= SHF_WRITE;
- sh->addr = sect->vaddr;
+ if(!isobj)
+ sh->addr = sect->vaddr;
sh->addralign = PtrSize;
sh->size = sect->len;
sh->off = sect->seg->fileoff + sect->vaddr - sect->seg->vaddr;
+
+ return sh;
+}
+
+ElfShdr*
+elfshreloc(Section *sect)
+{
+ int typ;
+ ElfShdr *sh;
+ char *prefix;
+ char buf[100];
+ // If main section is SHT_NOBITS, nothing to relocate.
+ // Also nothing to relocate in .shstrtab.
+ if(sect->vaddr >= sect->seg->vaddr + sect->seg->filelen)
+ return nil;
+ if(strcmp(sect->name, ".shstrtab") == 0)
+ return nil;
+
+ if(thechar == '6') {
+ prefix = ".rela";
+ typ = SHT_RELA;
+ } else {
+ prefix = ".rel";
+ typ = SHT_REL;
+ }
+
+ snprint(buf, sizeof buf, "%s%s", prefix, sect->name);
+ sh = elfshname(buf);
+ sh->type = typ;
+ sh->entsize = PtrSize*(2+(typ==SHT_RELA));
+ sh->link = elfshname(".symtab")->shnum;
+ sh->info = sect->elfsect->shnum;
+ sh->off = sect->reloff;
+ sh->size = sect->rellen;
+ sh->addralign = PtrSize;
return sh;
}
+
+void
+elfrelocsect(Section *sect, Sym *first)
+{
+ Sym *sym, *rs;
+ int32 eaddr;
+ Reloc *r;
+ int64 add;
+
+ // If main section is SHT_NOBITS, nothing to relocate.
+ // Also nothing to relocate in .shstrtab.
+ if(sect->vaddr >= sect->seg->vaddr + sect->seg->filelen)
+ return;
+ if(strcmp(sect->name, ".shstrtab") == 0)
+ return;
+
+ sect->reloff = cpos();
+ for(sym = first; sym != nil; sym = sym->next) {
+ if(!sym->reachable)
+ continue;
+ if(sym->value >= sect->vaddr)
+ break;
+ }
+
+ eaddr = sect->vaddr + sect->len;
+ for(; sym != nil; sym = sym->next) {
+ if(!sym->reachable)
+ continue;
+ if(sym->value >= eaddr)
+ break;
+ cursym = sym;
+
+ for(r = sym->r; r < sym->r+sym->nr; r++) {
+ // Ignore relocations handled by reloc already.
+ switch(r->type) {
+ case D_SIZE:
+ continue;
+ case D_ADDR:
+ case D_PCREL:
+ if(r->sym->type == SCONST)
+ continue;
+ break;
+ }
+
+ add = r->add;
+ rs = r->sym;
+ while(rs->outer != nil) {
+ add += rs->value - rs->outer->value;
+ rs = rs->outer;
+ }
+
+ if(rs->elfsym == 0)
+ diag("reloc %d to non-elf symbol %s (rs=%s) %d", r->type, r->sym->name, rs->name, rs->type);
+
+ if(elfreloc1(r, sym->value - sect->vaddr + r->off, rs->elfsym, add) < 0)
+ diag("unsupported obj reloc %d/%d to %s", r->type, r->siz, r->sym->name);
+ }
+ }
+
+ sect->rellen = cpos() - sect->reloff;
+}
+
+void
+elfemitreloc(void)
+{
+ Section *sect;
+
+ while(cpos()&7)
+ cput(0);
+
+ elfrelocsect(segtext.sect, textp);
+ for(sect=segtext.sect->next; sect!=nil; sect=sect->next)
+ elfrelocsect(sect, datap);
+ for(sect=segdata.sect; sect!=nil; sect=sect->next)
+ elfrelocsect(sect, datap);
+}
+
+void
+doelf(void)
+{
+ Sym *s, *shstrtab, *dynstr;
+
+ if(!iself)
+ return;
+
+ /* predefine strings we need for section headers */
+ shstrtab = lookup(".shstrtab", 0);
+ shstrtab->type = SELFROSECT;
+ shstrtab->reachable = 1;
+
+ addstring(shstrtab, "");
+ addstring(shstrtab, ".text");
+ addstring(shstrtab, ".noptrdata");
+ addstring(shstrtab, ".data");
+ addstring(shstrtab, ".bss");
+ addstring(shstrtab, ".noptrbss");
+ if(HEADTYPE == Hnetbsd)
+ addstring(shstrtab, ".note.netbsd.ident");
+ if(HEADTYPE == Hopenbsd)
+ addstring(shstrtab, ".note.openbsd.ident");
+ if(buildinfolen > 0)
+ addstring(shstrtab, ".note.gnu.build-id");
+ addstring(shstrtab, ".elfdata");
+ addstring(shstrtab, ".rodata");
+ addstring(shstrtab, ".typelink");
+ if(flag_shared)
+ addstring(shstrtab, ".data.rel.ro");
+ addstring(shstrtab, ".gcdata");
+ addstring(shstrtab, ".gcbss");
+ addstring(shstrtab, ".gosymtab");
+ addstring(shstrtab, ".gopclntab");
+
+ if(isobj) {
+ debug['s'] = 0;
+ debug['d'] = 1;
+
+ if(thechar == '6') {
+ addstring(shstrtab, ".rela.text");
+ addstring(shstrtab, ".rela.rodata");
+ addstring(shstrtab, ".rela.typelink");
+ addstring(shstrtab, ".rela.gcdata");
+ addstring(shstrtab, ".rela.gcbss");
+ addstring(shstrtab, ".rela.gosymtab");
+ addstring(shstrtab, ".rela.gopclntab");
+ addstring(shstrtab, ".rela.noptrdata");
+ addstring(shstrtab, ".rela.data");
+ } else {
+ addstring(shstrtab, ".rel.text");
+ addstring(shstrtab, ".rel.rodata");
+ addstring(shstrtab, ".rel.typelink");
+ addstring(shstrtab, ".rel.gcdata");
+ addstring(shstrtab, ".rel.gcbss");
+ addstring(shstrtab, ".rel.gosymtab");
+ addstring(shstrtab, ".rel.gopclntab");
+ addstring(shstrtab, ".rel.noptrdata");
+ addstring(shstrtab, ".rel.data");
+ }
+ }
+
+ if(!debug['s']) {
+ addstring(shstrtab, ".symtab");
+ addstring(shstrtab, ".strtab");
+ dwarfaddshstrings(shstrtab);
+ }
+ addstring(shstrtab, ".shstrtab");
+
+ if(!debug['d']) { /* -d suppresses dynamic loader format */
+ addstring(shstrtab, ".interp");
+ addstring(shstrtab, ".hash");
+ addstring(shstrtab, ".got");
+ addstring(shstrtab, ".got.plt");
+ addstring(shstrtab, ".dynamic");
+ addstring(shstrtab, ".dynsym");
+ addstring(shstrtab, ".dynstr");
+ if(thechar == '6') {
+ addstring(shstrtab, ".rela");
+ addstring(shstrtab, ".rela.plt");
+ } else {
+ addstring(shstrtab, ".rel");
+ addstring(shstrtab, ".rel.plt");
+ }
+ addstring(shstrtab, ".plt");
+ addstring(shstrtab, ".gnu.version");
+ addstring(shstrtab, ".gnu.version_r");
+
+ /* dynamic symbol table - first entry all zeros */
+ s = lookup(".dynsym", 0);
+ s->type = SELFROSECT;
+ s->reachable = 1;
+ if(thechar == '6')
+ s->size += ELF64SYMSIZE;
+ else
+ s->size += ELF32SYMSIZE;
+
+ /* dynamic string table */
+ s = lookup(".dynstr", 0);
+ s->type = SELFROSECT;
+ s->reachable = 1;
+ if(s->size == 0)
+ addstring(s, "");
+ dynstr = s;
+
+ /* relocation table */
+ if(thechar == '6')
+ s = lookup(".rela", 0);
+ else
+ s = lookup(".rel", 0);
+ s->reachable = 1;
+ s->type = SELFROSECT;
+
+ /* global offset table */
+ s = lookup(".got", 0);
+ s->reachable = 1;
+ s->type = SELFSECT; // writable
+
+ /* hash */
+ s = lookup(".hash", 0);
+ s->reachable = 1;
+ s->type = SELFROSECT;
+
+ s = lookup(".got.plt", 0);
+ s->reachable = 1;
+ s->type = SELFSECT; // writable
+
+ s = lookup(".plt", 0);
+ s->reachable = 1;
+ s->type = SELFROSECT;
+
+ elfsetupplt();
+
+ if(thechar == '6')
+ s = lookup(".rela.plt", 0);
+ else
+ s = lookup(".rel.plt", 0);
+ s->reachable = 1;
+ s->type = SELFROSECT;
+
+ s = lookup(".gnu.version", 0);
+ s->reachable = 1;
+ s->type = SELFROSECT;
+
+ s = lookup(".gnu.version_r", 0);
+ s->reachable = 1;
+ s->type = SELFROSECT;
+
+ /* define dynamic elf table */
+ s = lookup(".dynamic", 0);
+ s->reachable = 1;
+ s->type = SELFSECT; // writable
+
+ /*
+ * .dynamic table
+ */
+ elfwritedynentsym(s, DT_HASH, lookup(".hash", 0));
+ elfwritedynentsym(s, DT_SYMTAB, lookup(".dynsym", 0));
+ if(thechar == '6')
+ elfwritedynent(s, DT_SYMENT, ELF64SYMSIZE);
+ else
+ elfwritedynent(s, DT_SYMENT, ELF32SYMSIZE);
+ elfwritedynentsym(s, DT_STRTAB, lookup(".dynstr", 0));
+ elfwritedynentsymsize(s, DT_STRSZ, lookup(".dynstr", 0));
+ if(thechar == '6') {
+ elfwritedynentsym(s, DT_RELA, lookup(".rela", 0));
+ elfwritedynentsymsize(s, DT_RELASZ, lookup(".rela", 0));
+ elfwritedynent(s, DT_RELAENT, ELF64RELASIZE);
+ } else {
+ elfwritedynentsym(s, DT_REL, lookup(".rel", 0));
+ elfwritedynentsymsize(s, DT_RELSZ, lookup(".rel", 0));
+ elfwritedynent(s, DT_RELENT, ELF32RELSIZE);
+ }
+ if(rpath)
+ elfwritedynent(s, DT_RUNPATH, addstring(dynstr, rpath));
+
+ elfwritedynentsym(s, DT_PLTGOT, lookup(".got.plt", 0));
+
+ if(thechar == '6') {
+ elfwritedynent(s, DT_PLTREL, DT_RELA);
+ elfwritedynentsymsize(s, DT_PLTRELSZ, lookup(".rela.plt", 0));
+ elfwritedynentsym(s, DT_JMPREL, lookup(".rela.plt", 0));
+ } else {
+ elfwritedynent(s, DT_PLTREL, DT_REL);
+ elfwritedynentsymsize(s, DT_PLTRELSZ, lookup(".rel.plt", 0));
+ elfwritedynentsym(s, DT_JMPREL, lookup(".rel.plt", 0));
+ }
+
+ elfwritedynent(s, DT_DEBUG, 0);
+
+ if(flag_shared) {
+ Sym *init_sym = lookup(LIBINITENTRY, 0);
+ if(init_sym->type != STEXT)
+ diag("entry not text: %s", init_sym->name);
+ elfwritedynentsym(s, DT_INIT, init_sym);
+ }
+
+ // Do not write DT_NULL. elfdynhash will finish it.
+ }
+}
+
+void
+shsym(ElfShdr *sh, Sym *s)
+{
+ vlong addr;
+ addr = symaddr(s);
+ if(sh->flags&SHF_ALLOC)
+ sh->addr = addr;
+ sh->off = datoff(addr);
+ sh->size = s->size;
+}
+
+void
+phsh(ElfPhdr *ph, ElfShdr *sh)
+{
+ ph->vaddr = sh->addr;
+ ph->paddr = ph->vaddr;
+ ph->off = sh->off;
+ ph->filesz = sh->size;
+ ph->memsz = sh->size;
+ ph->align = sh->addralign;
+}
+
+void
+asmbelfsetup(void)
+{
+ Section *sect;
+
+ /* This null SHdr must appear before all others */
+ elfshname("");
+
+ for(sect=segtext.sect; sect!=nil; sect=sect->next)
+ elfshalloc(sect);
+ for(sect=segdata.sect; sect!=nil; sect=sect->next)
+ elfshalloc(sect);
+}
+
+void
+asmbelf(vlong symo)
+{
+ int a, o;
+ vlong startva, resoff;
+ ElfEhdr *eh;
+ ElfPhdr *ph, *pph, *pnote;
+ ElfShdr *sh;
+ Section *sect;
+
+ eh = getElfEhdr();
+ switch(thechar) {
+ default:
+ diag("unknown architecture in asmbelf");
+ errorexit();
+ case '5':
+ eh->machine = EM_ARM;
+ break;
+ case '6':
+ eh->machine = EM_X86_64;
+ break;
+ case '8':
+ eh->machine = EM_386;
+ break;
+ }
+
+ startva = INITTEXT - HEADR;
+ resoff = ELFRESERVE;
+
+ pph = nil;
+ if(isobj) {
+ /* skip program headers */
+ eh->phoff = 0;
+ eh->phentsize = 0;
+ goto elfobj;
+ }
+
+ /* program header info */
+ pph = newElfPhdr();
+ pph->type = PT_PHDR;
+ pph->flags = PF_R + PF_X;
+ pph->off = eh->ehsize;
+ pph->vaddr = INITTEXT - HEADR + pph->off;
+ pph->paddr = INITTEXT - HEADR + pph->off;
+ pph->align = INITRND;
+
+ /*
+ * PHDR must be in a loaded segment. Adjust the text
+ * segment boundaries downwards to include it.
+ */
+ o = segtext.vaddr - pph->vaddr;
+ segtext.vaddr -= o;
+ segtext.len += o;
+ o = segtext.fileoff - pph->off;
+ segtext.fileoff -= o;
+ segtext.filelen += o;
+
+ if(!debug['d']) {
+ /* interpreter */
+ sh = elfshname(".interp");
+ sh->type = SHT_PROGBITS;
+ sh->flags = SHF_ALLOC;
+ sh->addralign = 1;
+ if(interpreter == nil) {
+ switch(HEADTYPE) {
+ case Hlinux:
+ interpreter = linuxdynld;
+ break;
+ case Hfreebsd:
+ interpreter = freebsddynld;
+ break;
+ case Hnetbsd:
+ interpreter = netbsddynld;
+ break;
+ case Hopenbsd:
+ interpreter = openbsddynld;
+ break;
+ }
+ }
+ resoff -= elfinterp(sh, startva, resoff, interpreter);
+
+ ph = newElfPhdr();
+ ph->type = PT_INTERP;
+ ph->flags = PF_R;
+ phsh(ph, sh);
+ }
+
+ pnote = nil;
+ if(HEADTYPE == Hnetbsd || HEADTYPE == Hopenbsd) {
+ sh = nil;
+ switch(HEADTYPE) {
+ case Hnetbsd:
+ sh = elfshname(".note.netbsd.ident");
+ resoff -= elfnetbsdsig(sh, startva, resoff);
+ break;
+ case Hopenbsd:
+ sh = elfshname(".note.openbsd.ident");
+ resoff -= elfopenbsdsig(sh, startva, resoff);
+ break;
+ }
+
+ pnote = newElfPhdr();
+ pnote->type = PT_NOTE;
+ pnote->flags = PF_R;
+ phsh(pnote, sh);
+ }
+
+ if(buildinfolen > 0) {
+ sh = elfshname(".note.gnu.build-id");
+ resoff -= elfbuildinfo(sh, startva, resoff);
+
+ if(pnote == nil) {
+ pnote = newElfPhdr();
+ pnote->type = PT_NOTE;
+ pnote->flags = PF_R;
+ }
+ phsh(pnote, sh);
+ }
+
+ // Additions to the reserved area must be above this line.
+ USED(resoff);
+
+ elfphload(&segtext);
+ elfphload(&segdata);
+
+ /* Dynamic linking sections */
+ if(!debug['d']) { /* -d suppresses dynamic loader format */
+ sh = elfshname(".dynsym");
+ sh->type = SHT_DYNSYM;
+ sh->flags = SHF_ALLOC;
+ if(PtrSize == 8)
+ sh->entsize = ELF64SYMSIZE;
+ else
+ sh->entsize = ELF32SYMSIZE;
+ sh->addralign = PtrSize;
+ sh->link = elfshname(".dynstr")->shnum;
+ // sh->info = index of first non-local symbol (number of local symbols)
+ shsym(sh, lookup(".dynsym", 0));
+
+ sh = elfshname(".dynstr");
+ sh->type = SHT_STRTAB;
+ sh->flags = SHF_ALLOC;
+ sh->addralign = 1;
+ shsym(sh, lookup(".dynstr", 0));
+
+ if(elfverneed) {
+ sh = elfshname(".gnu.version");
+ sh->type = SHT_GNU_VERSYM;
+ sh->flags = SHF_ALLOC;
+ sh->addralign = 2;
+ sh->link = elfshname(".dynsym")->shnum;
+ sh->entsize = 2;
+ shsym(sh, lookup(".gnu.version", 0));
+
+ sh = elfshname(".gnu.version_r");
+ sh->type = SHT_GNU_VERNEED;
+ sh->flags = SHF_ALLOC;
+ sh->addralign = PtrSize;
+ sh->info = elfverneed;
+ sh->link = elfshname(".dynstr")->shnum;
+ shsym(sh, lookup(".gnu.version_r", 0));
+ }
+
+ switch(eh->machine) {
+ case EM_X86_64:
+ sh = elfshname(".rela.plt");
+ sh->type = SHT_RELA;
+ sh->flags = SHF_ALLOC;
+ sh->entsize = ELF64RELASIZE;
+ sh->addralign = PtrSize;
+ sh->link = elfshname(".dynsym")->shnum;
+ sh->info = elfshname(".plt")->shnum;
+ shsym(sh, lookup(".rela.plt", 0));
+
+ sh = elfshname(".rela");
+ sh->type = SHT_RELA;
+ sh->flags = SHF_ALLOC;
+ sh->entsize = ELF64RELASIZE;
+ sh->addralign = 8;
+ sh->link = elfshname(".dynsym")->shnum;
+ shsym(sh, lookup(".rela", 0));
+ break;
+
+ default:
+ sh = elfshname(".rel.plt");
+ sh->type = SHT_REL;
+ sh->flags = SHF_ALLOC;
+ sh->entsize = ELF32RELSIZE;
+ sh->link = elfshname(".dynsym")->shnum;
+ shsym(sh, lookup(".rel.plt", 0));
+
+ sh = elfshname(".rel");
+ sh->type = SHT_REL;
+ sh->flags = SHF_ALLOC;
+ sh->entsize = ELF32RELSIZE;
+ sh->addralign = 4;
+ sh->link = elfshname(".dynsym")->shnum;
+ shsym(sh, lookup(".rel", 0));
+ break;
+ }
+
+ sh = elfshname(".plt");
+ sh->type = SHT_PROGBITS;
+ sh->flags = SHF_ALLOC+SHF_EXECINSTR;
+ if(eh->machine == EM_X86_64)
+ sh->entsize = 16;
+ else
+ sh->entsize = 4;
+ sh->addralign = 4;
+ shsym(sh, lookup(".plt", 0));
+
+ sh = elfshname(".got");
+ sh->type = SHT_PROGBITS;
+ sh->flags = SHF_ALLOC+SHF_WRITE;
+ sh->entsize = PtrSize;
+ sh->addralign = PtrSize;
+ shsym(sh, lookup(".got", 0));
+
+ sh = elfshname(".got.plt");
+ sh->type = SHT_PROGBITS;
+ sh->flags = SHF_ALLOC+SHF_WRITE;
+ sh->entsize = PtrSize;
+ sh->addralign = PtrSize;
+ shsym(sh, lookup(".got.plt", 0));
+
+ sh = elfshname(".hash");
+ sh->type = SHT_HASH;
+ sh->flags = SHF_ALLOC;
+ sh->entsize = 4;
+ sh->addralign = PtrSize;
+ sh->link = elfshname(".dynsym")->shnum;
+ shsym(sh, lookup(".hash", 0));
+
+ /* sh and PT_DYNAMIC for .dynamic section */
+ sh = elfshname(".dynamic");
+ sh->type = SHT_DYNAMIC;
+ sh->flags = SHF_ALLOC+SHF_WRITE;
+ sh->entsize = 2*PtrSize;
+ sh->addralign = PtrSize;
+ sh->link = elfshname(".dynstr")->shnum;
+ shsym(sh, lookup(".dynamic", 0));
+ ph = newElfPhdr();
+ ph->type = PT_DYNAMIC;
+ ph->flags = PF_R + PF_W;
+ phsh(ph, sh);
+
+ /*
+ * Thread-local storage segment (really just size).
+ */
+ // Do not emit PT_TLS for OpenBSD since ld.so(1) does
+ // not currently support it. This is handled
+ // appropriately in runtime/cgo.
+ if(tlsoffset != 0 && HEADTYPE != Hopenbsd) {
+ ph = newElfPhdr();
+ ph->type = PT_TLS;
+ ph->flags = PF_R;
+ ph->memsz = -tlsoffset;
+ ph->align = PtrSize;
+ }
+ }
+
+ if(HEADTYPE == Hlinux) {
+ ph = newElfPhdr();
+ ph->type = PT_GNU_STACK;
+ ph->flags = PF_W+PF_R;
+ ph->align = PtrSize;
+
+ ph = newElfPhdr();
+ ph->type = PT_PAX_FLAGS;
+ ph->flags = 0x2a00; // mprotect, randexec, emutramp disabled
+ ph->align = PtrSize;
+ }
+
+elfobj:
+ sh = elfshname(".shstrtab");
+ sh->type = SHT_STRTAB;
+ sh->addralign = 1;
+ shsym(sh, lookup(".shstrtab", 0));
+ eh->shstrndx = sh->shnum;
+
+ // put these sections early in the list
+ if(!debug['s']) {
+ elfshname(".symtab");
+ elfshname(".strtab");
+ }
+
+ for(sect=segtext.sect; sect!=nil; sect=sect->next)
+ elfshbits(sect);
+ for(sect=segdata.sect; sect!=nil; sect=sect->next)
+ elfshbits(sect);
+
+ if(isobj) {
+ for(sect=segtext.sect; sect!=nil; sect=sect->next)
+ elfshreloc(sect);
+ for(sect=segdata.sect; sect!=nil; sect=sect->next)
+ elfshreloc(sect);
+ }
+
+ if(!debug['s']) {
+ sh = elfshname(".symtab");
+ sh->type = SHT_SYMTAB;
+ sh->off = symo;
+ sh->size = symsize;
+ sh->addralign = PtrSize;
+ sh->entsize = 8+2*PtrSize;
+ sh->link = elfshname(".strtab")->shnum;
+ sh->info = elfglobalsymndx;
+
+ sh = elfshname(".strtab");
+ sh->type = SHT_STRTAB;
+ sh->off = symo+symsize;
+ sh->size = elfstrsize;
+ sh->addralign = 1;
+
+ // TODO(rsc): Enable for isobj too, once we know it works.
+ if(!isobj)
+ dwarfaddelfheaders();
+ }
+
+ /* Main header */
+ eh->ident[EI_MAG0] = '\177';
+ eh->ident[EI_MAG1] = 'E';
+ eh->ident[EI_MAG2] = 'L';
+ eh->ident[EI_MAG3] = 'F';
+ if(HEADTYPE == Hfreebsd)
+ eh->ident[EI_OSABI] = ELFOSABI_FREEBSD;
+ else if(HEADTYPE == Hnetbsd)
+ eh->ident[EI_OSABI] = ELFOSABI_NETBSD;
+ else if(HEADTYPE == Hopenbsd)
+ eh->ident[EI_OSABI] = ELFOSABI_OPENBSD;
+ if(PtrSize == 8)
+ eh->ident[EI_CLASS] = ELFCLASS64;
+ else
+ eh->ident[EI_CLASS] = ELFCLASS32;
+ eh->ident[EI_DATA] = ELFDATA2LSB;
+ eh->ident[EI_VERSION] = EV_CURRENT;
+
+ if(flag_shared)
+ eh->type = ET_DYN;
+ else if(isobj)
+ eh->type = ET_REL;
+ else
+ eh->type = ET_EXEC;
+
+ if(!isobj)
+ eh->entry = entryvalue();
+
+ eh->version = EV_CURRENT;
+
+ if(pph != nil) {
+ pph->filesz = eh->phnum * eh->phentsize;
+ pph->memsz = pph->filesz;
+ }
+
+ cseek(0);
+ a = 0;
+ a += elfwritehdr();
+ a += elfwritephdrs();
+ a += elfwriteshdrs();
+ if(!debug['d'])
+ a += elfwriteinterp();
+ if(!isobj) {
+ if(HEADTYPE == Hnetbsd)
+ a += elfwritenetbsdsig();
+ if(HEADTYPE == Hopenbsd)
+ a += elfwriteopenbsdsig();
+ if(buildinfolen > 0)
+ a += elfwritebuildinfo();
+ }
+ if(a > ELFRESERVE)
+ diag("ELFRESERVE too small: %d > %d", a, ELFRESERVE);
+}
diff --git a/src/cmd/ld/elf.h b/src/cmd/ld/elf.h
index 690ade975..3e22125b2 100644
--- a/src/cmd/ld/elf.h
+++ b/src/cmd/ld/elf.h
@@ -251,6 +251,7 @@ typedef struct {
#define PT_LOPROC 0x70000000 /* First processor-specific type. */
#define PT_HIPROC 0x7fffffff /* Last processor-specific type. */
#define PT_GNU_STACK 0x6474e551
+#define PT_PAX_FLAGS 0x65041580
/* Values for p_flags. */
#define PF_X 0x1 /* Executable. */
@@ -562,6 +563,10 @@ typedef struct {
#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_CALL 28
+#define R_ARM_JUMP24 29
+#define R_ARM_V4BX 40
+#define R_ARM_GOT_PREL 96
#define R_ARM_GNU_VTENTRY 100
#define R_ARM_GNU_VTINHERIT 101
#define R_ARM_RSBREL32 250
@@ -571,7 +576,7 @@ typedef struct {
#define R_ARM_RPC24 254
#define R_ARM_RBASE 255
-#define R_ARM_COUNT 33 /* Count of defined relocation types. */
+#define R_ARM_COUNT 37 /* Count of defined relocation types. */
#define R_386_NONE 0 /* No relocation. */
@@ -836,7 +841,8 @@ typedef struct {
* Section header.
*/
-typedef struct {
+typedef struct Elf64_Shdr Elf64_Shdr;
+struct Elf64_Shdr {
Elf64_Word name; /* Section name (index into the
section header string table). */
Elf64_Word type; /* Section type. */
@@ -848,7 +854,9 @@ typedef struct {
Elf64_Word info; /* Depends on section type. */
Elf64_Xword addralign; /* Alignment in bytes. */
Elf64_Xword entsize; /* Size of each entry in section. */
-} Elf64_Shdr;
+
+ int shnum; /* section number, not stored on disk */
+};
/*
* Program header.
@@ -952,7 +960,6 @@ typedef Elf64_Phdr ElfPhdr;
void elfinit(void);
ElfEhdr *getElfEhdr(void);
-ElfShdr *newElfShstrtab(vlong);
ElfShdr *newElfShdr(vlong);
ElfPhdr *newElfPhdr(void);
uint32 elfwritehdr(void);
@@ -969,18 +976,40 @@ extern int numelfshdr;
extern int iself;
extern int elfverneed;
int elfinterp(ElfShdr*, uint64, uint64, char*);
-int elfwriteinterp(vlong);
+int elfwriteinterp(void);
int elfnetbsdsig(ElfShdr*, uint64, uint64);
-int elfwritenetbsdsig(vlong);
+int elfwritenetbsdsig(void);
+int elfopenbsdsig(ElfShdr*, uint64, uint64);
+int elfwriteopenbsdsig(void);
+void addbuildinfo(char*);
+int elfbuildinfo(ElfShdr*, uint64, uint64);
+int elfwritebuildinfo(void);
void elfdynhash(void);
ElfPhdr* elfphload(Segment*);
ElfShdr* elfshbits(Section*);
+ElfShdr* elfshalloc(Section*);
+ElfShdr* elfshname(char*);
+ElfShdr* elfshreloc(Section*);
void elfsetstring(char*, int);
void elfaddverneed(Sym*);
+void elfemitreloc(void);
+void shsym(ElfShdr*, Sym*);
+void phsh(ElfPhdr*, ElfShdr*);
+void doelf(void);
+void elfsetupplt(void);
+void dwarfaddshstrings(Sym*);
+void dwarfaddelfheaders(void);
+void asmbelf(vlong symo);
+void asmbelfsetup(void);
+extern char linuxdynld[];
+extern char freebsddynld[];
+extern char netbsddynld[];
+extern char openbsddynld[];
+int elfreloc1(Reloc*, vlong off, int32 elfsym, vlong add);
EXTERN int elfstrsize;
EXTERN char* elfstrdat;
-EXTERN int elftextsh;
+EXTERN int buildinfolen;
/*
* Total amount of space to reserve at the start of the file
diff --git a/src/cmd/ld/go.c b/src/cmd/ld/go.c
index 3271be1f5..b2527b13e 100644
--- a/src/cmd/ld/go.c
+++ b/src/cmd/ld/go.c
@@ -67,8 +67,7 @@ ilookup(char *name)
}
static void loadpkgdata(char*, char*, char*, int);
-static void loaddynimport(char*, char*, char*, int);
-static void loaddynexport(char*, char*, char*, int);
+static void loadcgo(char*, char*, char*, int);
static int parsemethod(char**, char*, char**);
static int parsepkgdata(char*, char*, char**, char*, char**, char**, char**);
@@ -177,12 +176,12 @@ ldpkg(Biobuf *f, char *pkg, int64 len, char *filename, int whence)
loadpkgdata(filename, pkg, p0, p1 - p0);
- // look for dynimport section
- p0 = strstr(p1, "\n$$ // dynimport");
+ // look for cgo section
+ p0 = strstr(p1, "\n$$ // cgo");
if(p0 != nil) {
p0 = strchr(p0+1, '\n');
if(p0 == nil) {
- fprint(2, "%s: found $$ // dynimport but no newline in %s\n", argv0, filename);
+ fprint(2, "%s: found $$ // cgo but no newline in %s\n", argv0, filename);
if(debug['u'])
errorexit();
return;
@@ -191,34 +190,12 @@ ldpkg(Biobuf *f, char *pkg, int64 len, char *filename, int whence)
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);
+ fprint(2, "%s: cannot find end of // cgo 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));
+ loadcgo(filename, pkg, p0 + 1, p1 - (p0+1));
}
}
@@ -409,10 +386,16 @@ parsemethod(char **pp, char *ep, char **methp)
if(p == ep)
return 0;
+ // might be a comment about the method
+ if(p + 2 < ep && strncmp(p, "//", 2) == 0)
+ goto useline;
+
// if it says "func (", it's a method
- if(p + 6 >= ep || strncmp(p, "func (", 6) != 0)
- return 0;
+ if(p + 6 < ep && strncmp(p, "func (", 6) == 0)
+ goto useline;
+ return 0;
+useline:
// definition to end of line
*methp = p;
while(p < ep && *p != '\n')
@@ -428,173 +411,182 @@ parsemethod(char **pp, char *ep, char **methp)
}
static void
-loaddynimport(char *file, char *pkg, char *p, int n)
+loadcgo(char *file, char *pkg, char *p, int n)
{
- char *pend, *next, *name, *def, *p0, *lib, *q;
+ char *pend, *next, *p0, *q;
+ char *f[10], *local, *remote, *lib;
+ int nf;
Sym *s;
USED(file);
pend = p + n;
+ p0 = nil;
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;
+ free(p0);
+ p0 = strdup(p); // save for error message
+ nf = tokenize(p, f, nelem(f));
- if(debug['d']) {
- fprint(2, "%s: %s: cannot use dynamic imports with -d flag\n", argv0, file);
- nerrors++;
- return;
- }
+ if(strcmp(f[0], "cgo_import_dynamic") == 0) {
+ if(nf < 2 || nf > 4)
+ goto err;
+
+ local = f[1];
+ remote = local;
+ if(nf > 2)
+ remote = f[2];
+ lib = "";
+ if(nf > 3)
+ lib = f[3];
+
+ 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);
+ if(strcmp(local, "_") == 0 && strcmp(remote, "_") == 0) {
+ // allow #pragma dynimport _ _ "foo.so"
+ // to force a link of foo.so.
+ havedynamic = 1;
+ adddynlib(lib);
+ continue;
+ }
+
+ local = expandpkg(local, pkg);
+ q = strchr(remote, '#');
+ if(q)
+ *q++ = '\0';
+ s = lookup(local, 0);
+ if(local != f[1])
+ free(local);
+ if(s->type == 0 || s->type == SXREF) {
+ s->dynimplib = lib;
+ s->dynimpname = remote;
+ s->dynimpvers = q;
+ s->type = SDYNIMPORT;
+ havedynamic = 1;
+ }
continue;
}
-
- name = expandpkg(name, pkg);
- q = strchr(def, '@');
- if(q)
- *q++ = '\0';
- s = lookup(name, 0);
- free(name);
- if(s->type == 0 || s->type == SXREF) {
- s->dynimplib = lib;
- s->dynimpname = def;
- s->dynimpvers = q;
- s->type = SDYNIMPORT;
- havedynamic = 1;
+
+ if(strcmp(f[0], "cgo_import_static") == 0) {
+ if(nf != 2)
+ goto err;
+ if(isobj) {
+ local = f[1];
+ s = lookup(local, 0);
+ s->type = SHOSTOBJ;
+ s->size = 0;
+ }
+ continue;
}
- }
- 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;
+ if(strcmp(f[0], "cgo_export") == 0) {
+ if(nf < 2 || nf > 3)
+ goto err;
+ local = f[1];
+ if(nf > 2)
+ remote = f[2];
+ else
+ remote = local;
+ local = expandpkg(local, pkg);
+ s = lookup(local, 0);
+ if(s->dynimplib != nil) {
+ fprint(2, "%s: symbol is both imported and exported: %s\n", argv0, local);
+ nerrors++;
+ }
+ s->dynimpname = remote;
+ s->dynexport = 1;
- elocal = expandpkg(local, pkg);
+ if(ndynexp%32 == 0)
+ dynexp = realloc(dynexp, (ndynexp+32)*sizeof dynexp[0]);
+ dynexp[ndynexp++] = s;
- s = lookup(elocal, 0);
- if(s->dynimplib != nil) {
- fprint(2, "%s: symbol is both dynimport and dynexport %s\n", argv0, local);
- nerrors++;
+ if(local != f[1])
+ free(local);
+ continue;
+ }
+
+ if(strcmp(f[0], "cgo_dynamic_linker") == 0) {
+ if(nf != 2)
+ goto err;
+
+ if(!debug['I']) { // not overridden by command line
+ if(interpreter != nil && strcmp(interpreter, f[1]) != 0) {
+ fprint(2, "%s: conflict dynlinker: %s and %s\n", argv0, interpreter, f[1]);
+ nerrors++;
+ return;
+ }
+ free(interpreter);
+ interpreter = strdup(f[1]);
+ }
+ continue;
}
- 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);
}
+ free(p0);
return;
err:
- fprint(2, "%s: invalid dynexport line: %s\n", argv0, p0);
+ fprint(2, "%s: %s: invalid dynimport line: %s\n", argv0, file, p0);
nerrors++;
}
-static int markdepth;
+static Sym *markq;
+static Sym *emarkq;
static void
-marktext(Sym *s)
+mark1(Sym *s, Sym *parent)
{
- Auto *a;
- Prog *p;
-
- if(s == S)
+ if(s == S || s->reachable)
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--;
+ if(strncmp(s->name, "go.weak.", 8) == 0)
+ return;
+ s->reachable = 1;
+ s->reachparent = parent;
+ if(markq == nil)
+ markq = s;
+ else
+ emarkq->queue = s;
+ emarkq = s;
}
void
mark(Sym *s)
{
- int i;
+ mark1(s, nil);
+}
- 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 void
+markflood(void)
+{
+ Auto *a;
+ Prog *p;
+ Sym *s;
+ int i;
+
+ for(s=markq; s!=S; s=s->queue) {
+ if(s->text) {
+ if(debug['v'] > 1)
+ Bprint(&bso, "marktext %s\n", s->name);
+ for(a=s->autom; a; a=a->link)
+ mark1(a->gotype, s);
+ for(p=s->text; p != P; p=p->link) {
+ mark1(p->from.sym, s);
+ mark1(p->to.sym, s);
+ }
+ }
+ for(i=0; i<s->nr; i++)
+ mark1(s->r[i].sym, s);
+ mark1(s->gotype, s);
+ mark1(s->sub, s);
+ mark1(s->outer, s);
+ }
}
static char*
@@ -651,19 +643,30 @@ void
deadcode(void)
{
int i;
- Sym *s, *last;
+ Sym *s, *last, *p;
Auto *z;
+ Fmt fmt;
if(debug['v'])
Bprint(&bso, "%5.2f deadcode\n", cputime());
mark(lookup(INITENTRY, 0));
+ if(flag_shared)
+ mark(lookup(LIBINITENTRY, 0));
for(i=0; i<nelem(morename); i++)
mark(lookup(morename[i], 0));
for(i=0; i<ndynexp; i++)
mark(dynexp[i]);
+
+ markflood();
+ // keep each beginning with 'typelink.' if the symbol it points at is being kept.
+ for(s = allsym; s != S; s = s->allsym) {
+ if(strncmp(s->name, "go.typelink.", 12) == 0)
+ s->reachable = s->nr==1 && s->r[0].sym->reachable;
+ }
+
// remove dead text but keep file information (z symbols).
last = nil;
z = nil;
@@ -690,11 +693,34 @@ deadcode(void)
last->next = nil;
for(s = allsym; s != S; s = s->allsym)
- if(strncmp(s->name, "weak.", 5) == 0) {
+ if(strncmp(s->name, "go.weak.", 8) == 0) {
s->special = 1; // do not lay out in data segment
s->reachable = 1;
s->hide = 1;
}
+
+ // record field tracking references
+ fmtstrinit(&fmt);
+ for(s = allsym; s != S; s = s->allsym) {
+ if(strncmp(s->name, "go.track.", 9) == 0) {
+ s->special = 1; // do not lay out in data segment
+ s->hide = 1;
+ if(s->reachable) {
+ fmtprint(&fmt, "%s", s->name+9);
+ for(p=s->reachparent; p; p=p->reachparent)
+ fmtprint(&fmt, "\t%s", p->name);
+ fmtprint(&fmt, "\n");
+ }
+ s->type = SCONST;
+ s->value = 0;
+ }
+ }
+ if(tracksym == nil)
+ return;
+ s = lookup(tracksym, 0);
+ if(!s->reachable)
+ return;
+ addstrdata(tracksym, fmtstrflush(&fmt));
}
void
@@ -705,11 +731,12 @@ doweak(void)
// 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(strncmp(s->name, "go.weak.", 8) == 0) {
+ t = rlookup(s->name+8, s->version);
if(t && t->type != 0 && t->reachable) {
s->value = t->value;
s->type = t->type;
+ s->outer = t;
} else {
s->type = SCONST;
s->value = 0;
@@ -871,3 +898,28 @@ importcycles(void)
for(p=pkgall; p; p=p->all)
cycle(p);
}
+
+static int
+scmp(const void *p1, const void *p2)
+{
+ Sym *s1, *s2;
+
+ s1 = *(Sym**)p1;
+ s2 = *(Sym**)p2;
+ return strcmp(s1->dynimpname, s2->dynimpname);
+}
+void
+sortdynexp(void)
+{
+ int i;
+
+ // On Mac OS X Mountain Lion, we must sort exported symbols
+ // So we sort them here and pre-allocate dynid for them
+ // See http://golang.org/issue/4029
+ if(HEADTYPE != Hdarwin)
+ return;
+ qsort(dynexp, ndynexp, sizeof dynexp[0], scmp);
+ for(i=0; i<ndynexp; i++) {
+ dynexp[i]->dynid = -i-100; // also known to [68]l/asm.c:^adddynsym
+ }
+}
diff --git a/src/cmd/ld/ldelf.c b/src/cmd/ld/ldelf.c
index bd4f3e7d8..2bbf4f83e 100644
--- a/src/cmd/ld/ldelf.c
+++ b/src/cmd/ld/ldelf.c
@@ -308,9 +308,19 @@ 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 readsym(ElfObj*, int i, ElfSym*, int);
static int reltype(char*, int, uchar*);
+int
+valuecmp(Sym *a, Sym *b)
+{
+ if(a->value < b->value)
+ return -1;
+ if(a->value > b->value)
+ return +1;
+ return 0;
+}
+
void
ldelf(Biobuf *f, char *pkg, int64 len, char *pn)
{
@@ -327,8 +337,10 @@ ldelf(Biobuf *f, char *pkg, int64 len, char *pn)
Endian *e;
Reloc *r, *rp;
Sym *s;
+ Sym **symbols;
+
+ symbols = nil;
- USED(pkg);
if(debug['v'])
Bprint(&bso, "%5.2f ldelf %s\n", cputime(), pn);
@@ -516,7 +528,7 @@ ldelf(Biobuf *f, char *pkg, int64 len, char *pn)
if(sect->type != ElfSectNobits && map(obj, sect) < 0)
goto bad;
- name = smprint("%s(%s)", pn, sect->name);
+ name = smprint("%s(%s)", pkg, sect->name);
s = lookup(name, version);
free(name);
switch((int)sect->flags&(ElfSectFlagAlloc|ElfSectFlagWrite|ElfSectFlagExec)) {
@@ -539,15 +551,99 @@ ldelf(Biobuf *f, char *pkg, int64 len, char *pn)
}
s->size = sect->size;
s->align = sect->align;
+ sect->sym = s;
+ }
+
+ // enter sub-symbols into symbol table.
+ // symbol 0 is the null symbol.
+ symbols = malloc(obj->nsymtab * sizeof(symbols[0]));
+ if(symbols == nil) {
+ diag("out of memory");
+ errorexit();
+ }
+ for(i=1; i<obj->nsymtab; i++) {
+ if(readsym(obj, i, &sym, 1) < 0)
+ goto bad;
+ symbols[i] = sym.sym;
+ 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;
+ // even when we pass needSym == 1 to readsym, it might still return nil to skip some unwanted symbols
+ if(sym.sym == S)
+ 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;
+ if(s->outer != S) {
+ if(s->dupok)
+ continue;
+ diag("%s: duplicate symbol reference: %s in both %s and %s", pn, s->name, s->outer->name, sect->sym->name);
+ errorexit();
+ }
+ s->sub = sect->sym->sub;
+ sect->sym->sub = s;
+ s->type = sect->sym->type | (s->type&~SMASK) | 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) {
+ if(!s->dupok)
+ diag("%s: duplicate definition of %s", pn, s->name);
+ } else {
+ // 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;
+ }
+ }
+ }
+
+ // Sort outer lists by address, adding to textp.
+ // This keeps textp in increasing address order.
+ for(i=0; i<obj->nsect; i++) {
+ s = obj->sect[i].sym;
+ if(s == S)
+ continue;
+ if(s->sub)
+ s->sub = listsort(s->sub, valuecmp, offsetof(Sym, sub));
if(s->type == STEXT) {
if(etextp)
etextp->next = s;
else
textp = s;
etextp = s;
+ for(s = s->sub; s != S; s = s->sub) {
+ etextp->next = s;
+ etextp = s;
+ }
}
- sect->sym = s;
- }
+ }
// load relocations
for(i=0; i<obj->nsect; i++) {
@@ -588,14 +684,24 @@ ldelf(Biobuf *f, char *pkg, int64 len, char *pn)
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;
+ if((info & 0xffffffff) == 0) { // skip R_*_NONE relocation
+ j--;
+ n--;
+ continue;
+ }
+ if((info >> 32) == 0) { // absolute relocation, don't bother reading the null symbol
+ rp->sym = S;
+ } else {
+ if(readsym(obj, info>>32, &sym, 0) < 0)
+ goto bad;
+ sym.sym = symbols[info>>32];
+ 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->sym = sym.sym;
rp->type = reltype(pn, (uint32)info, &rp->siz);
if(rela)
rp->add = add;
@@ -615,65 +721,13 @@ ldelf(Biobuf *f, char *pkg, int64 len, char *pn)
s->r = r;
s->nr = n;
}
+ free(symbols);
- // 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);
+ free(symbols);
}
static ElfSect*
@@ -707,7 +761,7 @@ map(ElfObj *obj, ElfSect *sect)
}
static int
-readsym(ElfObj *obj, int i, ElfSym *sym)
+readsym(ElfObj *obj, int i, ElfSym *sym, int needSym)
{
Sym *s;
@@ -715,6 +769,9 @@ readsym(ElfObj *obj, int i, ElfSym *sym)
werrstr("invalid elf symbol index");
return -1;
}
+ if(i == 0) {
+ diag("readym: read null symbol!");
+ }
if(obj->is64) {
ElfSymBytes64 *b;
@@ -743,8 +800,6 @@ readsym(ElfObj *obj, int i, ElfSym *sym)
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;
@@ -754,13 +809,37 @@ readsym(ElfObj *obj, int i, ElfSym *sym)
case ElfSymTypeNone:
switch(sym->bind) {
case ElfSymBindGlobal:
- if(sym->other != 2) {
+ if(needSym) {
s = lookup(sym->name, 0);
- break;
+ // for global scoped hidden symbols we should insert it into
+ // symbol hash table, but mark them as hidden.
+ // __i686.get_pc_thunk.bx is allowed to be duplicated, to
+ // workaround that we set dupok.
+ // TODO(minux): correctly handle __i686.get_pc_thunk.bx without
+ // set dupok generally. See http://codereview.appspot.com/5823055/
+ // comment #5 for details.
+ if(s && sym->other == 2) {
+ s->type |= SHIDDEN;
+ s->dupok = 1;
+ }
}
- // fall through
+ break;
case ElfSymBindLocal:
- s = lookup(sym->name, version);
+ if(!(thechar == '5' && (strcmp(sym->name, "$a") == 0 || strcmp(sym->name, "$d") == 0))) // binutils for arm generate these mapping symbols, ignore these
+ if(needSym) {
+ // local names and hidden visiblity global names are unique
+ // and should only reference by its index, not name, so we
+ // don't bother to add them into hash table
+ s = newsym(sym->name, version);
+ s->type |= SHIDDEN;
+ }
+ break;
+ case ElfSymBindWeak:
+ if(needSym) {
+ s = newsym(sym->name, 0);
+ if(sym->other == 2)
+ s->type |= SHIDDEN;
+ }
break;
default:
werrstr("%s: invalid symbol binding %d", sym->name, sym->bind);
@@ -797,6 +876,18 @@ 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('5', R_ARM_ABS32):
+ case R('5', R_ARM_GOT32):
+ case R('5', R_ARM_PLT32):
+ case R('5', R_ARM_GOTOFF):
+ case R('5', R_ARM_GOTPC):
+ case R('5', R_ARM_THM_PC22):
+ case R('5', R_ARM_REL32):
+ case R('5', R_ARM_CALL):
+ case R('5', R_ARM_V4BX):
+ case R('5', R_ARM_GOT_PREL):
+ case R('5', R_ARM_PC24):
+ case R('5', R_ARM_JUMP24):
case R('6', R_X86_64_PC32):
case R('6', R_X86_64_PLT32):
case R('6', R_X86_64_GOTPCREL):
diff --git a/src/cmd/ld/ldmacho.c b/src/cmd/ld/ldmacho.c
index 388848767..41852f17c 100644
--- a/src/cmd/ld/ldmacho.c
+++ b/src/cmd/ld/ldmacho.c
@@ -440,7 +440,6 @@ ldmacho(Biobuf *f, char *pkg, int64 len, char *pn)
Reloc *r, *rp;
char *name;
- USED(pkg);
version++;
base = Boffset(f);
if(Bread(f, hdr, sizeof hdr) != sizeof hdr)
@@ -566,16 +565,21 @@ ldmacho(Biobuf *f, char *pkg, int64 len, char *pn)
continue;
if(strcmp(sect->name, "__eh_frame") == 0)
continue;
- name = smprint("%s(%s/%s)", pn, sect->segname, sect->name);
+ name = smprint("%s(%s/%s)", pkg, 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((sect->flags & 0xff) == 1) // S_ZEROFILL
+ s->p = mal(s->size);
+ else {
+ s->p = dat + sect->addr - c->seg.vmaddr;
+ }
if(strcmp(sect->segname, "__TEXT") == 0) {
if(strcmp(sect->name, "__text") == 0)
@@ -589,13 +593,6 @@ ldmacho(Biobuf *f, char *pkg, int64 len, char *pn)
} else
s->type = SDATA;
}
- if(s->type == STEXT) {
- if(etextp)
- etextp->next = s;
- else
- textp = s;
- etextp = s;
- }
sect->sym = s;
}
@@ -627,6 +624,12 @@ ldmacho(Biobuf *f, char *pkg, int64 len, char *pn)
werrstr("reference to invalid section %s/%s", sect->segname, sect->name);
continue;
}
+ if(s->outer != S) {
+ if(s->dupok)
+ continue;
+ diag("%s: duplicate symbol reference: %s in both %s and %s", pn, s->name, s->outer->name, sect->sym->name);
+ errorexit();
+ }
s->type = outer->type | SSUB;
s->sub = outer->sub;
outer->sub = s;
@@ -657,11 +660,29 @@ ldmacho(Biobuf *f, char *pkg, int64 len, char *pn)
p->link = nil;
p->pc = pc++;
s->text = p;
+ }
+ sym->sym = s;
+ }
- etextp->next = s;
+ // Sort outer lists by address, adding to textp.
+ // This keeps textp in increasing address order.
+ for(i=0; i<c->seg.nsect; i++) {
+ sect = &c->seg.sect[i];
+ if((s = sect->sym) == S)
+ continue;
+ if(s->sub)
+ s->sub = listsort(s->sub, valuecmp, offsetof(Sym, sub));
+ if(s->type == STEXT) {
+ if(etextp)
+ etextp->next = s;
+ else
+ textp = s;
etextp = s;
+ for(s = s->sub; s != S; s = s->sub) {
+ etextp->next = s;
+ etextp = s;
+ }
}
- sym->sym = s;
}
// load relocations
@@ -680,19 +701,28 @@ ldmacho(Biobuf *f, char *pkg, int64 len, char *pn)
int k;
MachoSect *ks;
- if(thechar != '8')
+ if(thechar != '8') {
+ // mach-o only uses scattered relocation on 32-bit platforms
diag("unexpected scattered relocation");
+ continue;
+ }
- // on 386, rewrite scattered 4/1 relocation into
- // the pseudo-pc-relative reference that it is.
+ // on 386, rewrite scattered 4/1 relocation and some
+ // scattered 2/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) {
+ if(j+1 >= sect->nreloc) {
+ werrstr("unsupported scattered relocation %d", (int)rel->type);
+ goto bad;
+ }
+ if(!(rel+1)->scattered || (rel+1)->type != 1 ||
+ (rel->type != 4 && rel->type != 2) ||
+ (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;
diff --git a/src/cmd/ld/ldpe.c b/src/cmd/ld/ldpe.c
index feb8620bd..39c15e6a1 100644
--- a/src/cmd/ld/ldpe.c
+++ b/src/cmd/ld/ldpe.c
@@ -145,7 +145,6 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn)
PeSym *sym;
USED(len);
- USED(pkg);
if(debug['v'])
Bprint(&bso, "%5.2f ldpe %s\n", cputime(), pn);
@@ -213,7 +212,7 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn)
if(map(obj, sect) < 0)
goto bad;
- name = smprint("%s(%s)", pn, sect->name);
+ name = smprint("%s(%s)", pkg, sect->name);
s = lookup(name, version);
free(name);
switch(sect->sh.Characteristics&(IMAGE_SCN_CNT_UNINITIALIZED_DATA|IMAGE_SCN_CNT_INITIALIZED_DATA|
@@ -237,13 +236,6 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn)
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);
@@ -300,6 +292,11 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn)
rp->add = le64(rsect->base+rp->off);
break;
}
+ // ld -r could generate multiple section symbols for the
+ // same section but with different values, we have to take
+ // that into account
+ if (obj->pesym[symindex].name[0] == '.')
+ rp->add += obj->pesym[symindex].value;
}
qsort(r, rsect->sh.NumberOfRelocations, sizeof r[0], rbyoff);
@@ -341,6 +338,13 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn)
if(sect == nil)
return;
+
+ if(s->outer != S) {
+ if(s->dupok)
+ continue;
+ diag("%s: duplicate symbol reference: %s in both %s and %s", pn, s->name, s->outer->name, sect->sym->name);
+ errorexit();
+ }
s->sub = sect->sym->sub;
sect->sym->sub = s;
s->type = sect->sym->type | SSUB;
@@ -363,9 +367,27 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn)
p->link = nil;
p->pc = pc++;
s->text = p;
-
- etextp->next = s;
+ }
+ }
+
+ // Sort outer lists by address, adding to textp.
+ // This keeps textp in increasing address order.
+ for(i=0; i<obj->nsect; i++) {
+ s = obj->sect[i].sym;
+ if(s == S)
+ continue;
+ if(s->sub)
+ s->sub = listsort(s->sub, valuecmp, offsetof(Sym, sub));
+ if(s->type == STEXT) {
+ if(etextp)
+ etextp->next = s;
+ else
+ textp = s;
etextp = s;
+ for(s = s->sub; s != S; s = s->sub) {
+ etextp->next = s;
+ etextp = s;
+ }
}
}
diff --git a/src/cmd/ld/lib.c b/src/cmd/ld/lib.c
index 4a100cac3..26fa4f2ac 100644
--- a/src/cmd/ld/lib.c
+++ b/src/cmd/ld/lib.c
@@ -72,6 +72,8 @@ Lflag(char *arg)
void
libinit(void)
{
+ char *race;
+
fmtinstall('i', iconv);
fmtinstall('Y', Yconv);
fmtinstall('Z', Zconv);
@@ -80,7 +82,10 @@ libinit(void)
print("goarch is not known: %s\n", goarch);
// add goroot to the end of the libdir list.
- Lflag(smprint("%s/pkg/%s_%s", goroot, goos, goarch));
+ race = "";
+ if(flag_race)
+ race = "_race";
+ Lflag(smprint("%s/pkg/%s_%s%s", goroot, goos, goarch, race));
// Unix doesn't like it when we write to a running (or, sometimes,
// recently run) binary, so remove the output file before writing it.
@@ -99,6 +104,13 @@ libinit(void)
sprint(INITENTRY, "_rt0_%s_%s", goarch, goos);
}
lookup(INITENTRY, 0)->type = SXREF;
+ if(flag_shared) {
+ if(LIBINITENTRY == nil) {
+ LIBINITENTRY = mal(strlen(goarch)+strlen(goos)+20);
+ sprint(LIBINITENTRY, "_rt0_%s_%s_lib", goarch, goos);
+ }
+ lookup(LIBINITENTRY, 0)->type = SXREF;
+ }
}
void
@@ -281,6 +293,8 @@ loadlib(void)
loadinternal("runtime");
if(thechar == '5')
loadinternal("math");
+ if(flag_race)
+ loadinternal("runtime/race");
for(i=0; i<libraryp; i++) {
if(debug['v'])
@@ -298,10 +312,11 @@ loadlib(void)
//
// 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)
+ if(!flag_shared && !havedynamic && HEADTYPE != Hdarwin)
debug['d'] = 1;
importcycles();
+ sortdynexp();
}
/*
@@ -366,7 +381,7 @@ objfile(char *file, char *pkg)
return;
}
- /* skip over __.SYMDEF */
+ /* skip over __.GOSYMDEF */
off = Boffset(f);
if((l = nextar(f, off, &arhdr)) <= 0) {
diag("%s: short read on archive file symbol header", file);
@@ -402,7 +417,7 @@ objfile(char *file, char *pkg)
* the individual symbols that are unused.
*
* loading every object will also make it possible to
- * load foreign objects not referenced by __.SYMDEF.
+ * load foreign objects not referenced by __.GOSYMDEF.
*/
for(;;) {
l = nextar(f, off, &arhdr);
@@ -548,6 +563,36 @@ eof:
free(pn);
}
+Sym*
+newsym(char *symb, int v)
+{
+ Sym *s;
+ int l;
+
+ l = strlen(symb) + 1;
+ s = mal(sizeof(*s));
+ if(debug['v'] > 1)
+ Bprint(&bso, "newsym %s\n", symb);
+
+ s->dynid = -1;
+ s->plt = -1;
+ s->got = -1;
+ s->name = mal(l + 1);
+ memmove(s->name, symb, l);
+
+ s->type = 0;
+ s->version = v;
+ s->value = 0;
+ s->sig = 0;
+ s->size = 0;
+ nsymbol++;
+
+ s->allsym = allsym;
+ allsym = s;
+
+ return s;
+}
+
static Sym*
_lookup(char *symb, int v, int creat)
{
@@ -569,27 +614,10 @@ _lookup(char *symb, int v, int creat)
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 = newsym(symb, v);
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;
}
@@ -1372,6 +1400,19 @@ headtype(char *name)
return -1; // not reached
}
+char*
+headstr(int v)
+{
+ static char buf[20];
+ int i;
+
+ for(i=0; headers[i].name; i++)
+ if(v == headers[i].val)
+ return headers[i].name;
+ snprint(buf, sizeof buf, "%d", v);
+ return buf;
+}
+
void
undef(void)
{
@@ -1419,6 +1460,23 @@ Yconv(Fmt *fp)
vlong coutpos;
+static void
+dowrite(int fd, char *p, int n)
+{
+ int m;
+
+ while(n > 0) {
+ m = write(fd, p, n);
+ if(m <= 0) {
+ cursym = S;
+ diag("write error: %r");
+ errorexit();
+ }
+ n -= m;
+ p += m;
+ }
+}
+
void
cflush(void)
{
@@ -1427,13 +1485,8 @@ cflush(void)
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;
- }
+ dowrite(cout, buf.cbuf, n);
+ coutpos += n;
cbp = buf.cbuf;
cbc = sizeof(buf.cbuf);
cbpmax = cbp;
@@ -1472,9 +1525,142 @@ void
cwrite(void *buf, int n)
{
cflush();
- if(write(cout, buf, n) != n) {
- diag("write error: %r");
- errorexit();
- }
+ if(n <= 0)
+ return;
+ dowrite(cout, buf, n);
coutpos += n;
}
+
+void
+usage(void)
+{
+ fprint(2, "usage: %cl [options] main.%c\n", thechar, thechar);
+ flagprint(2);
+ exits("usage");
+}
+
+void
+setheadtype(char *s)
+{
+ HEADTYPE = headtype(s);
+}
+
+void
+setinterp(char *s)
+{
+ debug['I'] = 1; // denote cmdline interpreter override
+ interpreter = s;
+}
+
+void
+doversion(void)
+{
+ print("%cl version %s\n", thechar, getgoversion());
+ errorexit();
+}
+
+void
+genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*))
+{
+ Auto *a;
+ Sym *s;
+ int32 off;
+
+ // These symbols won't show up in the first loop below because we
+ // skip STEXT symbols. Normal STEXT symbols are emitted by walking textp.
+ s = lookup("text", 0);
+ if(s->type == STEXT)
+ put(s, s->name, 'T', s->value, s->size, s->version, 0);
+ s = lookup("etext", 0);
+ if(s->type == STEXT)
+ put(s, s->name, 'T', s->value, s->size, s->version, 0);
+
+ for(s=allsym; s!=S; s=s->allsym) {
+ if(s->hide)
+ continue;
+ switch(s->type&SMASK) {
+ case SCONST:
+ case SRODATA:
+ case SSYMTAB:
+ case SPCLNTAB:
+ case SDATA:
+ case SNOPTRDATA:
+ case SELFROSECT:
+ case SMACHOGOT:
+ case STYPE:
+ case SSTRING:
+ case SGOSTRING:
+ case SWINDOWS:
+ case SGCDATA:
+ case SGCBSS:
+ if(!s->reachable)
+ continue;
+ put(s, s->name, 'D', symaddr(s), s->size, s->version, s->gotype);
+ continue;
+
+ case SBSS:
+ case SNOPTRBSS:
+ if(!s->reachable)
+ continue;
+ if(s->np > 0)
+ diag("%s should not be bss (size=%d type=%d special=%d)", s->name, (int)s->np, s->type, s->special);
+ put(s, s->name, 'B', symaddr(s), s->size, s->version, s->gotype);
+ continue;
+
+ case SFILE:
+ put(nil, s->name, 'f', s->value, 0, s->version, 0);
+ continue;
+ }
+ }
+
+ for(s = textp; s != nil; s = s->next) {
+ if(s->text == nil)
+ continue;
+
+ /* filenames first */
+ for(a=s->autom; a; a=a->link)
+ if(a->type == D_FILE)
+ put(nil, a->asym->name, 'z', a->aoffset, 0, 0, 0);
+ else
+ if(a->type == D_FILE1)
+ put(nil, a->asym->name, 'Z', a->aoffset, 0, 0, 0);
+
+ put(s, s->name, 'T', s->value, s->size, s->version, s->gotype);
+
+ /* frame, locals, args, auto and param after */
+ put(nil, ".frame", 'm', (uint32)s->text->to.offset+PtrSize, 0, 0, 0);
+ put(nil, ".locals", 'm', s->locals, 0, 0, 0);
+ put(nil, ".args", 'm', s->args, 0, 0, 0);
+
+ for(a=s->autom; a; a=a->link) {
+ // Emit a or p according to actual offset, even if label is wrong.
+ // This avoids negative offsets, which cannot be encoded.
+ if(a->type != D_AUTO && a->type != D_PARAM)
+ continue;
+
+ // compute offset relative to FP
+ if(a->type == D_PARAM)
+ off = a->aoffset;
+ else
+ off = a->aoffset - PtrSize;
+
+ // FP
+ if(off >= 0) {
+ put(nil, a->asym->name, 'p', off, 0, 0, a->gotype);
+ continue;
+ }
+
+ // SP
+ if(off <= -PtrSize) {
+ put(nil, a->asym->name, 'a', -(off+PtrSize), 0, 0, a->gotype);
+ continue;
+ }
+
+ // Otherwise, off is addressing the saved program counter.
+ // Something underhanded is going on. Say nothing.
+ }
+ }
+ if(debug['v'] || debug['n'])
+ Bprint(&bso, "symsize = %ud\n", symsize);
+ Bflush(&bso);
+}
diff --git a/src/cmd/ld/lib.h b/src/cmd/ld/lib.h
index 02dac6e1c..94ad76ecc 100644
--- a/src/cmd/ld/lib.h
+++ b/src/cmd/ld/lib.h
@@ -31,7 +31,7 @@
enum
{
Sxxx,
-
+
/* order here is order in output file */
STEXT,
SMACHOPLT,
@@ -39,11 +39,15 @@ enum
SSTRING,
SGOSTRING,
SRODATA,
+ STYPELINK,
+ SGCDATA,
+ SGCBSS,
SSYMTAB,
SPCLNTAB,
SELFROSECT,
SELFSECT,
SNOPTRDATA,
+ SDATARELRO,
SDATA,
SMACHO, /* Mach-O __nl_symbol_ptr */
SMACHOGOT,
@@ -59,9 +63,12 @@ enum
SFILE,
SCONST,
SDYNIMPORT,
+ SHOSTOBJ,
SSUB = 1<<8, /* sub-symbol, linked from parent via ->sub list */
-
+ SMASK = SSUB - 1,
+ SHIDDEN = 1<<9, // hidden or local symbol
+
NHASH = 100003,
};
@@ -92,6 +99,8 @@ struct Segment
Section* sect;
};
+#pragma incomplete struct Elf64_Shdr
+
struct Section
{
uchar rwx;
@@ -100,6 +109,9 @@ struct Section
uvlong len;
Section *next; // in segment list
Segment *seg;
+ struct Elf64_Shdr *elfsect;
+ uvlong reloff;
+ uvlong rellen;
};
extern char symname[];
@@ -128,6 +140,12 @@ EXTERN char* thestring;
EXTERN int ndynexp;
EXTERN int havedynamic;
EXTERN int iscgo;
+EXTERN int isobj;
+EXTERN int elfglobalsymndx;
+EXTERN int flag_race;
+EXTERN int flag_shared;
+EXTERN char* tracksym;
+EXTERN char* interpreter;
EXTERN Segment segtext;
EXTERN Segment segdata;
@@ -142,6 +160,7 @@ void addhist(int32 line, int type);
void asmlc(void);
void histtoauto(void);
void collapsefrog(Sym *s);
+Sym* newsym(char *symb, int v);
Sym* lookup(char *symb, int v);
Sym* rlookup(char *symb, int v);
void nuxiinit(void);
@@ -163,6 +182,8 @@ void symtab(void);
void Lflag(char *arg);
void usage(void);
void adddynrel(Sym*, Reloc*);
+void adddynrela(Sym*, Sym*, Reloc*);
+Sym* lookuprel(void);
void ldobj1(Biobuf *f, char*, int64 len, char *pn);
void ldobj(Biobuf*, char*, int64, char*, int);
void ldelf(Biobuf*, char*, int64, char*);
@@ -176,21 +197,26 @@ 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);
void addstrdata(char*, char*);
vlong addstring(Sym*, char*);
+vlong adduint8(Sym*, uint8);
+vlong adduint16(Sym*, uint16);
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);
+vlong setaddrplus(Sym*, vlong, Sym*, int32);
+vlong setaddr(Sym*, vlong, Sym*);
+void setuint8(Sym*, vlong, uint8);
+void setuint16(Sym*, vlong, uint16);
+void setuint32(Sym*, vlong, uint32);
+void setuint64(Sym*, vlong, uint64);
void asmsym(void);
void asmelfsym(void);
void asmplan9sym(void);
@@ -209,6 +235,11 @@ void dostkcheck(void);
void undef(void);
void doweak(void);
void setpersrc(Sym*);
+void doversion(void);
+void usage(void);
+void setinterp(char*);
+Sym* listsort(Sym*, int(*cmp)(Sym*, Sym*), int);
+int valuecmp(Sym*, Sym*);
int pathchar(void);
void* mal(uint32);
@@ -233,12 +264,6 @@ struct Endian
extern Endian be, le;
-// relocation size bits
-enum {
- Rbig = 128,
- Rlittle = 64,
-};
-
/* set by call to mywhatsys() */
extern char* goroot;
extern char* goarch;
@@ -282,6 +307,8 @@ EXTERN char* headstring;
extern Header headers[];
int headtype(char*);
+char* headstr(int);
+void setheadtype(char*);
int Yconv(Fmt*);
@@ -312,3 +339,25 @@ void cseek(vlong);
void cwrite(void*, int);
void importcycles(void);
int Zconv(Fmt*);
+
+uint8 decodetype_kind(Sym*);
+vlong decodetype_size(Sym*);
+Sym* decodetype_gc(Sym*);
+Sym* decodetype_arrayelem(Sym*);
+vlong decodetype_arraylen(Sym*);
+Sym* decodetype_ptrelem(Sym*);
+Sym* decodetype_mapkey(Sym*);
+Sym* decodetype_mapvalue(Sym*);
+Sym* decodetype_chanelem(Sym*);
+int decodetype_funcdotdotdot(Sym*);
+int decodetype_funcincount(Sym*);
+int decodetype_funcoutcount(Sym*);
+Sym* decodetype_funcintype(Sym*, int);
+Sym* decodetype_funcouttype(Sym*, int);
+int decodetype_structfieldcount(Sym*);
+char* decodetype_structfieldname(Sym*, int);
+Sym* decodetype_structfieldtype(Sym*, int);
+vlong decodetype_structfieldoffs(Sym*, int);
+vlong decodetype_ifacemethodcount(Sym*);
+
+void sortdynexp(void);
diff --git a/src/cmd/ld/macho.h b/src/cmd/ld/macho.h
index f55104150..baea6ff03 100644
--- a/src/cmd/ld/macho.h
+++ b/src/cmd/ld/macho.h
@@ -50,7 +50,7 @@ struct MachoDebug {
uint32 filesize;
};
-MachoHdr* getMachoHdr();
+MachoHdr* getMachoHdr(void);
MachoSeg* newMachoSeg(char*, int);
MachoSect* newMachoSect(MachoSeg*, char*);
MachoLoad* newMachoLoad(uint32, uint32);
diff --git a/src/cmd/ld/pe.c b/src/cmd/ld/pe.c
index 1d70b4808..f2903ba0f 100644
--- a/src/cmd/ld/pe.c
+++ b/src/cmd/ld/pe.c
@@ -148,6 +148,9 @@ peinit(void)
PESECTHEADR = rnd(PEFILEHEADR, PESECTALIGN);
nextsectoff = PESECTHEADR;
nextfileoff = PEFILEHEADR;
+
+ // some mingw libs depend on this symbol, for example, FindPESectionByName
+ xdefine("__image_base__", SDATA, PEBASE);
}
static void
@@ -533,6 +536,7 @@ addexcept(IMAGE_SECTION_HEADER *text)
uvlong n;
Sym *sym;
+ USED(text);
if(thechar != '6')
return;
@@ -656,7 +660,7 @@ asmbpe(void)
// for other threads we specify stack size in runtime explicitly
// (runtime knows whether cgo is enabled or not).
// If you change stack reserve sizes here,
- // change them in runtime/cgo/windows_386/amd64.c as well.
+ // change STACKSIZE in runtime/cgo/gcc_windows_{386,amd64}.c as well.
if(!iscgo) {
set(SizeOfStackReserve, 0x00010000);
set(SizeOfStackCommit, 0x0000ffff);
diff --git a/src/cmd/ld/symtab.c b/src/cmd/ld/symtab.c
index 129b13ea0..89a594872 100644
--- a/src/cmd/ld/symtab.c
+++ b/src/cmd/ld/symtab.c
@@ -36,7 +36,7 @@
static int maxelfstr;
-int
+static int
putelfstr(char *s)
{
int off, n;
@@ -57,14 +57,14 @@ putelfstr(char *s)
return off;
}
-void
-putelfsyment(int off, vlong addr, vlong size, int info, int shndx)
+static void
+putelfsyment(int off, vlong addr, vlong size, int info, int shndx, int other)
{
switch(thechar) {
case '6':
LPUT(off);
cput(info);
- cput(0);
+ cput(other);
WPUT(shndx);
VPUT(addr);
VPUT(size);
@@ -75,17 +75,21 @@ putelfsyment(int off, vlong addr, vlong size, int info, int shndx)
LPUT(addr);
LPUT(size);
cput(info);
- cput(0);
+ cput(other);
WPUT(shndx);
symsize += ELF32SYMSIZE;
break;
}
}
-void
+static int numelfsym = 1; // 0 is reserved
+static int elfbind;
+
+static void
putelfsym(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go)
{
- int bind, type, shndx, off;
+ int bind, type, off;
+ Sym *xo;
USED(go);
switch(t) {
@@ -93,37 +97,68 @@ putelfsym(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go)
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;
+ xo = x;
+ while(xo->outer != nil)
+ xo = xo->outer;
+ if(xo->sect == nil) {
+ cursym = x;
+ diag("missing section in putelfsym");
+ return;
+ }
+ if(xo->sect->elfsect == nil) {
+ cursym = x;
+ diag("missing ELF section in putelfsym");
+ return;
+ }
+
+ // One pass for each binding: STB_LOCAL, STB_GLOBAL,
+ // maybe one day STB_WEAK.
+ bind = (ver || (x->type & SHIDDEN)) ? STB_LOCAL : STB_GLOBAL;
+ if(bind != elfbind)
+ return;
+
off = putelfstr(s);
- putelfsyment(off, addr, size, (bind<<4)|(type&0xf), shndx);
+ if(isobj)
+ addr -= xo->sect->vaddr;
+ putelfsyment(off, addr, size, (bind<<4)|(type&0xf), xo->sect->elfsect->shnum, (x->type & SHIDDEN) ? 2 : 0);
+ x->elfsym = numelfsym++;
}
void
asmelfsym(void)
{
+ Sym *s;
+
// the first symbol entry is reserved
- putelfsyment(0, 0, 0, (STB_LOCAL<<4)|STT_NOTYPE, 0);
+ putelfsyment(0, 0, 0, (STB_LOCAL<<4)|STT_NOTYPE, 0, 0);
+
+ elfbind = STB_LOCAL;
genasmsym(putelfsym);
+
+ elfbind = STB_GLOBAL;
+ elfglobalsymndx = numelfsym;
+ genasmsym(putelfsym);
+
+ for(s=allsym; s!=S; s=s->allsym) {
+ if(s->type != SHOSTOBJ)
+ continue;
+ putelfsyment(putelfstr(s->name), 0, 0, (STB_GLOBAL<<4)|STT_NOTYPE, 0, 0);
+ s->elfsym = numelfsym++;
+ }
}
-void
+static void
putplan9sym(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go)
{
- int i;
+ int i, l;
USED(go);
USED(ver);
@@ -142,6 +177,11 @@ putplan9sym(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go)
case 'z':
case 'Z':
case 'm':
+ l = 4;
+ if(HEADTYPE == Hplan9x64 && !debug['8']) {
+ lputb(addr>>32);
+ l = 8;
+ }
lputb(addr);
cput(t+0x80); /* 0x80 is variable length */
@@ -162,7 +202,7 @@ putplan9sym(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go)
cput(s[i]);
cput(0);
}
- symsize += 4 + 1 + i + 1;
+ symsize += l + 1 + i + 1;
break;
default:
return;
@@ -192,7 +232,7 @@ static void
slputb(int32 v)
{
uchar *p;
-
+
symgrow(symt, symt->size+4);
p = symt->p + symt->size;
*p++ = v>>24;
@@ -202,6 +242,22 @@ slputb(int32 v)
symt->size += 4;
}
+static void
+slputl(int32 v)
+{
+ uchar *p;
+
+ symgrow(symt, symt->size+4);
+ p = symt->p + symt->size;
+ *p++ = v;
+ *p++ = v>>8;
+ *p++ = v>>16;
+ *p = v>>24;
+ symt->size += 4;
+}
+
+static void (*slput)(int32);
+
void
wputl(ushort w)
{
@@ -248,32 +304,76 @@ vputl(uint64 v)
lputl(v >> 32);
}
+// Emit symbol table entry.
+// The table format is described at the top of ../../pkg/runtime/symtab.c.
void
putsymb(Sym *s, char *name, int t, vlong v, vlong size, int ver, Sym *typ)
{
- int i, f, l;
+ int i, f, c;
+ vlong v1;
Reloc *rel;
USED(size);
- if(t == 'f')
- name++;
- l = 4;
-// if(!debug['8'])
-// l = 8;
+
+ // type byte
+ if('A' <= t && t <= 'Z')
+ c = t - 'A';
+ else if('a' <= t && t <= 'z')
+ c = t - 'a' + 26;
+ else {
+ diag("invalid symbol table type %c", t);
+ errorexit();
+ return;
+ }
+
+ if(s != nil)
+ c |= 0x40; // wide value
+ if(typ != nil)
+ c |= 0x80; // has go type
+ scput(c);
+
+ // value
if(s != nil) {
+ // full width
rel = addrel(symt);
- rel->siz = l + Rbig;
+ rel->siz = PtrSize;
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(PtrSize == 8)
+ slput(0);
+ slput(0);
+ } else {
+ // varint
+ if(v < 0) {
+ diag("negative value in symbol table: %s %lld", name, v);
+ errorexit();
+ }
+ v1 = v;
+ while(v1 >= 0x80) {
+ scput(v1 | 0x80);
+ v1 >>= 7;
+ }
+ scput(v1);
+ }
+
+ // go type if present
+ if(typ != nil) {
+ if(!typ->reachable)
+ diag("unreachable type %s", typ->name);
+ rel = addrel(symt);
+ rel->siz = PtrSize;
+ rel->sym = typ;
+ rel->type = D_ADDR;
+ rel->off = symt->size;
+ if(PtrSize == 8)
+ slput(0);
+ slput(0);
+ }
+
+ // name
+ if(t == 'f')
+ name++;
if(t == 'Z' || t == 'z') {
scput(name[0]);
@@ -283,24 +383,11 @@ putsymb(Sym *s, char *name, int t, vlong v, vlong size, int ver, Sym *typ)
}
scput(0);
scput(0);
- }
- else {
+ } 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') {
@@ -313,25 +400,34 @@ putsymb(Sym *s, char *name, int t, vlong v, vlong size, int ver, Sym *typ)
return;
}
if(ver)
- Bprint(&bso, "%c %.8llux %s<%d> %s\n", t, v, s->name, ver, typ ? typ->name : "");
+ Bprint(&bso, "%c %.8llux %s<%d> %s\n", t, v, name, ver, typ ? typ->name : "");
else
- Bprint(&bso, "%c %.8llux %s %s\n", t, v, s->name, typ ? typ->name : "");
+ Bprint(&bso, "%c %.8llux %s %s\n", t, v, name, typ ? typ->name : "");
}
}
void
symtab(void)
{
- Sym *s;
-
+ Sym *s, *symtype, *symtypelink, *symgostring;
dosymtype();
// 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("typelink", SRODATA, 0);
+ xdefine("etypelink", SRODATA, 0);
xdefine("rodata", SRODATA, 0);
xdefine("erodata", SRODATA, 0);
+ if(flag_shared) {
+ xdefine("datarelro", SDATARELRO, 0);
+ xdefine("edatarelro", SDATARELRO, 0);
+ }
+ xdefine("gcdata", SGCDATA, 0);
+ xdefine("egcdata", SGCDATA, 0);
+ xdefine("gcbss", SGCBSS, 0);
+ xdefine("egcbss", SGCBSS, 0);
xdefine("noptrdata", SNOPTRDATA, 0);
xdefine("enoptrdata", SNOPTRDATA, 0);
xdefine("data", SDATA, 0);
@@ -343,23 +439,27 @@ symtab(void)
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;
+ symtype = s;
s = lookup("go.string.*", 0);
s->type = SGOSTRING;
s->size = 0;
s->reachable = 1;
+ symgostring = s;
+
+ symtypelink = lookup("typelink", 0);
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.
@@ -370,14 +470,44 @@ symtab(void)
if(strncmp(s->name, "type.", 5) == 0) {
s->type = STYPE;
s->hide = 1;
+ s->outer = symtype;
+ }
+ if(strncmp(s->name, "go.typelink.", 12) == 0) {
+ s->type = STYPELINK;
+ s->hide = 1;
+ s->outer = symtypelink;
}
if(strncmp(s->name, "go.string.", 10) == 0) {
s->type = SGOSTRING;
s->hide = 1;
+ s->outer = symgostring;
}
}
if(debug['s'])
return;
+
+ switch(thechar) {
+ default:
+ diag("unknown architecture %c", thechar);
+ errorexit();
+ case '5':
+ case '6':
+ case '8':
+ // little-endian symbol table
+ slput = slputl;
+ break;
+ case 'v':
+ // big-endian symbol table
+ slput = slputb;
+ break;
+ }
+ // new symbol table header.
+ slput(0xfffffffd);
+ scput(0);
+ scput(0);
+ scput(0);
+ scput(PtrSize);
+
genasmsym(putsymb);
}