diff options
author | Michael Stapelberg <stapelberg@debian.org> | 2013-03-04 21:27:36 +0100 |
---|---|---|
committer | Michael Stapelberg <michael@stapelberg.de> | 2013-03-04 21:27:36 +0100 |
commit | 04b08da9af0c450d645ab7389d1467308cfc2db8 (patch) | |
tree | db247935fa4f2f94408edc3acd5d0d4f997aa0d8 /src/cmd/ld | |
parent | 917c5fb8ec48e22459d77e3849e6d388f93d3260 (diff) | |
download | golang-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.c | 620 | ||||
-rw-r--r-- | src/cmd/ld/decodesym.c | 215 | ||||
-rw-r--r-- | src/cmd/ld/doc.go | 21 | ||||
-rw-r--r-- | src/cmd/ld/dwarf.c | 252 | ||||
-rw-r--r-- | src/cmd/ld/elf.c | 954 | ||||
-rw-r--r-- | src/cmd/ld/elf.h | 43 | ||||
-rw-r--r-- | src/cmd/ld/go.c | 380 | ||||
-rw-r--r-- | src/cmd/ld/ldelf.c | 237 | ||||
-rw-r--r-- | src/cmd/ld/ldmacho.c | 66 | ||||
-rw-r--r-- | src/cmd/ld/ldpe.c | 44 | ||||
-rw-r--r-- | src/cmd/ld/lib.c | 252 | ||||
-rw-r--r-- | src/cmd/ld/lib.h | 71 | ||||
-rw-r--r-- | src/cmd/ld/macho.h | 2 | ||||
-rw-r--r-- | src/cmd/ld/pe.c | 6 | ||||
-rw-r--r-- | src/cmd/ld/symtab.c | 240 |
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); } |