diff options
Diffstat (limited to 'src/cmd/ld')
-rw-r--r-- | src/cmd/ld/data.c | 429 | ||||
-rw-r--r-- | src/cmd/ld/doc.go | 31 | ||||
-rw-r--r-- | src/cmd/ld/dwarf.c | 522 | ||||
-rw-r--r-- | src/cmd/ld/dwarf_defs.h | 1 | ||||
-rw-r--r-- | src/cmd/ld/elf.c | 73 | ||||
-rw-r--r-- | src/cmd/ld/elf.h | 7 | ||||
-rw-r--r-- | src/cmd/ld/go.c | 112 | ||||
-rw-r--r-- | src/cmd/ld/ldelf.c | 4 | ||||
-rw-r--r-- | src/cmd/ld/ldmacho.c | 8 | ||||
-rw-r--r-- | src/cmd/ld/ldpe.c | 26 | ||||
-rw-r--r-- | src/cmd/ld/lib.c | 398 | ||||
-rw-r--r-- | src/cmd/ld/lib.h | 65 | ||||
-rw-r--r-- | src/cmd/ld/macho.c | 592 | ||||
-rw-r--r-- | src/cmd/ld/macho.h | 13 | ||||
-rw-r--r-- | src/cmd/ld/pe.c | 14 | ||||
-rw-r--r-- | src/cmd/ld/symtab.c | 69 |
16 files changed, 1707 insertions, 657 deletions
diff --git a/src/cmd/ld/data.c b/src/cmd/ld/data.c index 6c6b1be43..23fc23e5f 100644 --- a/src/cmd/ld/data.c +++ b/src/cmd/ld/data.c @@ -33,11 +33,12 @@ #include "l.h" #include "../ld/lib.h" #include "../ld/elf.h" +#include "../ld/macho.h" #include "../ld/pe.h" #include "../../pkg/runtime/mgc0.h" void dynreloc(void); -static vlong addaddrplus4(Sym *s, Sym *t, int32 add); +static vlong addaddrplus4(Sym *s, Sym *t, vlong add); /* * divide-and-conquer list-link @@ -135,11 +136,7 @@ addrel(Sym *s) s->maxr = 4; else s->maxr <<= 1; - s->r = realloc(s->r, s->maxr*sizeof s->r[0]); - if(s->r == 0) { - diag("out of memory"); - errorexit(); - } + s->r = erealloc(s->r, s->maxr*sizeof s->r[0]); memset(s->r+s->nr, 0, (s->maxr-s->nr)*sizeof s->r[0]); } return &s->r[s->nr++]; @@ -158,6 +155,7 @@ relocsym(Sym *s) cursym = s; memset(&p, 0, sizeof p); for(r=s->r; r<s->r+s->nr; r++) { + r->done = 1; off = r->off; siz = r->siz; if(off < 0 || off+siz > s->np) { @@ -174,41 +172,88 @@ relocsym(Sym *s) if(r->sym != S && r->sym->type == SDYNIMPORT) diag("unhandled relocation for %s (type %d rtype %d)", r->sym->name, r->sym->type, r->type); - if(r->sym != S && !r->sym->reachable) + if(r->sym != S && r->sym->type != STLSBSS && !r->sym->reachable) diag("unreachable sym in relocation: %s %s", s->name, r->sym->name); switch(r->type) { default: o = 0; - if(isobj || archreloc(r, s, &o) < 0) + if(linkmode == LinkExternal || archreloc(r, s, &o) < 0) diag("unknown reloc %d", r->type); break; + case D_TLS: + r->done = 0; + o = 0; + 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); + if(linkmode == LinkExternal && r->sym->type != SCONST) { + r->done = 0; + + // set up addend for eventual relocation via outer symbol. + rs = r->sym; + r->xadd = r->add; + while(rs->outer != nil) { + r->xadd += symaddr(rs) - symaddr(rs->outer); + rs = rs->outer; } + if(rs->type != SHOSTOBJ && rs->sect == nil) + diag("missing section for %s", rs->name); + r->xsym = rs; + + o = r->xadd; + if(iself) { + if(thechar == '6') + o = 0; + } else if(HEADTYPE == Hdarwin) { + if(rs->type != SHOSTOBJ) + o += symaddr(rs); + } else { + diag("unhandled pcrel relocation for %s", headtype); + } + break; } + o = symaddr(r->sym) + r->add; break; case D_PCREL: // r->sym can be null when CALL $(constant) is transformed from absolute PC to relative PC call. + if(linkmode == LinkExternal && r->sym && r->sym->type != SCONST && r->sym->sect != cursym->sect) { + r->done = 0; + + // set up addend for eventual relocation via outer symbol. + rs = r->sym; + r->xadd = r->add; + while(rs->outer != nil) { + r->xadd += symaddr(rs) - symaddr(rs->outer); + rs = rs->outer; + } + r->xadd -= r->siz; // relative to address after the relocated chunk + if(rs->type != SHOSTOBJ && rs->sect == nil) + diag("missing section for %s", rs->name); + r->xsym = rs; + + o = r->xadd; + if(iself) { + if(thechar == '6') + o = 0; + } else if(HEADTYPE == Hdarwin) { + if(rs->type != SHOSTOBJ) + o += symaddr(rs) - rs->sect->vaddr; + o -= r->off; // WTF? + } else { + diag("unhandled pcrel relocation for %s", headtype); + } + break; + } 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; - } + // NOTE: The (int32) cast on the next line works around a bug in Plan 9's 8c + // compiler. The expression s->value + r->off + r->siz is int32 + int32 + + // uchar, and Plan 9 8c incorrectly treats the expression as type uint32 + // instead of int32, causing incorrect values when sign extended for adding + // to o. The bug only occurs on Plan 9, because this C program is compiled by + // the standard host compiler (gcc on most other systems). + o += r->add - (s->value + r->off + (int32)r->siz); break; case D_SIZE: o = r->sym->size + r->add; @@ -220,6 +265,13 @@ relocsym(Sym *s) cursym = s; diag("bad reloc size %#ux for %s", siz, r->sym->name); case 4: + if(r->type == D_PCREL) { + if(o != (int32)o) + diag("pc-relative relocation address is too big: %#llx", o); + } else { + if(o != (int32)o && o != (uint32)o) + diag("non-pc-relative relocation address is too big: %#llux", o); + } fl = o; cast = (uchar*)&fl; for(i=0; i<4; i++) @@ -300,7 +352,7 @@ dynrelocsym(Sym *s) 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 + if(flag_shared && r->sym != S && s->type != SDYNIMPORT && 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); @@ -342,11 +394,7 @@ symgrow(Sym *s, int32 siz) s->maxp = 8; while(s->maxp < siz) s->maxp <<= 1; - s->p = realloc(s->p, s->maxp); - if(s->p == nil) { - diag("out of memory"); - errorexit(); - } + s->p = erealloc(s->p, s->maxp); memset(s->p+s->np, 0, s->maxp-s->np); } s->np = siz; @@ -560,8 +608,10 @@ void datblk(int32 addr, int32 size) { Sym *sym; - int32 eaddr; + int32 i, eaddr; uchar *p, *ep; + char *typ, *rsname; + Reloc *r; if(debug['a']) Bprint(&bso, "datblk [%#x,%#x) at offset %#llx\n", addr, addr+size, cpos()); @@ -581,23 +631,46 @@ datblk(int32 addr, int32 size) if(sym->value >= eaddr) break; if(addr < sym->value) { - Bprint(&bso, "%-20s %.8ux| 00 ...\n", "(pre-pad)", addr); + Bprint(&bso, "\t%.8ux| 00 ...\n", addr); addr = sym->value; } - Bprint(&bso, "%-20s %.8ux|", sym->name, (uint)addr); + Bprint(&bso, "%s\n\t%.8ux|", sym->name, (uint)addr); p = sym->p; ep = p + sym->np; - while(p < ep) + while(p < ep) { + if(p > sym->p && (int)(p-sym->p)%16 == 0) + Bprint(&bso, "\n\t%.8ux|", (uint)(addr+(p-sym->p))); Bprint(&bso, " %.2ux", *p++); + } addr += sym->np; for(; addr < sym->value+sym->size; addr++) Bprint(&bso, " %.2ux", 0); Bprint(&bso, "\n"); + + if(linkmode == LinkExternal) { + for(i=0; i<sym->nr; i++) { + r = &sym->r[i]; + rsname = ""; + if(r->sym) + rsname = r->sym->name; + typ = "?"; + switch(r->type) { + case D_ADDR: + typ = "addr"; + break; + case D_PCREL: + typ = "pcrel"; + break; + } + Bprint(&bso, "\treloc %.8ux/%d %s %s+%#llx [%#llx]\n", + (uint)(sym->value+r->off), r->siz, typ, rsname, (vlong)r->add, (vlong)(r->sym->value+r->add)); + } + } } if(addr < eaddr) - Bprint(&bso, "%-20s %.8ux| 00 ...\n", "(post-pad)", (uint)addr); - Bprint(&bso, "%-20s %.8ux|\n", "", (uint)eaddr); + Bprint(&bso, "\t%.8ux| 00 ...\n", (uint)addr); + Bprint(&bso, "\t%.8ux|\n", (uint)eaddr); } void @@ -656,7 +729,7 @@ addstring(Sym *s, char *str) } vlong -setuintxx(Sym *s, vlong off, uint64 v, int wid) +setuintxx(Sym *s, vlong off, uint64 v, vlong wid) { int32 i, fl; vlong o; @@ -696,7 +769,7 @@ setuintxx(Sym *s, vlong off, uint64 v, int wid) vlong adduintxx(Sym *s, uint64 v, int wid) { - int32 off; + vlong off; off = s->size; setuintxx(s, off, v, wid); @@ -752,7 +825,7 @@ setuint64(Sym *s, vlong r, uint64 v) } vlong -addaddrplus(Sym *s, Sym *t, int32 add) +addaddrplus(Sym *s, Sym *t, vlong add) { vlong i; Reloc *r; @@ -773,7 +846,7 @@ addaddrplus(Sym *s, Sym *t, int32 add) } static vlong -addaddrplus4(Sym *s, Sym *t, int32 add) +addaddrplus4(Sym *s, Sym *t, vlong add) { vlong i; Reloc *r; @@ -794,7 +867,7 @@ addaddrplus4(Sym *s, Sym *t, int32 add) } vlong -addpcrelplus(Sym *s, Sym *t, int32 add) +addpcrelplus(Sym *s, Sym *t, vlong add) { vlong i; Reloc *r; @@ -821,7 +894,7 @@ addaddr(Sym *s, Sym *t) } vlong -setaddrplus(Sym *s, vlong off, Sym *t, int32 add) +setaddrplus(Sym *s, vlong off, Sym *t, vlong add) { Reloc *r; @@ -883,42 +956,47 @@ dosymtype(void) } static int32 -alignsymsize(int32 s) +symalign(Sym *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; + int32 align; + + if(s->align != 0) + return s->align; + + align = MaxAlign; + while(align > s->size && align > 1) + align >>= 1; + if(align < s->align) + align = s->align; + return align; +} + +static vlong +aligndatsize(vlong datsize, Sym *s) +{ + return rnd(datsize, symalign(s)); } +// maxalign returns the maximum required alignment for +// the list of symbols s; the list stops when s->type exceeds type. static int32 -aligndatsize(int32 datsize, Sym *s) +maxalign(Sym *s, int type) { - 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); + int32 align, max; + + max = 0; + for(; s != S && s->type <= type; s = s->next) { + align = symalign(s); + if(max < align) + max = align; } - return datsize; + return max; } static void -gcaddsym(Sym *gc, Sym *s, int32 off) +gcaddsym(Sym *gc, Sym *s, vlong off) { - int32 a; + vlong a; Sym *gotype; if(s->size < PtrSize) @@ -944,9 +1022,23 @@ gcaddsym(Sym *gc, Sym *s, int32 off) } void +growdatsize(vlong *datsizep, Sym *s) +{ + vlong datsize; + + datsize = *datsizep; + if(s->size < 0) + diag("negative size (datsize = %lld, s->size = %lld)", datsize, s->size); + if(datsize + s->size < datsize) + diag("symbol too large (datsize = %lld, s->size = %lld)", datsize, s->size); + *datsizep = datsize + s->size; +} + +void dodata(void) { - int32 t, datsize; + int32 n; + vlong datsize; Section *sect; Sym *s, *last, **l; Sym *gcdata1, *gcbss1; @@ -956,11 +1048,11 @@ dodata(void) Bflush(&bso); // define garbage collection symbols - gcdata1 = lookup("gcdata1", 0); - gcdata1->type = SGCDATA; + gcdata1 = lookup("gcdata", 0); + gcdata1->type = STYPE; gcdata1->reachable = 1; - gcbss1 = lookup("gcbss1", 0); - gcbss1->type = SGCBSS; + gcbss1 = lookup("gcbss", 0); + gcbss1->type = STYPE; gcbss1->reachable = 1; // size of .data and .bss section. the zero value is later replaced by the actual size of the section. @@ -995,7 +1087,11 @@ dodata(void) * to assign addresses, record all the necessary * dynamic relocations. these will grow the relocation * symbol, which is itself data. + * + * on darwin, we need the symbol table numbers for dynreloc. */ + if(HEADTYPE == Hdarwin) + machosymorder(); dynreloc(); /* some symbols may no longer belong in datap (Mach-O) */ @@ -1034,52 +1130,54 @@ dodata(void) 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->align = symalign(s); + datsize = rnd(datsize, sect->align); sect->vaddr = datsize; s->sect = sect; s->type = SDATA; s->value = datsize; - datsize += rnd(s->size, PtrSize); + growdatsize(&datsize, s); sect->len = datsize - sect->vaddr; } /* pointer-free data */ sect = addsection(&segdata, ".noptrdata", 06); + sect->align = maxalign(s, SDATARELRO-1); + datsize = rnd(datsize, sect->align); sect->vaddr = datsize; lookup("noptrdata", 0)->sect = sect; lookup("enoptrdata", 0)->sect = sect; for(; s != nil && s->type < SDATARELRO; s = s->next) { + datsize = aligndatsize(datsize, s); s->sect = sect; s->type = SDATA; - t = alignsymsize(s->size); - datsize = aligndatsize(datsize, s); s->value = datsize; - datsize += t; + growdatsize(&datsize, s); } sect->len = datsize - sect->vaddr; - datsize = rnd(datsize, PtrSize); /* dynamic relocated rodata */ if(flag_shared) { sect = addsection(&segdata, ".data.rel.ro", 06); + sect->align = maxalign(s, SDATARELRO); + datsize = rnd(datsize, sect->align); 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); + datsize = aligndatsize(datsize, s); s->sect = sect; s->type = SDATA; s->value = datsize; - datsize += rnd(s->size, PtrSize); + growdatsize(&datsize, s); } sect->len = datsize - sect->vaddr; - datsize = rnd(datsize, PtrSize); } /* data */ sect = addsection(&segdata, ".data", 06); + sect->align = maxalign(s, SBSS-1); + datsize = rnd(datsize, sect->align); sect->vaddr = datsize; lookup("data", 0)->sect = sect; lookup("edata", 0)->sect = sect; @@ -1090,158 +1188,166 @@ dodata(void) } 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; + growdatsize(&datsize, s); } 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->align = maxalign(s, SNOPTRBSS-1); + datsize = rnd(datsize, sect->align); 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; + growdatsize(&datsize, s); } 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->align = maxalign(s, SNOPTRBSS); + datsize = rnd(datsize, sect->align); 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); + for(; s != nil && s->type == SNOPTRBSS; s = s->next) { datsize = aligndatsize(datsize, s); + s->sect = sect; s->value = datsize; - datsize += t; + growdatsize(&datsize, s); } sect->len = datsize - sect->vaddr; lookup("end", 0)->sect = sect; + // 6g uses 4-byte relocation offsets, so the entire segment must fit in 32 bits. + if(datsize != (uint32)datsize) { + diag("data or bss segment too large"); + } + + if(iself && linkmode == LinkExternal && s != nil && s->type == STLSBSS && HEADTYPE != Hopenbsd) { + sect = addsection(&segdata, ".tbss", 06); + sect->align = PtrSize; + sect->vaddr = 0; + datsize = 0; + for(; s != nil && s->type == STLSBSS; s = s->next) { + datsize = aligndatsize(datsize, s); + s->sect = sect; + s->value = datsize; + growdatsize(&datsize, s); + } + sect->len = datsize; + } + + if(s != nil) { + cursym = nil; + diag("unexpected symbol type %d for %s", s->type, s->name); + } + /* we finished segdata, begin segtext */ + s = datap; /* read-only data */ sect = addsection(&segtext, ".rodata", 04); + sect->align = maxalign(s, STYPELINK-1); sect->vaddr = 0; lookup("rodata", 0)->sect = sect; lookup("erodata", 0)->sect = sect; datsize = 0; - s = datap; for(; s != nil && s->type < STYPELINK; s = s->next) { + datsize = aligndatsize(datsize, s); s->sect = sect; - if(s->align != 0) - datsize = rnd(datsize, s->align); s->type = SRODATA; s->value = datsize; - datsize += rnd(s->size, PtrSize); + growdatsize(&datsize, s); } sect->len = datsize - sect->vaddr; - datsize = rnd(datsize, PtrSize); - /* type */ + /* typelink */ sect = addsection(&segtext, ".typelink", 04); + sect->align = maxalign(s, STYPELINK); + datsize = rnd(datsize, sect->align); sect->vaddr = datsize; lookup("typelink", 0)->sect = sect; lookup("etypelink", 0)->sect = sect; for(; s != nil && s->type == STYPELINK; s = s->next) { + datsize = aligndatsize(datsize, s); 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; + growdatsize(&datsize, s); } sect->len = datsize - sect->vaddr; - datsize = rnd(datsize, PtrSize); /* gosymtab */ sect = addsection(&segtext, ".gosymtab", 04); + sect->align = maxalign(s, SPCLNTAB-1); + datsize = rnd(datsize, sect->align); sect->vaddr = datsize; lookup("symtab", 0)->sect = sect; lookup("esymtab", 0)->sect = sect; for(; s != nil && s->type < SPCLNTAB; s = s->next) { + datsize = aligndatsize(datsize, s); s->sect = sect; s->type = SRODATA; s->value = datsize; - datsize += s->size; + growdatsize(&datsize, s); } sect->len = datsize - sect->vaddr; - datsize = rnd(datsize, PtrSize); /* gopclntab */ sect = addsection(&segtext, ".gopclntab", 04); + sect->align = maxalign(s, SELFROSECT-1); + datsize = rnd(datsize, sect->align); sect->vaddr = datsize; lookup("pclntab", 0)->sect = sect; lookup("epclntab", 0)->sect = sect; for(; s != nil && s->type < SELFROSECT; s = s->next) { + datsize = aligndatsize(datsize, s); s->sect = sect; s->type = SRODATA; s->value = datsize; - datsize += s->size; + growdatsize(&datsize, s); } sect->len = datsize - sect->vaddr; - datsize = rnd(datsize, PtrSize); - /* read-only ELF sections */ + /* read-only ELF, Mach-O sections */ for(; s != nil && s->type < SELFSECT; s = s->next) { sect = addsection(&segtext, s->name, 04); - if(s->align != 0) - datsize = rnd(datsize, s->align); + sect->align = symalign(s); + datsize = rnd(datsize, sect->align); sect->vaddr = datsize; s->sect = sect; s->type = SRODATA; s->value = datsize; - datsize += rnd(s->size, PtrSize); + growdatsize(&datsize, s); sect->len = datsize - sect->vaddr; } + + // 6g uses 4-byte relocation offsets, so the entire segment must fit in 32 bits. + if(datsize != (uint32)datsize) { + diag("text segment too large"); + } + + /* number the sections */ + n = 1; + for(sect = segtext.sect; sect != nil; sect = sect->next) + sect->extnum = n++; + for(sect = segdata.sect; sect != nil; sect = sect->next) + sect->extnum = n++; } // assign addresses to text @@ -1259,6 +1365,7 @@ textaddress(void) // Could parallelize, by assigning to text // and then letting threads copy down, but probably not worth it. sect = segtext.sect; + sect->align = FuncAlign; lookup("text", 0)->sect = sect; lookup("etext", 0)->sect = sect; va = INITTEXT; @@ -1282,11 +1389,6 @@ 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); - sect->len = va - sect->vaddr; } @@ -1295,17 +1397,19 @@ void address(void) { Section *s, *text, *data, *rodata, *symtab, *pclntab, *noptr, *bss, *noptrbss, *datarelro; - Section *gcdata, *gcbss, *typelink; + Section *typelink; Sym *sym, *sub; uvlong va; + vlong vlen; va = INITTEXT; segtext.rwx = 05; segtext.vaddr = va; segtext.fileoff = HEADR; for(s=segtext.sect; s != nil; s=s->next) { + va = rnd(va, s->align); s->vaddr = va; - va += rnd(s->len, PtrSize); + va += s->len; } segtext.len = va - INITTEXT; segtext.filelen = segtext.len; @@ -1326,9 +1430,11 @@ address(void) noptrbss = nil; datarelro = nil; for(s=segdata.sect; s != nil; s=s->next) { + vlen = s->len; + if(s->next) + vlen = s->next->vaddr - s->vaddr; s->vaddr = va; - va += s->len; - segdata.filelen += s->len; + va += vlen; segdata.len = va - segdata.vaddr; if(strcmp(s->name, ".data") == 0) data = s; @@ -1341,14 +1447,12 @@ address(void) if(strcmp(s->name, ".data.rel.ro") == 0) datarelro = s; } - segdata.filelen -= bss->len + noptrbss->len; // deduct .bss + segdata.filelen = bss->vaddr - segdata.vaddr; text = segtext.sect; rodata = text->next; typelink = rodata->next; - gcdata = typelink->next; - gcbss = gcdata->next; - symtab = gcbss->next; + symtab = typelink->next; pclntab = symtab->next; for(sym = datap; sym != nil; sym = sym->next) { @@ -1371,10 +1475,15 @@ address(void) 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); + + sym = lookup("gcdata", 0); + xdefine("egcdata", STYPE, symaddr(sym) + sym->size); + lookup("egcdata", 0)->sect = sym->sect; + + sym = lookup("gcbss", 0); + xdefine("egcbss", STYPE, symaddr(sym) + sym->size); + lookup("egcbss", 0)->sect = sym->sect; + xdefine("symtab", SRODATA, symtab->vaddr); xdefine("esymtab", SRODATA, symtab->vaddr + symtab->len); xdefine("pclntab", SRODATA, pclntab->vaddr); diff --git a/src/cmd/ld/doc.go b/src/cmd/ld/doc.go index bad4e540f..3493f41d8 100644 --- a/src/cmd/ld/doc.go +++ b/src/cmd/ld/doc.go @@ -31,19 +31,19 @@ Options new in this version: is statically linked and does not refer to a dynamic linker. Without this option (the default), the binary's contents are identical but it is loaded with a dynamic linker. This flag cannot be used when $GOOS is windows. - -Hdarwin (only in 6l/8l) + -H darwin (only in 6l/8l) Write Apple Mach-O binaries (default when $GOOS is darwin) - -Hlinux + -H linux Write Linux ELF binaries (default when $GOOS is linux) - -Hfreebsd + -H freebsd Write FreeBSD ELF binaries (default when $GOOS is freebsd) - -Hnetbsd (only in 6l/8l) + -H netbsd Write NetBSD ELF binaries (default when $GOOS is netbsd) - -Hopenbsd (only in 6l/8l) + -H openbsd (only in 6l/8l) Write OpenBSD ELF binaries (default when $GOOS is openbsd) - -Hwindows (only in 6l/8l) + -H windows (only in 6l/8l) Write Windows PE32+ Console binaries (default when $GOOS is windows) - -Hwindowsgui (only in 6l/8l) + -H windowsgui (only in 6l/8l) Write Windows PE32+ GUI binaries -I interpreter Set the ELF dynamic linker to use. @@ -71,5 +71,22 @@ Options new in this version: NOTE: it only eliminates false positives caused by other function calls, not false positives caused by dead temporaries stored in the current function call. + -linkmode argument + Set the linkmode. The argument must be one of + internal, external, or auto. The default is auto. + This sets the linking mode as described in + ../cgo/doc.go. + -tmpdir dir + Set the location to use for any temporary files. The + default is a newly created directory that is removed + after the linker completes. Temporary files are only + used in external linking mode. + -extld name + Set the name of the external linker to use in external + linking mode. The default is "gcc". + -extldflags flags + Set space-separated trailing flags to pass to the + external linker in external linking mode. The default + is to not pass any additional trailing flags. */ package main diff --git a/src/cmd/ld/dwarf.c b/src/cmd/ld/dwarf.c index d6a357e49..98b03f1c3 100644 --- a/src/cmd/ld/dwarf.c +++ b/src/cmd/ld/dwarf.c @@ -27,10 +27,16 @@ static vlong abbrevo; static vlong abbrevsize; +static Sym* abbrevsym; +static vlong abbrevsympos; static vlong lineo; static vlong linesize; +static Sym* linesym; +static vlong linesympos; static vlong infoo; // also the base for DWDie->offs and reference attributes. static vlong infosize; +static Sym* infosym; +static vlong infosympos; static vlong frameo; static vlong framesize; static vlong pubnameso; @@ -42,6 +48,18 @@ static vlong arangessize; static vlong gdbscripto; static vlong gdbscriptsize; +static Sym *infosec; +static vlong inforeloco; +static vlong inforelocsize; + +static Sym *arangessec; +static vlong arangesreloco; +static vlong arangesrelocsize; + +static Sym *linesec; +static vlong linereloco; +static vlong linerelocsize; + static char gdbscript[1024]; /* @@ -150,6 +168,7 @@ enum DW_ABRV_IFACETYPE, DW_ABRV_MAPTYPE, DW_ABRV_PTRTYPE, + DW_ABRV_BARE_PTRTYPE, // only for void*, no DW_AT_type attr to please gdb 6. DW_ABRV_SLICETYPE, DW_ABRV_STRINGTYPE, DW_ABRV_STRUCTTYPE, @@ -303,6 +322,12 @@ static struct DWAbbrev { DW_AT_type, DW_FORM_ref_addr, 0, 0 }, + /* BARE_PTRTYPE */ + { + DW_TAG_pointer_type, DW_CHILDREN_no, + DW_AT_name, DW_FORM_string, + 0, 0 + }, /* SLICETYPE */ { @@ -485,26 +510,43 @@ mkindex(DWDie *die) die->hash = mal(HASHSIZE * sizeof(DWDie*)); } +static DWDie* +walktypedef(DWDie *die) +{ + DWAttr *attr; + + // Resolve typedef if present. + if (die->abbrev == DW_ABRV_TYPEDECL) { + for (attr = die->attr; attr; attr = attr->link) { + if (attr->atr == DW_AT_type && attr->cls == DW_CLS_REFERENCE && attr->data != nil) { + return (DWDie*)attr->data; + } + } + } + return die; +} + // Find child by AT_name using hashtable if available or linear scan // if not. static DWDie* find(DWDie *die, char* name) { - DWDie *a, *b; + DWDie *a, *b, *die2; int h; +top: if (die->hash == nil) { for (a = die->child; a != nil; a = a->link) if (strcmp(name, getattr(a, DW_AT_name)->data) == 0) return a; - return nil; + goto notfound; } h = hashstr(name); a = die->hash[h]; if (a == nil) - return nil; + goto notfound; if (strcmp(name, getattr(a, DW_AT_name)->data) == 0) @@ -522,6 +564,14 @@ find(DWDie *die, char* name) a = b; b = b->hlink; } + +notfound: + die2 = walktypedef(die); + if(die2 != die) { + die = die2; + goto top; + } + return nil; } @@ -531,12 +581,40 @@ find_or_diag(DWDie *die, char* name) DWDie *r; r = find(die, name); if (r == nil) { - diag("dwarf find: %s has no %s", getattr(die, DW_AT_name)->data, name); + diag("dwarf find: %s %p has no %s", getattr(die, DW_AT_name)->data, die, name); errorexit(); } return r; } +static void +adddwarfrel(Sym* sec, Sym* sym, vlong offsetbase, int siz, vlong addend) +{ + Reloc *r; + + r = addrel(sec); + r->sym = sym; + r->xsym = sym; + r->off = cpos() - offsetbase; + r->siz = siz; + r->type = D_ADDR; + r->add = addend; + r->xadd = addend; + if(iself && thechar == '6') + addend = 0; + switch(siz) { + case 4: + LPUT(addend); + break; + case 8: + VPUT(addend); + break; + default: + diag("bad size in adddwarfrel"); + break; + } +} + static DWAttr* newrefattr(DWDie *die, uint8 attr, DWDie* ref) { @@ -548,14 +626,32 @@ newrefattr(DWDie *die, uint8 attr, DWDie* ref) static int fwdcount; static void -putattr(int form, int cls, vlong value, char *data) +putattr(int abbrev, int form, int cls, vlong value, char *data) { + vlong off; + switch(form) { case DW_FORM_addr: // address + if(linkmode == LinkExternal) { + value -= ((Sym*)data)->value; + adddwarfrel(infosec, (Sym*)data, infoo, PtrSize, value); + break; + } addrput(value); break; case DW_FORM_block1: // block + if(cls == DW_CLS_ADDRESS) { + cput(1+PtrSize); + cput(DW_OP_addr); + if(linkmode == LinkExternal) { + value -= ((Sym*)data)->value; + adddwarfrel(infosec, (Sym*)data, infoo, PtrSize, value); + break; + } + addrput(value); + break; + } value &= 0xff; cput(value); while(value--) @@ -591,6 +687,10 @@ putattr(int form, int cls, vlong value, char *data) break; case DW_FORM_data4: // constant, {line,loclist,mac,rangelist}ptr + if(linkmode == LinkExternal && cls == DW_CLS_PTR) { + adddwarfrel(infosec, linesym, infoo, 4, value); + break; + } LPUT(value); break; @@ -615,13 +715,25 @@ putattr(int form, int cls, vlong value, char *data) break; case DW_FORM_ref_addr: // reference to a DIE in the .info section + // In DWARF 2 (which is what we claim to generate), + // the ref_addr is the same size as a normal address. + // In DWARF 3 it is always 32 bits, unless emitting a large + // (> 4 GB of debug info aka "64-bit") unit, which we don't implement. if (data == nil) { - diag("dwarf: null reference"); - LPUT(0); // invalid dwarf, gdb will complain. + diag("dwarf: null reference in %d", abbrev); + if(PtrSize == 8) + VPUT(0); // invalid dwarf, gdb will complain. + else + LPUT(0); // invalid dwarf, gdb will complain. } else { - if (((DWDie*)data)->offs == 0) + off = ((DWDie*)data)->offs; + if (off == 0) fwdcount++; - LPUT(((DWDie*)data)->offs); + if(linkmode == LinkExternal) { + adddwarfrel(infosec, infosym, infoo, PtrSize, off); + break; + } + addrput(off); } break; @@ -654,12 +766,12 @@ putattrs(int abbrev, DWAttr* attr) for(af = abbrevs[abbrev].attr; af->attr; af++) if (attrs[af->attr]) - putattr(af->form, + putattr(abbrev, af->form, attrs[af->attr]->cls, attrs[af->attr]->value, attrs[af->attr]->data); else - putattr(af->form, 0, 0, 0); + putattr(abbrev, af->form, 0, 0, nil); } static void putdie(DWDie* die); @@ -729,16 +841,9 @@ newmemberoffsetattr(DWDie *die, int32 offs) // GDB doesn't like DW_FORM_addr for DW_AT_location, so emit a // location expression that evals to a const. static void -newabslocexprattr(DWDie *die, vlong addr) +newabslocexprattr(DWDie *die, vlong addr, Sym *sym) { - char block[10]; - int i; - - i = 0; - block[i++] = DW_OP_constu; - i += uleb128enc(addr, block+i); - newattr(die, DW_AT_location, DW_CLS_BLOCK, i, mal(i)); - memmove(die->attr->data, block, i); + newattr(die, DW_AT_location, DW_CLS_ADDRESS, addr, (char*)sym); } @@ -766,6 +871,31 @@ lookup_or_diag(char *n) return s; } +static void +dotypedef(DWDie *parent, char *name, DWDie *def) +{ + DWDie *die; + + // Only emit typedefs for real names. + if(strncmp(name, "map[", 4) == 0) + return; + if(strncmp(name, "struct {", 8) == 0) + return; + if(strncmp(name, "chan ", 5) == 0) + return; + if(*name == '[' || *name == '*') + return; + if(def == nil) + diag("dwarf: bad def in dotypedef"); + + // The typedef entry must be created after the def, + // so that future lookups will find the typedef instead + // of the real definition. This hooks the typedef into any + // circular definition loops, so that gdb can understand them. + die = newdie(parent, DW_ABRV_TYPEDECL, name); + newrefattr(die, DW_AT_type, def); +} + // Define gotype, for composite ones recurse into constituents. static DWDie* defgotype(Sym *gotype) @@ -840,6 +970,7 @@ defgotype(Sym *gotype) case KindArray: die = newdie(&dwtypes, DW_ABRV_ARRAYTYPE, name); + dotypedef(&dwtypes, name, die); newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); s = decodetype_arrayelem(gotype); newrefattr(die, DW_AT_type, defgotype(s)); @@ -857,6 +988,7 @@ defgotype(Sym *gotype) case KindFunc: die = newdie(&dwtypes, DW_ABRV_FUNCTYPE, name); + dotypedef(&dwtypes, name, die); newrefattr(die, DW_AT_type, find_or_diag(&dwtypes, "void")); nfields = decodetype_funcincount(gotype); for (i = 0; i < nfields; i++) { @@ -876,6 +1008,7 @@ defgotype(Sym *gotype) case KindInterface: die = newdie(&dwtypes, DW_ABRV_IFACETYPE, name); + dotypedef(&dwtypes, name, die); newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); nfields = decodetype_ifacemethodcount(gotype); if (nfields == 0) @@ -895,12 +1028,14 @@ defgotype(Sym *gotype) case KindPtr: die = newdie(&dwtypes, DW_ABRV_PTRTYPE, name); + dotypedef(&dwtypes, name, die); s = decodetype_ptrelem(gotype); newrefattr(die, DW_AT_type, defgotype(s)); break; case KindSlice: die = newdie(&dwtypes, DW_ABRV_SLICETYPE, name); + dotypedef(&dwtypes, name, die); newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); s = decodetype_arrayelem(gotype); newrefattr(die, DW_AT_internal_elem_type, defgotype(s)); @@ -913,6 +1048,7 @@ defgotype(Sym *gotype) case KindStruct: die = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, name); + dotypedef(&dwtypes, name, die); newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); nfields = decodetype_structfieldcount(gotype); for (i = 0; i < nfields; i++) { @@ -927,8 +1063,7 @@ defgotype(Sym *gotype) break; case KindUnsafePointer: - die = newdie(&dwtypes, DW_ABRV_PTRTYPE, name); - newrefattr(die, DW_AT_type, find(&dwtypes, "void")); + die = newdie(&dwtypes, DW_ABRV_BARE_PTRTYPE, name); break; default: @@ -998,7 +1133,7 @@ synthesizestringtypes(DWDie* die) { DWDie *prototype; - prototype = defgotype(lookup_or_diag("type.runtime._string")); + prototype = walktypedef(defgotype(lookup_or_diag("type.runtime._string"))); if (prototype == nil) return; @@ -1014,7 +1149,7 @@ synthesizeslicetypes(DWDie *die) { DWDie *prototype, *elem; - prototype = defgotype(lookup_or_diag("type.runtime.slice")); + prototype = walktypedef(defgotype(lookup_or_diag("type.runtime.slice"))); if (prototype == nil) return; @@ -1042,90 +1177,85 @@ mkinternaltypename(char *base, char *arg1, char *arg2) return n; } - // synthesizemaptypes is way too closely married to runtime/hashmap.c enum { - MaxValsize = 256 - 64 + MaxKeySize = 128, + MaxValSize = 128, + BucketSize = 8, }; static void synthesizemaptypes(DWDie *die) { - DWDie *hash, *hash_subtable, *hash_entry, - *dwh, *dwhs, *dwhe, *dwhash, *keytype, *valtype, *fld; - int hashsize, keysize, valsize, datsize, valsize_in_hash, datavo; + DWDie *hash, *bucket, *dwh, *dwhk, *dwhv, *dwhb, *keytype, *valtype, *fld; + int indirect_key, indirect_val; + int keysize, valsize; DWAttr *a; - hash = defgotype(lookup_or_diag("type.runtime.hmap")); - hash_subtable = defgotype(lookup_or_diag("type.runtime.hash_subtable")); - hash_entry = defgotype(lookup_or_diag("type.runtime.hash_entry")); - - if (hash == nil || hash_subtable == nil || hash_entry == nil) - return; + hash = walktypedef(defgotype(lookup_or_diag("type.runtime.hmap"))); + bucket = walktypedef(defgotype(lookup_or_diag("type.runtime.bucket"))); - dwhash = (DWDie*)getattr(find_or_diag(hash_entry, "hash"), DW_AT_type)->data; - if (dwhash == nil) + if (hash == nil) return; - hashsize = getattr(dwhash, DW_AT_byte_size)->value; - for (; die != nil; die = die->link) { if (die->abbrev != DW_ABRV_MAPTYPE) continue; - keytype = (DWDie*) getattr(die, DW_AT_internal_key_type)->data; - valtype = (DWDie*) getattr(die, DW_AT_internal_val_type)->data; + keytype = walktypedef((DWDie*) getattr(die, DW_AT_internal_key_type)->data); + valtype = walktypedef((DWDie*) getattr(die, DW_AT_internal_val_type)->data); + // compute size info like hashmap.c does. a = getattr(keytype, DW_AT_byte_size); keysize = a ? a->value : PtrSize; // We don't store size with Pointers - a = getattr(valtype, DW_AT_byte_size); valsize = a ? a->value : PtrSize; + indirect_key = 0; + indirect_val = 0; + if(keysize > MaxKeySize) { + keysize = PtrSize; + indirect_key = 1; + } + if(valsize > MaxValSize) { + valsize = PtrSize; + indirect_val = 1; + } - // This is what happens in hash_init and makemap_c - valsize_in_hash = valsize; - if (valsize > MaxValsize) - valsize_in_hash = PtrSize; - datavo = keysize; - if (valsize_in_hash >= PtrSize) - datavo = rnd(keysize, PtrSize); - datsize = datavo + valsize_in_hash; - if (datsize < PtrSize) - datsize = PtrSize; - datsize = rnd(datsize, PtrSize); - - // Construct struct hash_entry<K,V> - dwhe = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, - mkinternaltypename("hash_entry", - getattr(keytype, DW_AT_name)->data, - getattr(valtype, DW_AT_name)->data)); - - fld = newdie(dwhe, DW_ABRV_STRUCTFIELD, "hash"); - newrefattr(fld, DW_AT_type, dwhash); - newmemberoffsetattr(fld, 0); - - fld = newdie(dwhe, DW_ABRV_STRUCTFIELD, "key"); - newrefattr(fld, DW_AT_type, keytype); - newmemberoffsetattr(fld, hashsize); - - fld = newdie(dwhe, DW_ABRV_STRUCTFIELD, "val"); - if (valsize > MaxValsize) - valtype = defptrto(valtype); - newrefattr(fld, DW_AT_type, valtype); - newmemberoffsetattr(fld, hashsize + datavo); - newattr(dwhe, DW_AT_byte_size, DW_CLS_CONSTANT, hashsize + datsize, nil); + // Construct type to represent an array of BucketSize keys + dwhk = newdie(&dwtypes, DW_ABRV_ARRAYTYPE, + mkinternaltypename("[]key", + getattr(keytype, DW_AT_name)->data, nil)); + newattr(dwhk, DW_AT_byte_size, DW_CLS_CONSTANT, BucketSize * keysize, 0); + newrefattr(dwhk, DW_AT_type, indirect_key ? defptrto(keytype) : keytype); + fld = newdie(dwhk, DW_ABRV_ARRAYRANGE, "size"); + newattr(fld, DW_AT_upper_bound, DW_CLS_CONSTANT, BucketSize, 0); + newrefattr(fld, DW_AT_type, find_or_diag(&dwtypes, "uintptr")); + + // Construct type to represent an array of BucketSize values + dwhv = newdie(&dwtypes, DW_ABRV_ARRAYTYPE, + mkinternaltypename("[]val", + getattr(valtype, DW_AT_name)->data, nil)); + newattr(dwhv, DW_AT_byte_size, DW_CLS_CONSTANT, BucketSize * valsize, 0); + newrefattr(dwhv, DW_AT_type, indirect_val ? defptrto(valtype) : valtype); + fld = newdie(dwhv, DW_ABRV_ARRAYRANGE, "size"); + newattr(fld, DW_AT_upper_bound, DW_CLS_CONSTANT, BucketSize, 0); + newrefattr(fld, DW_AT_type, find_or_diag(&dwtypes, "uintptr")); - // Construct hash_subtable<hash_entry<K,V>> - dwhs = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, - mkinternaltypename("hash_subtable", - getattr(keytype, DW_AT_name)->data, - getattr(valtype, DW_AT_name)->data)); - copychildren(dwhs, hash_subtable); - substitutetype(dwhs, "last", defptrto(dwhe)); - substitutetype(dwhs, "entry", dwhe); // todo: []hash_entry with dynamic size - newattr(dwhs, DW_AT_byte_size, DW_CLS_CONSTANT, - getattr(hash_subtable, DW_AT_byte_size)->value, nil); + // Construct bucket<K,V> + dwhb = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, + mkinternaltypename("bucket", + getattr(keytype, DW_AT_name)->data, + getattr(valtype, DW_AT_name)->data)); + copychildren(dwhb, bucket); + fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "keys"); + newrefattr(fld, DW_AT_type, dwhk); + newmemberoffsetattr(fld, BucketSize + PtrSize); + fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "values"); + newrefattr(fld, DW_AT_type, dwhv); + newmemberoffsetattr(fld, BucketSize + PtrSize + BucketSize * keysize); + newattr(dwhb, DW_AT_byte_size, DW_CLS_CONSTANT, BucketSize + PtrSize + BucketSize * keysize + BucketSize * valsize, 0); + substitutetype(dwhb, "overflow", defptrto(dwhb)); // Construct hash<K,V> dwh = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, @@ -1133,10 +1263,12 @@ synthesizemaptypes(DWDie *die) getattr(keytype, DW_AT_name)->data, getattr(valtype, DW_AT_name)->data)); copychildren(dwh, hash); - substitutetype(dwh, "st", defptrto(dwhs)); + substitutetype(dwh, "buckets", defptrto(dwhb)); + substitutetype(dwh, "oldbuckets", defptrto(dwhb)); newattr(dwh, DW_AT_byte_size, DW_CLS_CONSTANT, getattr(hash, DW_AT_byte_size)->value, nil); + // make map type a pointer to hash<K,V> newrefattr(die, DW_AT_type, defptrto(dwh)); } } @@ -1149,9 +1281,9 @@ synthesizechantypes(DWDie *die) DWAttr *a; int elemsize, sudogsize; - sudog = defgotype(lookup_or_diag("type.runtime.sudog")); - waitq = defgotype(lookup_or_diag("type.runtime.waitq")); - hchan = defgotype(lookup_or_diag("type.runtime.hchan")); + sudog = walktypedef(defgotype(lookup_or_diag("type.runtime.sudog"))); + waitq = walktypedef(defgotype(lookup_or_diag("type.runtime.waitq"))); + hchan = walktypedef(defgotype(lookup_or_diag("type.runtime.hchan"))); if (sudog == nil || waitq == nil || hchan == nil) return; @@ -1220,7 +1352,7 @@ defdwsymb(Sym* sym, char *s, int t, vlong v, vlong size, int ver, Sym *gotype) case 'D': case 'B': dv = newdie(&dwglobals, DW_ABRV_VARIABLE, s); - newabslocexprattr(dv, v); + newabslocexprattr(dv, v, sym); if (ver == 0) newattr(dv, DW_AT_external, DW_CLS_FLAG, 1, 0); // fallthrough @@ -1260,7 +1392,7 @@ dwarfaddfrag(int n, char *frag) if (n >= ftabsize) { s = ftabsize; ftabsize = 1 + n + (n >> 2); - ftab = realloc(ftab, ftabsize * sizeof(ftab[0])); + ftab = erealloc(ftab, ftabsize * sizeof(ftab[0])); memset(ftab + s, 0, (ftabsize - s) * sizeof(ftab[0])); } @@ -1342,7 +1474,7 @@ addhistfile(char *zentry) if (histfilesize == histfilecap) { histfilecap = 2 * histfilecap + 2; - histfile = realloc(histfile, histfilecap * sizeof(char*)); + histfile = erealloc(histfile, histfilecap * sizeof(char*)); } if (histfilesize == 0) histfile[histfilesize++] = "<eof>"; @@ -1412,7 +1544,7 @@ checknesting(void) includestacksize += 1; includestacksize <<= 2; // print("checknesting: growing to %d\n", includestacksize); - includestack = realloc(includestack, includestacksize * sizeof *includestack); + includestack = erealloc(includestack, includestacksize * sizeof *includestack); } } @@ -1581,12 +1713,12 @@ mkvarname(char* name, int da) // flush previous compilation unit. static void -flushunit(DWDie *dwinfo, vlong pc, vlong unitstart, int32 header_length) +flushunit(DWDie *dwinfo, vlong pc, Sym *pcsym, vlong unitstart, int32 header_length) { vlong here; if (dwinfo != nil && pc != 0) { - newattr(dwinfo, DW_AT_high_pc, DW_CLS_ADDRESS, pc+1, 0); + newattr(dwinfo, DW_AT_high_pc, DW_CLS_ADDRESS, pc+1, (char*)pcsym); } if (unitstart >= 0) { @@ -1597,7 +1729,7 @@ flushunit(DWDie *dwinfo, vlong pc, vlong unitstart, int32 header_length) here = cpos(); cseek(unitstart); LPUT(here - unitstart - sizeof(int32)); // unit_length - WPUT(3); // dwarf version + WPUT(2); // dwarf version LPUT(header_length); // header length starting here cseek(here); } @@ -1607,7 +1739,7 @@ static void writelines(void) { Prog *q; - Sym *s; + Sym *s, *epcs; Auto *a; vlong unitstart, headerend, offs; vlong pc, epc, lc, llc, lline; @@ -1618,10 +1750,15 @@ writelines(void) DWDie *varhash[HASHSIZE]; char *n, *nn; + if(linesec == S) + linesec = lookup(".dwarfline", 0); + linesec->nr = 0; + unitstart = -1; headerend = -1; pc = 0; epc = 0; + epcs = S; lc = 1; llc = 1; currfile = -1; @@ -1637,7 +1774,7 @@ writelines(void) // we're entering a new compilation unit if (inithist(s->autom)) { - flushunit(dwinfo, epc, unitstart, headerend - unitstart - 10); + flushunit(dwinfo, epc, epcs, unitstart, headerend - unitstart - 10); unitstart = cpos(); if(debug['v'] > 1) { @@ -1651,15 +1788,15 @@ writelines(void) lang = guesslang(histfile[1]); finddebugruntimepath(); - dwinfo = newdie(&dwroot, DW_ABRV_COMPUNIT, strdup(histfile[1])); + dwinfo = newdie(&dwroot, DW_ABRV_COMPUNIT, estrdup(histfile[1])); newattr(dwinfo, DW_AT_language, DW_CLS_CONSTANT,lang, 0); newattr(dwinfo, DW_AT_stmt_list, DW_CLS_PTR, unitstart - lineo, 0); - newattr(dwinfo, DW_AT_low_pc, DW_CLS_ADDRESS, s->text->pc, 0); + newattr(dwinfo, DW_AT_low_pc, DW_CLS_ADDRESS, s->text->pc, (char*)s); // Write .debug_line Line Number Program Header (sec 6.2.4) // Fields marked with (*) must be changed for 64-bit dwarf LPUT(0); // unit_length (*), will be filled in by flushunit. - WPUT(3); // dwarf version (appendix F) + WPUT(2); // dwarf version (appendix F) LPUT(0); // header_length (*), filled in by flushunit. // cpos == unitstart + 4 + 2 + 4 cput(1); // minimum_instruction_length @@ -1683,6 +1820,7 @@ writelines(void) pc = s->text->pc; epc = pc; + epcs = s; currfile = 1; lc = 1; llc = 1; @@ -1690,7 +1828,11 @@ writelines(void) cput(0); // start extended opcode uleb128put(1 + PtrSize); cput(DW_LNE_set_address); - addrput(pc); + + if(linkmode == LinkExternal) + adddwarfrel(linesec, s, lineo, PtrSize, 0); + else + addrput(pc); } if(s->text == nil) continue; @@ -1701,9 +1843,9 @@ writelines(void) } dwfunc = newdie(dwinfo, DW_ABRV_FUNCTION, s->name); - newattr(dwfunc, DW_AT_low_pc, DW_CLS_ADDRESS, s->value, 0); + newattr(dwfunc, DW_AT_low_pc, DW_CLS_ADDRESS, s->value, (char*)s); epc = s->value + s->size; - newattr(dwfunc, DW_AT_high_pc, DW_CLS_ADDRESS, epc, 0); + newattr(dwfunc, DW_AT_high_pc, DW_CLS_ADDRESS, epc, (char*)s); if (s->version == 0) newattr(dwfunc, DW_AT_external, DW_CLS_FLAG, 1, 0); @@ -1785,7 +1927,7 @@ writelines(void) dwfunc->hash = nil; } - flushunit(dwinfo, epc, unitstart, headerend - unitstart - 10); + flushunit(dwinfo, epc, epcs, unitstart, headerend - unitstart - 10); linesize = cpos() - lineo; } @@ -1909,6 +2051,13 @@ writeinfo(void) vlong unitstart, here; fwdcount = 0; + if (infosec == S) + infosec = lookup(".dwarfinfo", 0); + infosec->nr = 0; + + if(arangessec == S) + arangessec = lookup(".dwarfaranges", 0); + arangessec->nr = 0; for (compunit = dwroot.child; compunit; compunit = compunit->link) { unitstart = cpos(); @@ -1917,8 +2066,14 @@ writeinfo(void) // Fields marked with (*) must be changed for 64-bit dwarf // This must match COMPUNITHEADERSIZE above. LPUT(0); // unit_length (*), will be filled in later. - WPUT(3); // dwarf version (appendix F) - LPUT(0); // debug_abbrev_offset (*) + WPUT(2); // dwarf version (appendix F) + + // debug_abbrev_offset (*) + if(linkmode == LinkExternal) + adddwarfrel(infosec, abbrevsym, infoo, 4, 0); + else + LPUT(0); + cput(PtrSize); // address_size putdie(compunit); @@ -2006,6 +2161,7 @@ writearanges(void) DWAttr *b, *e; int headersize; vlong sectionstart; + vlong value; sectionstart = cpos(); headersize = rnd(4+2+4+1+1, PtrSize); // don't count unit_length field itself @@ -2021,12 +2177,22 @@ writearanges(void) // Write .debug_aranges Header + entry (sec 6.1.2) LPUT(headersize + 4*PtrSize - 4); // unit_length (*) WPUT(2); // dwarf version (appendix F) - LPUT(compunit->offs - COMPUNITHEADERSIZE); // debug_info_offset + + value = compunit->offs - COMPUNITHEADERSIZE; // debug_info_offset + if(linkmode == LinkExternal) + adddwarfrel(arangessec, infosym, sectionstart, 4, value); + else + LPUT(value); + cput(PtrSize); // address_size cput(0); // segment_size strnput("", headersize - (4+2+4+1+1)); // align to PtrSize - addrput(b->value); + if(linkmode == LinkExternal) + adddwarfrel(arangessec, (Sym*)b->data, sectionstart, PtrSize, b->value-((Sym*)b->data)->value); + else + addrput(b->value); + addrput(e->value - b->value); addrput(0); addrput(0); @@ -2057,6 +2223,27 @@ align(vlong size) strnput("", rnd(size, PEFILEALIGN) - size); } +static vlong +writedwarfreloc(Sym* s) +{ + int i; + vlong start; + Reloc *r; + + start = cpos(); + for(r = s->r; r < s->r+s->nr; r++) { + if(iself) + i = elfreloc1(r, r->off); + else if(HEADTYPE == Hdarwin) + i = machoreloc1(r, r->off); + else + i = -1; + if(i < 0) + diag("unsupported obj reloc %d/%d to %s", r->type, r->siz, r->sym->name); + } + return start; +} + /* * This is the main entry point for generating dwarf. After emitting * the mandatory debug_abbrev section, it calls writelines() to set up @@ -2075,6 +2262,9 @@ dwarfemitdebugsections(void) if(debug['w']) // disable dwarf return; + if(linkmode == LinkExternal && !iself) + return; + // For diagnostic messages. newattr(&dwtypes, DW_AT_name, DW_CLS_STRING, strlen("dwtypes"), "dwtypes"); @@ -2085,8 +2275,7 @@ dwarfemitdebugsections(void) // Some types that must exist to define other ones. newdie(&dwtypes, DW_ABRV_NULLTYPE, "<unspecified>"); newdie(&dwtypes, DW_ABRV_NULLTYPE, "void"); - newrefattr(newdie(&dwtypes, DW_ABRV_PTRTYPE, "unsafe.Pointer"), - DW_AT_type, find(&dwtypes, "void")); + newdie(&dwtypes, DW_ABRV_BARE_PTRTYPE, "unsafe.Pointer"); die = newdie(&dwtypes, DW_ABRV_BASETYPE, "uintptr"); // needed for array size newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_unsigned, 0); newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, PtrSize, 0); @@ -2157,6 +2346,20 @@ dwarfemitdebugsections(void) gdbscripto = writegdbscript(); gdbscriptsize = cpos() - gdbscripto; align(gdbscriptsize); + + while(cpos()&7) + cput(0); + inforeloco = writedwarfreloc(infosec); + inforelocsize = cpos() - inforeloco; + align(inforelocsize); + + arangesreloco = writedwarfreloc(arangessec); + arangesrelocsize = cpos() - arangesreloco; + align(arangesrelocsize); + + linereloco = writedwarfreloc(linesec); + linerelocsize = cpos() - linereloco; + align(linerelocsize); } /* @@ -2176,6 +2379,9 @@ enum ElfStrDebugRanges, ElfStrDebugStr, ElfStrGDBScripts, + ElfStrRelDebugInfo, + ElfStrRelDebugAranges, + ElfStrRelDebugLine, NElfStrDbg }; @@ -2199,13 +2405,72 @@ dwarfaddshstrings(Sym *shstrtab) elfstrdbg[ElfStrDebugRanges] = addstring(shstrtab, ".debug_ranges"); elfstrdbg[ElfStrDebugStr] = addstring(shstrtab, ".debug_str"); elfstrdbg[ElfStrGDBScripts] = addstring(shstrtab, ".debug_gdb_scripts"); + if(linkmode == LinkExternal) { + if(thechar == '6') { + elfstrdbg[ElfStrRelDebugInfo] = addstring(shstrtab, ".rela.debug_info"); + elfstrdbg[ElfStrRelDebugAranges] = addstring(shstrtab, ".rela.debug_aranges"); + elfstrdbg[ElfStrRelDebugLine] = addstring(shstrtab, ".rela.debug_line"); + } else { + elfstrdbg[ElfStrRelDebugInfo] = addstring(shstrtab, ".rel.debug_info"); + elfstrdbg[ElfStrRelDebugAranges] = addstring(shstrtab, ".rel.debug_aranges"); + elfstrdbg[ElfStrRelDebugLine] = addstring(shstrtab, ".rel.debug_line"); + } + + infosym = lookup(".debug_info", 0); + infosym->hide = 1; + + abbrevsym = lookup(".debug_abbrev", 0); + abbrevsym->hide = 1; + + linesym = lookup(".debug_line", 0); + linesym->hide = 1; + } } +// Add section symbols for DWARF debug info. This is called before +// dwarfaddelfheaders. void -dwarfaddelfheaders(void) +dwarfaddelfsectionsyms() +{ + if(infosym != nil) { + infosympos = cpos(); + putelfsectionsym(infosym, 0); + } + if(abbrevsym != nil) { + abbrevsympos = cpos(); + putelfsectionsym(abbrevsym, 0); + } + if(linesym != nil) { + linesympos = cpos(); + putelfsectionsym(linesym, 0); + } +} + +static void +dwarfaddelfrelocheader(int elfstr, ElfShdr *shdata, vlong off, vlong size) { ElfShdr *sh; + sh = newElfShdr(elfstrdbg[elfstr]); + if(thechar == '6') { + sh->type = SHT_RELA; + } else { + sh->type = SHT_REL; + } + sh->entsize = PtrSize*(2+(sh->type==SHT_RELA)); + sh->link = elfshname(".symtab")->shnum; + sh->info = shdata->shnum; + sh->off = off; + sh->size = size; + sh->addralign = PtrSize; + +} + +void +dwarfaddelfheaders(void) +{ + ElfShdr *sh, *shinfo, *sharanges, *shline; + if(debug['w']) // disable dwarf return; @@ -2214,12 +2479,17 @@ dwarfaddelfheaders(void) sh->off = abbrevo; sh->size = abbrevsize; sh->addralign = 1; + if(abbrevsympos > 0) + putelfsymshndx(abbrevsympos, sh->shnum); sh = newElfShdr(elfstrdbg[ElfStrDebugLine]); sh->type = SHT_PROGBITS; sh->off = lineo; sh->size = linesize; sh->addralign = 1; + if(linesympos > 0) + putelfsymshndx(linesympos, sh->shnum); + shline = sh; sh = newElfShdr(elfstrdbg[ElfStrDebugFrame]); sh->type = SHT_PROGBITS; @@ -2232,6 +2502,9 @@ dwarfaddelfheaders(void) sh->off = infoo; sh->size = infosize; sh->addralign = 1; + if(infosympos > 0) + putelfsymshndx(infosympos, sh->shnum); + shinfo = sh; if (pubnamessize > 0) { sh = newElfShdr(elfstrdbg[ElfStrDebugPubNames]); @@ -2249,12 +2522,14 @@ dwarfaddelfheaders(void) sh->addralign = 1; } + sharanges = nil; if (arangessize) { sh = newElfShdr(elfstrdbg[ElfStrDebugAranges]); sh->type = SHT_PROGBITS; sh->off = arangeso; sh->size = arangessize; sh->addralign = 1; + sharanges = sh; } if (gdbscriptsize) { @@ -2264,6 +2539,15 @@ dwarfaddelfheaders(void) sh->size = gdbscriptsize; sh->addralign = 1; } + + if(inforelocsize) + dwarfaddelfrelocheader(ElfStrRelDebugInfo, shinfo, inforeloco, inforelocsize); + + if(arangesrelocsize) + dwarfaddelfrelocheader(ElfStrRelDebugAranges, sharanges, arangesreloco, arangesrelocsize); + + if(linerelocsize) + dwarfaddelfrelocheader(ElfStrRelDebugLine, shline, linereloco, linerelocsize); } /* @@ -2298,42 +2582,42 @@ dwarfaddmachoheaders(void) ms->fileoffset = fakestart; ms->filesize = abbrevo-fakestart; - msect = newMachoSect(ms, "__debug_abbrev"); + msect = newMachoSect(ms, "__debug_abbrev", "__DWARF"); msect->off = abbrevo; msect->size = abbrevsize; ms->filesize += msect->size; - msect = newMachoSect(ms, "__debug_line"); + msect = newMachoSect(ms, "__debug_line", "__DWARF"); msect->off = lineo; msect->size = linesize; ms->filesize += msect->size; - msect = newMachoSect(ms, "__debug_frame"); + msect = newMachoSect(ms, "__debug_frame", "__DWARF"); msect->off = frameo; msect->size = framesize; ms->filesize += msect->size; - msect = newMachoSect(ms, "__debug_info"); + msect = newMachoSect(ms, "__debug_info", "__DWARF"); msect->off = infoo; msect->size = infosize; ms->filesize += msect->size; if (pubnamessize > 0) { - msect = newMachoSect(ms, "__debug_pubnames"); + msect = newMachoSect(ms, "__debug_pubnames", "__DWARF"); msect->off = pubnameso; msect->size = pubnamessize; ms->filesize += msect->size; } if (pubtypessize > 0) { - msect = newMachoSect(ms, "__debug_pubtypes"); + msect = newMachoSect(ms, "__debug_pubtypes", "__DWARF"); msect->off = pubtypeso; msect->size = pubtypessize; ms->filesize += msect->size; } if (arangessize > 0) { - msect = newMachoSect(ms, "__debug_aranges"); + msect = newMachoSect(ms, "__debug_aranges", "__DWARF"); msect->off = arangeso; msect->size = arangessize; ms->filesize += msect->size; @@ -2341,7 +2625,7 @@ dwarfaddmachoheaders(void) // TODO(lvd) fix gdb/python to load MachO (16 char section name limit) if (gdbscriptsize > 0) { - msect = newMachoSect(ms, "__debug_gdb_scripts"); + msect = newMachoSect(ms, "__debug_gdb_scripts", "__DWARF"); msect->off = gdbscripto; msect->size = gdbscriptsize; ms->filesize += msect->size; diff --git a/src/cmd/ld/dwarf_defs.h b/src/cmd/ld/dwarf_defs.h index eed143dff..93e99ff74 100644 --- a/src/cmd/ld/dwarf_defs.h +++ b/src/cmd/ld/dwarf_defs.h @@ -93,6 +93,7 @@ enum DW_CLS_FLAG, DW_CLS_PTR, // lineptr, loclistptr, macptr, rangelistptr DW_CLS_REFERENCE, + DW_CLS_ADDRLOC, DW_CLS_STRING }; diff --git a/src/cmd/ld/elf.c b/src/cmd/ld/elf.c index 630906653..daef5793f 100644 --- a/src/cmd/ld/elf.c +++ b/src/cmd/ld/elf.c @@ -605,9 +605,7 @@ elfdynhash(void) if(sy->dynimpvers) need[sy->dynid] = addelflib(&needlib, sy->dynimplib, sy->dynimpvers); - name = sy->dynimpname; - if(name == nil) - name = sy->name; + name = sy->extname; hc = elfhash((uchar*)name); b = hc % nbucket; @@ -760,9 +758,13 @@ elfshbits(Section *sect) sh->flags |= SHF_EXECINSTR; if(sect->rwx & 2) sh->flags |= SHF_WRITE; - if(!isobj) + if(strcmp(sect->name, ".tbss") == 0) { + sh->flags |= SHF_TLS; + sh->type = SHT_NOBITS; + } + if(linkmode != LinkExternal) sh->addr = sect->vaddr; - sh->addralign = PtrSize; + sh->addralign = sect->align; sh->size = sect->len; sh->off = sect->seg->fileoff + sect->vaddr - sect->seg->vaddr; @@ -781,7 +783,7 @@ elfshreloc(Section *sect) // Also nothing to relocate in .shstrtab. if(sect->vaddr >= sect->seg->vaddr + sect->seg->filelen) return nil; - if(strcmp(sect->name, ".shstrtab") == 0) + if(strcmp(sect->name, ".shstrtab") == 0 || strcmp(sect->name, ".tbss") == 0) return nil; if(thechar == '6') { @@ -807,10 +809,9 @@ elfshreloc(Section *sect) void elfrelocsect(Section *sect, Sym *first) { - Sym *sym, *rs; + Sym *sym; int32 eaddr; Reloc *r; - int64 add; // If main section is SHT_NOBITS, nothing to relocate. // Also nothing to relocate in .shstrtab. @@ -836,28 +837,15 @@ elfrelocsect(Section *sect, Sym *first) cursym = sym; for(r = sym->r; r < sym->r+sym->nr; r++) { - // Ignore relocations handled by reloc already. - switch(r->type) { - case D_SIZE: + if(r->done) + continue; + if(r->xsym == nil) { + diag("missing xsym in relocation"); 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) + if(r->xsym->elfsym == 0) + diag("reloc %d to non-elf symbol %s (outer=%s) %d", r->type, r->sym->name, r->xsym->name, r->sym->type); + if(elfreloc1(r, sym->value+r->off - sect->vaddr) < 0) diag("unsupported obj reloc %d/%d to %s", r->type, r->siz, r->sym->name); } } @@ -899,6 +887,8 @@ doelf(void) addstring(shstrtab, ".data"); addstring(shstrtab, ".bss"); addstring(shstrtab, ".noptrbss"); + if(linkmode == LinkExternal && HEADTYPE != Hopenbsd) + addstring(shstrtab, ".tbss"); if(HEADTYPE == Hnetbsd) addstring(shstrtab, ".note.netbsd.ident"); if(HEADTYPE == Hopenbsd) @@ -915,7 +905,7 @@ doelf(void) addstring(shstrtab, ".gosymtab"); addstring(shstrtab, ".gopclntab"); - if(isobj) { + if(linkmode == LinkExternal) { debug['s'] = 0; debug['d'] = 1; @@ -940,6 +930,8 @@ doelf(void) addstring(shstrtab, ".rel.noptrdata"); addstring(shstrtab, ".rel.data"); } + // add a .note.GNU-stack section to mark the stack as non-executable + addstring(shstrtab, ".note.GNU-stack"); } if(!debug['s']) { @@ -1120,7 +1112,7 @@ asmbelfsetup(void) void asmbelf(vlong symo) { - int a, o; + vlong a, o; vlong startva, resoff; ElfEhdr *eh; ElfPhdr *ph, *pph, *pnote; @@ -1147,7 +1139,7 @@ asmbelf(vlong symo) resoff = ELFRESERVE; pph = nil; - if(isobj) { + if(linkmode == LinkExternal) { /* skip program headers */ eh->phoff = 0; eh->phentsize = 0; @@ -1408,13 +1400,18 @@ elfobj: for(sect=segdata.sect; sect!=nil; sect=sect->next) elfshbits(sect); - if(isobj) { + if(linkmode == LinkExternal) { for(sect=segtext.sect; sect!=nil; sect=sect->next) elfshreloc(sect); for(sect=segdata.sect; sect!=nil; sect=sect->next) elfshreloc(sect); + // add a .note.GNU-stack section to mark the stack as non-executable + sh = elfshname(".note.GNU-stack"); + sh->type = SHT_PROGBITS; + sh->addralign = 1; + sh->flags = 0; } - + if(!debug['s']) { sh = elfshname(".symtab"); sh->type = SHT_SYMTAB; @@ -1431,9 +1428,7 @@ elfobj: sh->size = elfstrsize; sh->addralign = 1; - // TODO(rsc): Enable for isobj too, once we know it works. - if(!isobj) - dwarfaddelfheaders(); + dwarfaddelfheaders(); } /* Main header */ @@ -1456,12 +1451,12 @@ elfobj: if(flag_shared) eh->type = ET_DYN; - else if(isobj) + else if(linkmode == LinkExternal) eh->type = ET_REL; else eh->type = ET_EXEC; - if(!isobj) + if(linkmode != LinkExternal) eh->entry = entryvalue(); eh->version = EV_CURRENT; @@ -1478,7 +1473,7 @@ elfobj: a += elfwriteshdrs(); if(!debug['d']) a += elfwriteinterp(); - if(!isobj) { + if(linkmode != LinkExternal) { if(HEADTYPE == Hnetbsd) a += elfwritenetbsdsig(); if(HEADTYPE == Hopenbsd) diff --git a/src/cmd/ld/elf.h b/src/cmd/ld/elf.h index 3e22125b2..24c0ac43e 100644 --- a/src/cmd/ld/elf.h +++ b/src/cmd/ld/elf.h @@ -855,7 +855,8 @@ struct Elf64_Shdr { Elf64_Xword addralign; /* Alignment in bytes. */ Elf64_Xword entsize; /* Size of each entry in section. */ - int shnum; /* section number, not stored on disk */ + int shnum; /* section number, not stored on disk */ + Sym* secsym; /* section symbol, if needed; not on disk */ }; /* @@ -998,6 +999,7 @@ void phsh(ElfPhdr*, ElfShdr*); void doelf(void); void elfsetupplt(void); void dwarfaddshstrings(Sym*); +void dwarfaddelfsectionsyms(void); void dwarfaddelfheaders(void); void asmbelf(vlong symo); void asmbelfsetup(void); @@ -1005,7 +1007,8 @@ extern char linuxdynld[]; extern char freebsddynld[]; extern char netbsddynld[]; extern char openbsddynld[]; -int elfreloc1(Reloc*, vlong off, int32 elfsym, vlong add); +int elfreloc1(Reloc*, vlong sectoff); +void putelfsectionsyms(void); EXTERN int elfstrsize; EXTERN char* elfstrdat; diff --git a/src/cmd/ld/go.c b/src/cmd/ld/go.c index b2527b13e..47fdbe944 100644 --- a/src/cmd/ld/go.c +++ b/src/cmd/ld/go.c @@ -59,7 +59,7 @@ ilookup(char *name) if(x->name[0] == name[0] && strcmp(x->name, name) == 0) return x; x = mal(sizeof *x); - x->name = strdup(name); + x->name = estrdup(name); x->hash = ihash[h]; ihash[h] = x; nimport++; @@ -71,8 +71,6 @@ static void loadcgo(char*, char*, char*, int); static int parsemethod(char**, char*, char**); static int parsepkgdata(char*, char*, char**, char*, char**, char**, char**); -static Sym **dynexp; - void ldpkg(Biobuf *f, char *pkg, int64 len, char *filename, int whence) { @@ -205,14 +203,14 @@ loadpkgdata(char *file, char *pkg, char *data, int len) char *p, *ep, *prefix, *name, *def; Import *x; - file = strdup(file); + file = estrdup(file); p = data; ep = data + len; while(parsepkgdata(file, pkg, &p, ep, &prefix, &name, &def) > 0) { x = ilookup(name); if(x->prefix == nil) { x->prefix = prefix; - x->def = strdup(def); + x->def = estrdup(def); x->file = file; } else if(strcmp(x->prefix, prefix) != 0) { fprint(2, "%s: conflicting definitions for %s\n", argv0, name); @@ -244,7 +242,7 @@ expandpkg(char *t0, char *pkg) n++; if(n == 0) - return strdup(t0); + return estrdup(t0); // use malloc, not mal, so that caller can free w0 = malloc(strlen(t0) + strlen(pkg)*n); @@ -429,7 +427,7 @@ loadcgo(char *file, char *pkg, char *p, int n) *next++ = '\0'; free(p0); - p0 = strdup(p); // save for error message + p0 = estrdup(p); // save for error message nf = tokenize(p, f, nelem(f)); if(strcmp(f[0], "cgo_import_dynamic") == 0) { @@ -465,11 +463,12 @@ loadcgo(char *file, char *pkg, char *p, int n) s = lookup(local, 0); if(local != f[1]) free(local); - if(s->type == 0 || s->type == SXREF) { + if(s->type == 0 || s->type == SXREF || s->type == SHOSTOBJ) { s->dynimplib = lib; - s->dynimpname = remote; + s->extname = remote; s->dynimpvers = q; - s->type = SDYNIMPORT; + if(s->type != SHOSTOBJ) + s->type = SDYNIMPORT; havedynamic = 1; } continue; @@ -478,16 +477,18 @@ loadcgo(char *file, char *pkg, char *p, int n) 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; - } + local = f[1]; + s = lookup(local, 0); + s->type = SHOSTOBJ; + s->size = 0; continue; } - if(strcmp(f[0], "cgo_export") == 0) { + if(strcmp(f[0], "cgo_export_static") == 0 || strcmp(f[0], "cgo_export_dynamic") == 0) { + // TODO: Remove once we know Windows is okay. + if(strcmp(f[0], "cgo_export_static") == 0 && HEADTYPE == Hwindows) + continue; + if(nf < 2 || nf > 3) goto err; local = f[1]; @@ -497,17 +498,30 @@ loadcgo(char *file, char *pkg, char *p, int n) remote = local; local = expandpkg(local, pkg); s = lookup(local, 0); + + // export overrides import, for openbsd/cgo. + // see issue 4878. if(s->dynimplib != nil) { - fprint(2, "%s: symbol is both imported and exported: %s\n", argv0, local); - nerrors++; + s->dynimplib = nil; + s->extname = nil; + s->dynimpvers = nil; + s->type = 0; } - s->dynimpname = remote; - s->dynexport = 1; - - if(ndynexp%32 == 0) - dynexp = realloc(dynexp, (ndynexp+32)*sizeof dynexp[0]); - dynexp[ndynexp++] = s; + if(s->cgoexport == 0) { + s->extname = remote; + if(ndynexp%32 == 0) + dynexp = erealloc(dynexp, (ndynexp+32)*sizeof dynexp[0]); + dynexp[ndynexp++] = s; + } else if(strcmp(s->extname, remote) != 0) { + fprint(2, "%s: conflicting cgo_export directives: %s as %s and %s\n", argv0, s->name, s->extname, remote); + nerrors++; + return; + } + if(strcmp(f[0], "cgo_export_static") == 0) + s->cgoexport |= CgoExportStatic; + else + s->cgoexport |= CgoExportDynamic; if(local != f[1]) free(local); continue; @@ -524,10 +538,19 @@ loadcgo(char *file, char *pkg, char *p, int n) return; } free(interpreter); - interpreter = strdup(f[1]); + interpreter = estrdup(f[1]); } continue; } + + if(strcmp(f[0], "cgo_ldflag") == 0) { + if(nf != 2) + goto err; + if(nldflag%32 == 0) + ldflag = erealloc(ldflag, (nldflag+32)*sizeof ldflag[0]); + ldflag[nldflag++] = estrdup(f[1]); + continue; + } } free(p0); return; @@ -751,6 +774,9 @@ addexport(void) { int i; + if(HEADTYPE == Hdarwin) + return; + for(i=0; i<ndynexp; i++) adddynsym(dynexp[i]); } @@ -829,7 +855,7 @@ getpkg(char *path) if(strcmp(p->path, path) == 0) return p; p = mal(sizeof *p); - p->path = strdup(path); + p->path = estrdup(path); p->next = phash[h]; phash[h] = p; p->all = pkgall; @@ -853,7 +879,7 @@ imported(char *pkg, char *import) i->mimpby *= 2; if(i->mimpby == 0) i->mimpby = 16; - i->impby = realloc(i->impby, i->mimpby*sizeof i->impby[0]); + i->impby = erealloc(i->impby, i->mimpby*sizeof i->impby[0]); } i->impby[i->nimpby++] = p; free(pkg); @@ -899,27 +925,17 @@ importcycles(void) 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) +setlinkmode(char *arg) { - 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 + if(strcmp(arg, "internal") == 0) + linkmode = LinkInternal; + else if(strcmp(arg, "external") == 0) + linkmode = LinkExternal; + else if(strcmp(arg, "auto") == 0) + linkmode = LinkAuto; + else { + fprint(2, "unknown link mode -linkmode %s\n", arg); + errorexit(); } } diff --git a/src/cmd/ld/ldelf.c b/src/cmd/ld/ldelf.c index 2bbf4f83e..27041bc47 100644 --- a/src/cmd/ld/ldelf.c +++ b/src/cmd/ld/ldelf.c @@ -595,10 +595,8 @@ ldelf(Biobuf *f, char *pkg, int64 len, char *pn) s->sub = sect->sym->sub; sect->sym->sub = s; s->type = sect->sym->type | (s->type&~SMASK) | SSUB; - if(!s->dynexport) { + if(!(s->cgoexport & CgoExportDynamic)) s->dynimplib = nil; // satisfy dynimport - s->dynimpname = nil; // satisfy dynimport - } s->value = sym.value; s->size = sym.size; s->outer = sect->sym; diff --git a/src/cmd/ld/ldmacho.c b/src/cmd/ld/ldmacho.c index 41852f17c..d384a5094 100644 --- a/src/cmd/ld/ldmacho.c +++ b/src/cmd/ld/ldmacho.c @@ -639,10 +639,8 @@ ldmacho(Biobuf *f, char *pkg, int64 len, char *pn) s->size = (sym+1)->value - sym->value; else s->size = sect->addr + sect->size - sym->value; - if(!s->dynexport) { + if(!(s->cgoexport & CgoExportDynamic)) s->dynimplib = nil; // satisfy dynimport - s->dynimpname = nil; // satisfy dynimport - } if(outer->type == STEXT) { Prog *p; @@ -806,9 +804,9 @@ ldmacho(Biobuf *f, char *pkg, int64 len, char *pn) // // [For future reference, see Darwin's /usr/include/mach-o/x86_64/reloc.h] secaddr = c->seg.sect[rel->symnum-1].addr; - rp->add = e->e32(s->p+rp->off) + rp->off + 4 - secaddr; + rp->add = (int32)e->e32(s->p+rp->off) + rp->off + 4 - secaddr; } else - rp->add = e->e32(s->p+rp->off); + rp->add = (int32)e->e32(s->p+rp->off); // For i386 Mach-O PC-relative, the addend is written such that // it *is* the PC being subtracted. Use that to make diff --git a/src/cmd/ld/ldpe.c b/src/cmd/ld/ldpe.c index 39c15e6a1..033e522f2 100644 --- a/src/cmd/ld/ldpe.c +++ b/src/cmd/ld/ldpe.c @@ -135,7 +135,8 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn) { char *name; int32 base; - int i, j, l, numaux; + uint32 l; + int i, j, numaux; PeObj *obj; PeSect *sect, *rsect; IMAGE_SECTION_HEADER sh; @@ -170,11 +171,12 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn) // TODO return error if found .cormeta } // load string table - Bseek(f, base+obj->fh.PointerToSymbolTable+18*obj->fh.NumberOfSymbols, 0); - if(Bread(f, &l, sizeof l) != sizeof l) + Bseek(f, base+obj->fh.PointerToSymbolTable+sizeof(symbuf)*obj->fh.NumberOfSymbols, 0); + if(Bread(f, symbuf, 4) != 4) goto bad; + l = le32(symbuf); obj->snames = mal(l); - Bseek(f, base+obj->fh.PointerToSymbolTable+18*obj->fh.NumberOfSymbols, 0); + Bseek(f, base+obj->fh.PointerToSymbolTable+sizeof(symbuf)*obj->fh.NumberOfSymbols, 0); if(Bread(f, obj->snames, l) != l) goto bad; // read symbols @@ -209,6 +211,13 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn) sect = &obj->sect[i]; if(sect->sh.Characteristics&IMAGE_SCN_MEM_DISCARDABLE) continue; + + if((sect->sh.Characteristics&(IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_CNT_UNINITIALIZED_DATA)) == 0) { + // This has been seen for .idata sections, which we + // want to ignore. See issues 5106 and 5273. + continue; + } + if(map(obj, sect) < 0) goto bad; @@ -230,7 +239,7 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn) s->type = STEXT; break; default: - werrstr("unexpected flags for PE section %s", sect->name); + werrstr("unexpected flags %#08ux for PE section %s", sect->sh.Characteristics, sect->name); goto bad; } s->p = sect->base; @@ -248,6 +257,11 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn) continue; if(rsect->sh.Characteristics&IMAGE_SCN_MEM_DISCARDABLE) continue; + if((sect->sh.Characteristics&(IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_CNT_UNINITIALIZED_DATA)) == 0) { + // This has been seen for .idata sections, which we + // want to ignore. See issues 5106 and 5273. + continue; + } r = mal(rsect->sh.NumberOfRelocations*sizeof r[0]); Bseek(f, obj->base+rsect->sh.PointerToRelocations, 0); for(j=0; j<rsect->sh.NumberOfRelocations; j++) { @@ -277,7 +291,7 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn) case IMAGE_REL_AMD64_ADDR32: // R_X86_64_PC32 case IMAGE_REL_AMD64_ADDR32NB: rp->type = D_PCREL; - rp->add = le32(rsect->base+rp->off); + rp->add = (int32)le32(rsect->base+rp->off); break; case IMAGE_REL_I386_DIR32NB: case IMAGE_REL_I386_DIR32: diff --git a/src/cmd/ld/lib.c b/src/cmd/ld/lib.c index 26fa4f2ac..0a6bd3e8f 100644 --- a/src/cmd/ld/lib.c +++ b/src/cmd/ld/lib.c @@ -31,6 +31,7 @@ #include "l.h" #include "lib.h" +#include "../ld/elf.h" #include "../../pkg/runtime/stack.h" #include <ar.h> @@ -44,6 +45,12 @@ int nlibdir = 0; static int maxlibdir = 0; static int cout = -1; +// Set if we see an object compiled by the host compiler that is not +// from a package that is known to support internal linking mode. +static int externalobj = 0; + +static void hostlinksetup(void); + char* goroot; char* goarch; char* goos; @@ -59,11 +66,7 @@ Lflag(char *arg) maxlibdir = 8; else maxlibdir *= 2; - p = realloc(libdir, maxlibdir * sizeof(*p)); - if (p == nil) { - print("too many -L's: %d\n", nlibdir); - usage(); - } + p = erealloc(libdir, maxlibdir * sizeof(*p)); libdir = p; } libdir[nlibdir++] = arg; @@ -95,7 +98,7 @@ libinit(void) #endif cout = create(outfile, 1, 0775); if(cout < 0) { - diag("cannot create %s", outfile); + diag("cannot create %s: %r", outfile); errorexit(); } @@ -242,7 +245,7 @@ addlibpath(char *srcref, char *objref, char *file, char *pkg) if(libraryp == nlibrary){ nlibrary = 50 + 2*libraryp; - library = realloc(library, sizeof library[0] * nlibrary); + library = erealloc(library, sizeof library[0] * nlibrary); } l = &library[libraryp++]; @@ -288,13 +291,27 @@ loadinternal(char *name) void loadlib(void) { - int i; + int i, w, x; + Sym *s; loadinternal("runtime"); if(thechar == '5') loadinternal("math"); if(flag_race) loadinternal("runtime/race"); + if(linkmode == LinkExternal) { + // This indicates a user requested -linkmode=external. + // The startup code uses an import of runtime/cgo to decide + // whether to initialize the TLS. So give it one. This could + // be handled differently but it's an unusual case. + loadinternal("runtime/cgo"); + // Pretend that we really imported the package. + // This will do no harm if we did in fact import it. + s = lookup("go.importpath.runtime/cgo.", 0); + s->type = SDATA; + s->dupok = 1; + s->reachable = 1; + } for(i=0; i<libraryp; i++) { if(debug['v']) @@ -303,6 +320,45 @@ loadlib(void) objfile(library[i].file, library[i].pkg); } + if(linkmode == LinkAuto) { + if(iscgo && externalobj) + linkmode = LinkExternal; + else + linkmode = LinkInternal; + } + + if(linkmode == LinkInternal) { + // Drop all the cgo_import_static declarations. + // Turns out we won't be needing them. + for(s = allsym; s != S; s = s->allsym) + if(s->type == SHOSTOBJ) { + // If a symbol was marked both + // cgo_import_static and cgo_import_dynamic, + // then we want to make it cgo_import_dynamic + // now. + if(s->extname != nil && s->dynimplib != nil && s->cgoexport == 0) { + s->type = SDYNIMPORT; + } else + s->type = 0; + } + } + + // Now that we know the link mode, trim the dynexp list. + x = CgoExportDynamic; + if(linkmode == LinkExternal) + x = CgoExportStatic; + w = 0; + for(i=0; i<ndynexp; i++) + if(dynexp[i]->cgoexport & x) + dynexp[w++] = dynexp[i]; + ndynexp = w; + + // In internal link mode, read the host object files. + if(linkmode == LinkInternal) + hostobjs(); + else + hostlinksetup(); + // We've loaded all the code now. // If there are no dynamic libraries needed, gcc disables dynamic linking. // Because of this, glibc's dynamic ELF loader occasionally (like in version 2.13) @@ -316,7 +372,6 @@ loadlib(void) debug['d'] = 1; importcycles(); - sortdynexp(); } /* @@ -375,7 +430,7 @@ objfile(char *file, char *pkg) /* load it as a regular file */ l = Bseek(f, 0L, 2); Bseek(f, 0L, 0); - ldobj(f, pkg, l, file, FileObj); + ldobj(f, pkg, l, file, file, FileObj); Bterm(f); free(pkg); return; @@ -434,7 +489,7 @@ objfile(char *file, char *pkg) l--; snprint(pname, sizeof pname, "%s(%.*s)", file, utfnlen(arhdr.name, l), arhdr.name); l = atolwhex(arhdr.size); - ldobj(f, pkg, l, pname, ArchiveObj); + ldobj(f, pkg, l, pname, file, ArchiveObj); } out: @@ -442,8 +497,257 @@ out: free(pkg); } +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; + } +} + +typedef struct Hostobj Hostobj; + +struct Hostobj +{ + void (*ld)(Biobuf*, char*, int64, char*); + char *pkg; + char *pn; + char *file; + int64 off; + int64 len; +}; + +Hostobj *hostobj; +int nhostobj; +int mhostobj; + +// These packages can use internal linking mode. +// Others trigger external mode. +const char *internalpkg[] = { + "crypto/x509", + "net", + "os/user", + "runtime/cgo", + "runtime/race" +}; + void -ldobj(Biobuf *f, char *pkg, int64 len, char *pn, int whence) +ldhostobj(void (*ld)(Biobuf*, char*, int64, char*), Biobuf *f, char *pkg, int64 len, char *pn, char *file) +{ + int i, isinternal; + Hostobj *h; + + isinternal = 0; + for(i=0; i<nelem(internalpkg); i++) { + if(strcmp(pkg, internalpkg[i]) == 0) { + isinternal = 1; + break; + } + } + + if(!isinternal) + externalobj = 1; + + if(nhostobj >= mhostobj) { + if(mhostobj == 0) + mhostobj = 16; + else + mhostobj *= 2; + hostobj = erealloc(hostobj, mhostobj*sizeof hostobj[0]); + } + h = &hostobj[nhostobj++]; + h->ld = ld; + h->pkg = estrdup(pkg); + h->pn = estrdup(pn); + h->file = estrdup(file); + h->off = Boffset(f); + h->len = len; +} + +void +hostobjs(void) +{ + int i; + Biobuf *f; + Hostobj *h; + + for(i=0; i<nhostobj; i++) { + h = &hostobj[i]; + f = Bopen(h->file, OREAD); + if(f == nil) { + cursym = S; + diag("cannot reopen %s: %r", h->pn); + errorexit(); + } + Bseek(f, h->off, 0); + h->ld(f, h->pkg, h->len, h->pn); + Bterm(f); + } +} + +// provided by lib9 +int runcmd(char**); +char* mktempdir(void); +void removeall(char*); + +static void +rmtemp(void) +{ + removeall(tmpdir); +} + +static void +hostlinksetup(void) +{ + char *p; + + if(linkmode != LinkExternal) + return; + + // create temporary directory and arrange cleanup + if(tmpdir == nil) { + tmpdir = mktempdir(); + atexit(rmtemp); + } + + // change our output to temporary object file + close(cout); + p = smprint("%s/go.o", tmpdir); + cout = create(p, 1, 0775); + if(cout < 0) { + diag("cannot create %s: %r", p); + errorexit(); + } + free(p); +} + +void +hostlink(void) +{ + char *p, **argv; + int c, i, w, n, argc, len; + Hostobj *h; + Biobuf *f; + static char buf[64<<10]; + + if(linkmode != LinkExternal || nerrors > 0) + return; + + c = 0; + p = extldflags; + while(p != nil) { + while(*p == ' ') + p++; + if(*p == '\0') + break; + c++; + p = strchr(p + 1, ' '); + } + + argv = malloc((10+nhostobj+nldflag+c)*sizeof argv[0]); + argc = 0; + if(extld == nil) + extld = "gcc"; + argv[argc++] = extld; + switch(thechar){ + case '8': + argv[argc++] = "-m32"; + break; + case '6': + argv[argc++] = "-m64"; + break; + } + if(!debug['s']) + argv[argc++] = "-gdwarf-2"; + if(HEADTYPE == Hdarwin) + argv[argc++] = "-Wl,-no_pie,-pagezero_size,4000000"; + argv[argc++] = "-o"; + argv[argc++] = outfile; + + if(rpath) + argv[argc++] = smprint("-Wl,-rpath,%s", rpath); + + // Force global symbols to be exported for dlopen, etc. + if(iself) + argv[argc++] = "-rdynamic"; + + // already wrote main object file + // copy host objects to temporary directory + for(i=0; i<nhostobj; i++) { + h = &hostobj[i]; + f = Bopen(h->file, OREAD); + if(f == nil) { + cursym = S; + diag("cannot reopen %s: %r", h->pn); + errorexit(); + } + Bseek(f, h->off, 0); + p = smprint("%s/%06d.o", tmpdir, i); + argv[argc++] = p; + w = create(p, 1, 0775); + if(w < 0) { + cursym = S; + diag("cannot create %s: %r", p); + errorexit(); + } + len = h->len; + while(len > 0 && (n = Bread(f, buf, sizeof buf)) > 0){ + if(n > len) + n = len; + dowrite(w, buf, n); + len -= n; + } + if(close(w) < 0) { + cursym = S; + diag("cannot write %s: %r", p); + errorexit(); + } + Bterm(f); + } + + argv[argc++] = smprint("%s/go.o", tmpdir); + for(i=0; i<nldflag; i++) + argv[argc++] = ldflag[i]; + + p = extldflags; + while(p != nil) { + while(*p == ' ') + *p++ = '\0'; + if(*p == '\0') + break; + argv[argc++] = p; + p = strchr(p + 1, ' '); + } + + argv[argc] = nil; + + quotefmtinstall(); + if(debug['v']) { + Bprint(&bso, "host link:"); + for(i=0; i<argc; i++) + Bprint(&bso, " %q", argv[i]); + Bprint(&bso, "\n"); + Bflush(&bso); + } + + if(runcmd(argv) < 0) { + cursym = S; + diag("%s: running %s failed: %r", argv0, argv[0]); + errorexit(); + } +} + +void +ldobj(Biobuf *f, char *pkg, int64 len, char *pn, char *file, int whence) { char *line; int n, c1, c2, c3, c4; @@ -453,7 +757,7 @@ ldobj(Biobuf *f, char *pkg, int64 len, char *pn, int whence) eof = Boffset(f) + len; - pn = strdup(pn); + pn = estrdup(pn); c1 = Bgetc(f); c2 = Bgetc(f); @@ -466,18 +770,15 @@ ldobj(Biobuf *f, char *pkg, int64 len, char *pn, int whence) magic = c1<<24 | c2<<16 | c3<<8 | c4; if(magic == 0x7f454c46) { // \x7F E L F - ldelf(f, pkg, len, pn); - free(pn); + ldhostobj(ldelf, f, pkg, len, pn, file); return; } if((magic&~1) == 0xfeedface || (magic&~0x01000000) == 0xcefaedfe) { - ldmacho(f, pkg, len, pn); - free(pn); + ldhostobj(ldmacho, f, pkg, len, pn, file); return; } if(c1 == 0x4c && c2 == 0x01 || c1 == 0x64 && c2 == 0x86) { - ldpe(f, pkg, len, pn); - free(pn); + ldhostobj(ldpe, f, pkg, len, pn, file); return; } @@ -524,7 +825,7 @@ ldobj(Biobuf *f, char *pkg, int64 len, char *pn, int whence) line[n] = '\0'; if(n-10 > strlen(t)) { if(theline == nil) - theline = strdup(line+10); + theline = estrdup(line+10); else if(strcmp(theline, line+10) != 0) { line[n] = '\0'; diag("%s: object is [%s] expected [%s]", pn, line+10, theline); @@ -599,22 +900,22 @@ _lookup(char *symb, int v, int creat) Sym *s; char *p; int32 h; - int l, c; + int c; h = v; for(p=symb; c = *p; p++) h = h+h+h + c; - l = (p - symb) + 1; // not if(h < 0) h = ~h, because gcc 4.3 -O2 miscompiles it. h &= 0xffffff; h %= NHASH; for(s = hash[h]; s != S; s = s->hash) - if(memcmp(s->name, symb, l) == 0) + if(strcmp(s->name, symb) == 0) return s; if(!creat) return nil; s = newsym(symb, v); + s->extname = s->name; s->hash = hash[h]; hash[h] = s; @@ -1027,6 +1328,7 @@ addsection(Segment *seg, char *name, int rwx) sect->rwx = rwx; sect->name = name; sect->seg = seg; + sect->align = PtrSize; // everything is at least pointer-aligned *l = sect; return sect; } @@ -1460,23 +1762,6 @@ 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) { @@ -1576,7 +1861,7 @@ genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)) put(s, s->name, 'T', s->value, s->size, s->version, 0); for(s=allsym; s!=S; s=s->allsym) { - if(s->hide) + if(s->hide || (s->name[0] == '.' && s->version == 0 && strcmp(s->name, ".rathole") != 0)) continue; switch(s->type&SMASK) { case SCONST: @@ -1591,8 +1876,6 @@ genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)) 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); @@ -1630,7 +1913,10 @@ genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)) /* 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); + if(s->text->textflag & NOSPLIT) + put(nil, ".args", 'm', ArgsSizeUnknown, 0, 0, 0); + else + 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. @@ -1664,3 +1950,27 @@ genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)) Bprint(&bso, "symsize = %ud\n", symsize); Bflush(&bso); } + +char* +estrdup(char *p) +{ + p = strdup(p); + if(p == nil) { + cursym = S; + diag("out of memory"); + errorexit(); + } + return p; +} + +void* +erealloc(void *p, long n) +{ + p = realloc(p, n); + if(p == nil) { + cursym = S; + diag("out of memory"); + errorexit(); + } + return p; +} diff --git a/src/cmd/ld/lib.h b/src/cmd/ld/lib.h index 94ad76ecc..e552deb02 100644 --- a/src/cmd/ld/lib.h +++ b/src/cmd/ld/lib.h @@ -34,30 +34,29 @@ enum /* order here is order in output file */ STEXT, - SMACHOPLT, STYPE, SSTRING, SGOSTRING, SRODATA, STYPELINK, - SGCDATA, - SGCBSS, SSYMTAB, SPCLNTAB, SELFROSECT, + SMACHOPLT, SELFSECT, + SMACHO, /* Mach-O __nl_symbol_ptr */ + SMACHOGOT, SNOPTRDATA, SDATARELRO, SDATA, - SMACHO, /* Mach-O __nl_symbol_ptr */ - SMACHOGOT, SWINDOWS, SBSS, SNOPTRBSS, + STLSBSS, SXREF, - SMACHODYNSTR, - SMACHODYNSYM, + SMACHOSYMSTR, + SMACHOSYMTAB, SMACHOINDIRECTPLT, SMACHOINDIRECTGOT, SFILE, @@ -72,6 +71,13 @@ enum NHASH = 100003, }; +enum +{ + // This value is known to the garbage collector and should be kept in + // sync with runtime/pkg/runtime.h + ArgsSizeUnknown = 0x80000000 +}; + typedef struct Library Library; struct Library { @@ -104,6 +110,8 @@ struct Segment struct Section { uchar rwx; + int16 extnum; + int32 align; char *name; uvlong vaddr; uvlong len; @@ -138,20 +146,40 @@ EXTERN char* outfile; EXTERN int32 nsymbol; EXTERN char* thestring; EXTERN int ndynexp; +EXTERN Sym** dynexp; +EXTERN int nldflag; +EXTERN char** ldflag; 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 char* tmpdir; +EXTERN char* extld; +EXTERN char* extldflags; + +enum +{ + LinkAuto = 0, + LinkInternal, + LinkExternal, +}; +EXTERN int linkmode; + +// for dynexport field of Sym +enum +{ + CgoExportDynamic = 1<<0, + CgoExportStatic = 1<<1, +}; EXTERN Segment segtext; EXTERN Segment segdata; -EXTERN Segment segsym; -EXTERN Segment segdwarf; +EXTERN Segment segdwarf; +void setlinkmode(char*); void addlib(char *src, char *obj); void addlibpath(char *srcref, char *objref, char *file, char *pkg); Section* addsection(Segment*, char*, int); @@ -185,7 +213,7 @@ 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 ldobj(Biobuf*, char*, int64, char*, char*, int); void ldelf(Biobuf*, char*, int64, char*); void ldmacho(Biobuf*, char*, int64, char*); void ldpe(Biobuf*, char*, int64, char*); @@ -207,11 +235,12 @@ vlong adduint8(Sym*, uint8); vlong adduint16(Sym*, uint16); vlong adduint32(Sym*, uint32); vlong adduint64(Sym*, uint64); +vlong adduintxx(Sym*, uint64, int); vlong addaddr(Sym*, Sym*); -vlong addaddrplus(Sym*, Sym*, int32); -vlong addpcrelplus(Sym*, Sym*, int32); +vlong addaddrplus(Sym*, Sym*, vlong); +vlong addpcrelplus(Sym*, Sym*, vlong); vlong addsize(Sym*, Sym*); -vlong setaddrplus(Sym*, vlong, Sym*, int32); +vlong setaddrplus(Sym*, vlong, Sym*, vlong); vlong setaddr(Sym*, vlong, Sym*); void setuint8(Sym*, vlong, uint8); void setuint16(Sym*, vlong, uint16); @@ -220,6 +249,8 @@ void setuint64(Sym*, vlong, uint64); void asmsym(void); void asmelfsym(void); void asmplan9sym(void); +void putelfsectionsym(Sym*, int); +void putelfsymshndx(vlong, int); void strnput(char*, int); void dodata(void); void dosymtype(void); @@ -240,6 +271,10 @@ void usage(void); void setinterp(char*); Sym* listsort(Sym*, int(*cmp)(Sym*, Sym*), int); int valuecmp(Sym*, Sym*); +void hostobjs(void); +void hostlink(void); +char* estrdup(char*); +void* erealloc(void*, long); int pathchar(void); void* mal(uint32); @@ -359,5 +394,3 @@ 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.c b/src/cmd/ld/macho.c index 6781c25a4..d135a92da 100644 --- a/src/cmd/ld/macho.c +++ b/src/cmd/ld/macho.c @@ -14,9 +14,20 @@ static int macho64; static MachoHdr hdr; static MachoLoad *load; static MachoSeg seg[16]; -static MachoDebug xdebug[16]; static int nload, mload, nseg, ndebug, nsect; +enum +{ + SymKindLocal = 0, + SymKindExtdef, + SymKindUndef, + NumSymKind +}; + +static int nkind[NumSymKind]; +static Sym** sortsym; +static int nsortsym; + // Amount of space left for adding load commands // that refer to dynamic libraries. Because these have // to go in the Mach-O header, we can't just pick a @@ -25,6 +36,8 @@ static int nload, mload, nseg, ndebug, nsect; // up about 1300 bytes; we overestimate that as 2k. static int load_budget = INITIAL_MACHO_HEADR - 2*1024; +static void machodysymtab(void); + void machoinit(void) { @@ -56,11 +69,7 @@ newMachoLoad(uint32 type, uint32 ndata) mload = 1; else mload *= 2; - load = realloc(load, mload*sizeof load[0]); - if(load == nil) { - diag("out of memory"); - errorexit(); - } + load = erealloc(load, mload*sizeof load[0]); } if(macho64 && (ndata & 1)) @@ -90,7 +99,7 @@ newMachoSeg(char *name, int msect) } MachoSect* -newMachoSect(MachoSeg *seg, char *name) +newMachoSect(MachoSeg *seg, char *name, char *segname) { MachoSect *s; @@ -100,21 +109,11 @@ newMachoSect(MachoSeg *seg, char *name) } s = &seg->sect[seg->nsect++]; s->name = name; + s->segname = segname; nsect++; return s; } -MachoDebug* -newMachoDebug(void) -{ - if(ndebug >= nelem(xdebug)) { - diag("too many debugs"); - errorexit(); - } - return &xdebug[ndebug++]; -} - - // Generic linking code. static char **dylib; @@ -130,7 +129,6 @@ machowrite(void) int i, j; MachoSeg *s; MachoSect *t; - MachoDebug *d; MachoLoad *l; o1 = cpos(); @@ -152,7 +150,10 @@ machowrite(void) LPUT(0xfeedface); LPUT(hdr.cpu); LPUT(hdr.subcpu); - LPUT(2); /* file type - mach executable */ + if(linkmode == LinkExternal) + LPUT(1); /* file type - mach object */ + else + LPUT(2); /* file type - mach executable */ LPUT(nload+nseg+ndebug); LPUT(loadsize); LPUT(1); /* flags - no undefines */ @@ -190,7 +191,7 @@ machowrite(void) t = &s->sect[j]; if(macho64) { strnput(t->name, 16); - strnput(s->name, 16); + strnput(t->segname, 16); VPUT(t->addr); VPUT(t->size); LPUT(t->off); @@ -203,7 +204,7 @@ machowrite(void) LPUT(0); /* reserved */ } else { strnput(t->name, 16); - strnput(s->name, 16); + strnput(t->segname, 16); LPUT(t->addr); LPUT(t->size); LPUT(t->off); @@ -225,14 +226,6 @@ machowrite(void) LPUT(l->data[j]); } - for(i=0; i<ndebug; i++) { - d = &xdebug[i]; - LPUT(3); /* obsolete gdb debug info */ - LPUT(16); /* size of symseg command */ - LPUT(d->fileoffset); - LPUT(d->filesize); - } - return cpos() - o1; } @@ -245,31 +238,34 @@ domacho(void) return; // empirically, string table must begin with " \x00". - s = lookup(".dynstr", 0); - s->type = SMACHODYNSTR; + s = lookup(".machosymstr", 0); + s->type = SMACHOSYMSTR; s->reachable = 1; adduint8(s, ' '); adduint8(s, '\0'); - s = lookup(".dynsym", 0); - s->type = SMACHODYNSYM; + s = lookup(".machosymtab", 0); + s->type = SMACHOSYMTAB; s->reachable = 1; - s = lookup(".plt", 0); // will be __symbol_stub - s->type = SMACHOPLT; - s->reachable = 1; + if(linkmode != LinkExternal) { + s = lookup(".plt", 0); // will be __symbol_stub + s->type = SMACHOPLT; + s->reachable = 1; - s = lookup(".got", 0); // will be __nl_symbol_ptr - s->type = SMACHOGOT; - s->reachable = 1; + s = lookup(".got", 0); // will be __nl_symbol_ptr + s->type = SMACHOGOT; + s->reachable = 1; + s->align = 4; - s = lookup(".linkedit.plt", 0); // indirect table for .plt - s->type = SMACHOINDIRECTPLT; - s->reachable = 1; + s = lookup(".linkedit.plt", 0); // indirect table for .plt + s->type = SMACHOINDIRECTPLT; + s->reachable = 1; - s = lookup(".linkedit.got", 0); // indirect table for .got - s->type = SMACHOINDIRECTGOT; - s->reachable = 1; + s = lookup(".linkedit.got", 0); // indirect table for .got + s->type = SMACHOINDIRECTGOT; + s->reachable = 1; + } } void @@ -286,16 +282,62 @@ machoadddynlib(char *lib) load_budget += 4096; } - if(ndylib%32 == 0) { - dylib = realloc(dylib, (ndylib+32)*sizeof dylib[0]); - if(dylib == nil) { - diag("out of memory"); - errorexit(); - } - } + if(ndylib%32 == 0) + dylib = erealloc(dylib, (ndylib+32)*sizeof dylib[0]); dylib[ndylib++] = lib; } +static void +machoshbits(MachoSeg *mseg, Section *sect, char *segname) +{ + MachoSect *msect; + char buf[40]; + char *p; + + snprint(buf, sizeof buf, "__%s", sect->name+1); + for(p=buf; *p; p++) + if(*p == '.') + *p = '_'; + + msect = newMachoSect(mseg, estrdup(buf), segname); + if(sect->rellen > 0) { + msect->reloc = sect->reloff; + msect->nreloc = sect->rellen / 8; + } + + while(1<<msect->align < sect->align) + msect->align++; + msect->addr = sect->vaddr; + msect->size = sect->len; + + if(sect->vaddr < sect->seg->vaddr + sect->seg->filelen) { + // data in file + if(sect->len > sect->seg->vaddr + sect->seg->filelen - sect->vaddr) + diag("macho cannot represent section %s crossing data and bss", sect->name); + msect->off = sect->seg->fileoff + sect->vaddr - sect->seg->vaddr; + } else { + // zero fill + msect->off = 0; + msect->flag |= 1; + } + + if(sect->rwx & 1) + msect->flag |= 0x400; /* has instructions */ + + if(strcmp(sect->name, ".plt") == 0) { + msect->name = "__symbol_stub1"; + msect->flag = 0x80000408; /* only instructions, code, symbol stubs */ + msect->res1 = 0;//nkind[SymKindLocal]; + msect->res2 = 6; + } + + if(strcmp(sect->name, ".got") == 0) { + msect->name = "__nl_symbol_ptr"; + msect->flag = 6; /* section with nonlazy symbol pointers */ + msect->res1 = lookup(".linkedit.plt", 0)->size / 4; /* offset into indirect symbol table */ + } +} + void asmbmacho(void) { @@ -303,11 +345,9 @@ asmbmacho(void) vlong va; int a, i; MachoHdr *mh; - MachoSect *msect; MachoSeg *ms; - MachoDebug *md; MachoLoad *ml; - Sym *s; + Section *sect; /* apple MACH */ va = INITTEXT - HEADR; @@ -325,179 +365,305 @@ asmbmacho(void) mh->subcpu = MACHO_SUBCPU_X86; break; } + + ms = nil; + if(linkmode == LinkExternal) { + /* segment for entire file */ + ms = newMachoSeg("", 40); + ms->fileoffset = segtext.fileoff; + ms->filesize = segdata.fileoff + segdata.filelen - segtext.fileoff; + } /* segment for zero page */ - ms = newMachoSeg("__PAGEZERO", 0); - ms->vsize = va; + if(linkmode != LinkExternal) { + ms = newMachoSeg("__PAGEZERO", 0); + ms->vsize = va; + } /* text */ v = rnd(HEADR+segtext.len, INITRND); - ms = newMachoSeg("__TEXT", 2); - ms->vaddr = va; - ms->vsize = v; - ms->filesize = v; - ms->prot1 = 7; - ms->prot2 = 5; - - msect = newMachoSect(ms, "__text"); - msect->addr = INITTEXT; - msect->size = segtext.sect->len; - msect->off = INITTEXT - va; - msect->flag = 0x400; /* flag - some instructions */ - - s = lookup(".plt", 0); - if(s->size > 0) { - msect = newMachoSect(ms, "__symbol_stub1"); - msect->addr = symaddr(s); - msect->size = s->size; - msect->off = ms->fileoffset + msect->addr - ms->vaddr; - msect->flag = 0x80000408; /* flag */ - msect->res1 = 0; /* index into indirect symbol table */ - msect->res2 = 6; /* size of stubs */ + if(linkmode != LinkExternal) { + ms = newMachoSeg("__TEXT", 20); + ms->vaddr = va; + ms->vsize = v; + ms->fileoffset = 0; + ms->filesize = v; + ms->prot1 = 7; + ms->prot2 = 5; } + for(sect=segtext.sect; sect!=nil; sect=sect->next) + machoshbits(ms, sect, "__TEXT"); + /* data */ - w = segdata.len; - ms = newMachoSeg("__DATA", 3); - ms->vaddr = va+v; - ms->vsize = w; - ms->fileoffset = v; - ms->filesize = segdata.filelen; - ms->prot1 = 7; - ms->prot2 = 3; - - msect = newMachoSect(ms, "__data"); - msect->addr = va+v; - msect->off = v; - msect->size = segdata.filelen; - - s = lookup(".got", 0); - if(s->size > 0) { - msect->size = symaddr(s) - msect->addr; - - msect = newMachoSect(ms, "__nl_symbol_ptr"); - msect->addr = symaddr(s); - msect->size = s->size; - msect->off = datoff(msect->addr); - msect->align = 2; - msect->flag = 6; /* section with nonlazy symbol pointers */ - msect->res1 = lookup(".linkedit.plt", 0)->size / 4; /* offset into indirect symbol table */ + if(linkmode != LinkExternal) { + w = segdata.len; + ms = newMachoSeg("__DATA", 20); + ms->vaddr = va+v; + ms->vsize = w; + ms->fileoffset = v; + ms->filesize = segdata.filelen; + ms->prot1 = 3; + ms->prot2 = 3; } - msect = newMachoSect(ms, "__bss"); - msect->addr = va+v+segdata.filelen; - msect->size = segdata.len - segdata.filelen; - msect->flag = 1; /* flag - zero fill */ + for(sect=segdata.sect; sect!=nil; sect=sect->next) + machoshbits(ms, sect, "__DATA"); - switch(thechar) { - default: - diag("unknown macho architecture"); - errorexit(); - case '6': - ml = newMachoLoad(5, 42+2); /* unix thread */ - ml->data[0] = 4; /* thread type */ - ml->data[1] = 42; /* word count */ - ml->data[2+32] = entryvalue(); /* start pc */ - ml->data[2+32+1] = entryvalue()>>16>>16; // hide >>32 for 8l - break; - case '8': - ml = newMachoLoad(5, 16+2); /* unix thread */ - ml->data[0] = 1; /* thread type */ - ml->data[1] = 16; /* word count */ - ml->data[2+10] = entryvalue(); /* start pc */ - break; + if(linkmode != LinkExternal) { + switch(thechar) { + default: + diag("unknown macho architecture"); + errorexit(); + case '6': + ml = newMachoLoad(5, 42+2); /* unix thread */ + ml->data[0] = 4; /* thread type */ + ml->data[1] = 42; /* word count */ + ml->data[2+32] = entryvalue(); /* start pc */ + ml->data[2+32+1] = entryvalue()>>16>>16; // hide >>32 for 8l + break; + case '8': + ml = newMachoLoad(5, 16+2); /* unix thread */ + ml->data[0] = 1; /* thread type */ + ml->data[1] = 16; /* word count */ + ml->data[2+10] = entryvalue(); /* start pc */ + break; + } } - + if(!debug['d']) { Sym *s1, *s2, *s3, *s4; // must match domacholink below - s1 = lookup(".dynsym", 0); + s1 = lookup(".machosymtab", 0); s2 = lookup(".linkedit.plt", 0); s3 = lookup(".linkedit.got", 0); - s4 = lookup(".dynstr", 0); - - ms = newMachoSeg("__LINKEDIT", 0); - ms->vaddr = va+v+rnd(segdata.len, INITRND); - ms->vsize = s1->size + s2->size + s3->size + s4->size; - ms->fileoffset = linkoff; - ms->filesize = ms->vsize; - ms->prot1 = 7; - ms->prot2 = 3; + s4 = lookup(".machosymstr", 0); + + if(linkmode != LinkExternal) { + ms = newMachoSeg("__LINKEDIT", 0); + ms->vaddr = va+v+rnd(segdata.len, INITRND); + ms->vsize = s1->size + s2->size + s3->size + s4->size; + ms->fileoffset = linkoff; + ms->filesize = ms->vsize; + ms->prot1 = 7; + ms->prot2 = 3; + } ml = newMachoLoad(2, 4); /* LC_SYMTAB */ ml->data[0] = linkoff; /* symoff */ - ml->data[1] = s1->size / (macho64 ? 16 : 12); /* nsyms */ + ml->data[1] = nsortsym; /* nsyms */ ml->data[2] = linkoff + s1->size + s2->size + s3->size; /* stroff */ ml->data[3] = s4->size; /* strsize */ - ml = newMachoLoad(11, 18); /* LC_DYSYMTAB */ - ml->data[0] = 0; /* ilocalsym */ - ml->data[1] = 0; /* nlocalsym */ - ml->data[2] = 0; /* iextdefsym */ - ml->data[3] = ndynexp; /* nextdefsym */ - ml->data[4] = ndynexp; /* iundefsym */ - ml->data[5] = (s1->size / (macho64 ? 16 : 12)) - ndynexp; /* nundefsym */ - ml->data[6] = 0; /* tocoffset */ - ml->data[7] = 0; /* ntoc */ - ml->data[8] = 0; /* modtaboff */ - ml->data[9] = 0; /* nmodtab */ - ml->data[10] = 0; /* extrefsymoff */ - ml->data[11] = 0; /* nextrefsyms */ - ml->data[12] = linkoff + s1->size; /* indirectsymoff */ - ml->data[13] = (s2->size + s3->size) / 4; /* nindirectsyms */ - ml->data[14] = 0; /* extreloff */ - ml->data[15] = 0; /* nextrel */ - ml->data[16] = 0; /* locreloff */ - ml->data[17] = 0; /* nlocrel */ - - ml = newMachoLoad(14, 6); /* LC_LOAD_DYLINKER */ - ml->data[0] = 12; /* offset to string */ - strcpy((char*)&ml->data[1], "/usr/lib/dyld"); - - for(i=0; i<ndylib; i++) { - ml = newMachoLoad(12, 4+(strlen(dylib[i])+1+7)/8*2); /* LC_LOAD_DYLIB */ - ml->data[0] = 24; /* offset of string from beginning of load */ - ml->data[1] = 0; /* time stamp */ - ml->data[2] = 0; /* version */ - ml->data[3] = 0; /* compatibility version */ - strcpy((char*)&ml->data[4], dylib[i]); + machodysymtab(); + + if(linkmode != LinkExternal) { + ml = newMachoLoad(14, 6); /* LC_LOAD_DYLINKER */ + ml->data[0] = 12; /* offset to string */ + strcpy((char*)&ml->data[1], "/usr/lib/dyld"); + + for(i=0; i<ndylib; i++) { + ml = newMachoLoad(12, 4+(strlen(dylib[i])+1+7)/8*2); /* LC_LOAD_DYLIB */ + ml->data[0] = 24; /* offset of string from beginning of load */ + ml->data[1] = 0; /* time stamp */ + ml->data[2] = 0; /* version */ + ml->data[3] = 0; /* compatibility version */ + strcpy((char*)&ml->data[4], dylib[i]); + } } } - if(!debug['s']) { - Sym *s; - - md = newMachoDebug(); - s = lookup("symtab", 0); - md->fileoffset = datoff(s->value); - md->filesize = s->size; - - md = newMachoDebug(); - s = lookup("pclntab", 0); - md->fileoffset = datoff(s->value); - md->filesize = s->size; - + // TODO: dwarf headers go in ms too + if(!debug['s'] && linkmode != LinkExternal) dwarfaddmachoheaders(); - } a = machowrite(); if(a > HEADR) diag("HEADR too small: %d > %d", a, HEADR); } +static int +symkind(Sym *s) +{ + if(s->type == SDYNIMPORT) + return SymKindUndef; + if(s->cgoexport) + return SymKindExtdef; + return SymKindLocal; +} + +static void +addsym(Sym *s, char *name, int type, vlong addr, vlong size, int ver, Sym *gotype) +{ + USED(name); + USED(addr); + USED(size); + USED(ver); + USED(gotype); + + if(s == nil) + return; + + switch(type) { + default: + return; + case 'D': + case 'B': + case 'T': + break; + } + + if(sortsym) { + sortsym[nsortsym] = s; + nkind[symkind(s)]++; + } + nsortsym++; +} + +static int +scmp(const void *p1, const void *p2) +{ + Sym *s1, *s2; + int k1, k2; + + s1 = *(Sym**)p1; + s2 = *(Sym**)p2; + + k1 = symkind(s1); + k2 = symkind(s2); + if(k1 != k2) + return k1 - k2; + + return strcmp(s1->extname, s2->extname); +} + +static void +machogenasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)) +{ + Sym *s; + + genasmsym(put); + for(s=allsym; s; s=s->allsym) + if(s->type == SDYNIMPORT || s->type == SHOSTOBJ) + if(s->reachable) + put(s, nil, 'D', 0, 0, 0, nil); +} + +void +machosymorder(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 + for(i=0; i<ndynexp; i++) + dynexp[i]->reachable = 1; + machogenasmsym(addsym); + sortsym = mal(nsortsym * sizeof sortsym[0]); + nsortsym = 0; + machogenasmsym(addsym); + qsort(sortsym, nsortsym, sizeof sortsym[0], scmp); + for(i=0; i<nsortsym; i++) + sortsym[i]->dynid = i; +} + +static void +machosymtab(void) +{ + int i; + Sym *symtab, *symstr, *s, *o; + + symtab = lookup(".machosymtab", 0); + symstr = lookup(".machosymstr", 0); + + for(i=0; i<nsortsym; i++) { + s = sortsym[i]; + adduint32(symtab, symstr->size); + + // Only add _ to C symbols. Go symbols have dot in the name. + if(strstr(s->extname, ".") == nil) + adduint8(symstr, '_'); + addstring(symstr, s->extname); + if(s->type == SDYNIMPORT || s->type == SHOSTOBJ) { + adduint8(symtab, 0x01); // type N_EXT, external symbol + adduint8(symtab, 0); // no section + adduint16(symtab, 0); // desc + adduintxx(symtab, 0, PtrSize); // no value + } else { + if(s->cgoexport) + adduint8(symtab, 0x0f); + else + adduint8(symtab, 0x0e); + o = s; + while(o->outer != nil) + o = o->outer; + if(o->sect == nil) { + diag("missing section for %s", s->name); + adduint8(symtab, 0); + } else + adduint8(symtab, o->sect->extnum); + adduint16(symtab, 0); // desc + adduintxx(symtab, symaddr(s), PtrSize); + } + } +} + +static void +machodysymtab(void) +{ + int n; + MachoLoad *ml; + Sym *s1, *s2, *s3; + + ml = newMachoLoad(11, 18); /* LC_DYSYMTAB */ + + n = 0; + ml->data[0] = n; /* ilocalsym */ + ml->data[1] = nkind[SymKindLocal]; /* nlocalsym */ + n += nkind[SymKindLocal]; + + ml->data[2] = n; /* iextdefsym */ + ml->data[3] = nkind[SymKindExtdef]; /* nextdefsym */ + n += nkind[SymKindExtdef]; + + ml->data[4] = n; /* iundefsym */ + ml->data[5] = nkind[SymKindUndef]; /* nundefsym */ + + ml->data[6] = 0; /* tocoffset */ + ml->data[7] = 0; /* ntoc */ + ml->data[8] = 0; /* modtaboff */ + ml->data[9] = 0; /* nmodtab */ + ml->data[10] = 0; /* extrefsymoff */ + ml->data[11] = 0; /* nextrefsyms */ + + // must match domacholink below + s1 = lookup(".machosymtab", 0); + s2 = lookup(".linkedit.plt", 0); + s3 = lookup(".linkedit.got", 0); + ml->data[12] = linkoff + s1->size; /* indirectsymoff */ + ml->data[13] = (s2->size + s3->size) / 4; /* nindirectsyms */ + + ml->data[14] = 0; /* extreloff */ + ml->data[15] = 0; /* nextrel */ + ml->data[16] = 0; /* locreloff */ + ml->data[17] = 0; /* nlocrel */ +} + vlong domacholink(void) { int size; Sym *s1, *s2, *s3, *s4; + machosymtab(); + // write data that will be linkedit section - s1 = lookup(".dynsym", 0); - relocsym(s1); + s1 = lookup(".machosymtab", 0); s2 = lookup(".linkedit.plt", 0); s3 = lookup(".linkedit.got", 0); - s4 = lookup(".dynstr", 0); + s4 = lookup(".machosymstr", 0); // Force the linkedit section to end on a 16-byte // boundary. This allows pure (non-cgo) Go binaries @@ -533,3 +699,57 @@ domacholink(void) return rnd(size, INITRND); } + + +void +machorelocsect(Section *sect, Sym *first) +{ + Sym *sym; + int32 eaddr; + Reloc *r; + + // If main section has no bits, nothing to relocate. + if(sect->vaddr >= sect->seg->vaddr + sect->seg->filelen) + 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++) { + if(r->done) + continue; + if(machoreloc1(r, sym->value+r->off - sect->vaddr) < 0) + diag("unsupported obj reloc %d/%d to %s", r->type, r->siz, r->sym->name); + } + } + + sect->rellen = cpos() - sect->reloff; +} + +void +machoemitreloc(void) +{ + Section *sect; + + while(cpos()&7) + cput(0); + + machorelocsect(segtext.sect, textp); + for(sect=segtext.sect->next; sect!=nil; sect=sect->next) + machorelocsect(sect, datap); + for(sect=segdata.sect; sect!=nil; sect=sect->next) + machorelocsect(sect, datap); +} diff --git a/src/cmd/ld/macho.h b/src/cmd/ld/macho.h index baea6ff03..d759f4b0f 100644 --- a/src/cmd/ld/macho.h +++ b/src/cmd/ld/macho.h @@ -11,6 +11,7 @@ struct MachoHdr { typedef struct MachoSect MachoSect; struct MachoSect { char* name; + char* segname; uint64 addr; uint64 size; uint32 off; @@ -44,19 +45,15 @@ struct MachoLoad { uint32 *data; }; -typedef struct MachoDebug MachoDebug; -struct MachoDebug { - uint32 fileoffset; - uint32 filesize; -}; - MachoHdr* getMachoHdr(void); MachoSeg* newMachoSeg(char*, int); -MachoSect* newMachoSect(MachoSeg*, char*); +MachoSect* newMachoSect(MachoSeg*, char*, char*); MachoLoad* newMachoLoad(uint32, uint32); -MachoDebug* newMachoDebug(void); int machowrite(void); void machoinit(void); +void machosymorder(void); +void machoemitreloc(void); +int machoreloc1(Reloc*, vlong); /* * Total amount of space to reserve at the start of the file diff --git a/src/cmd/ld/pe.c b/src/cmd/ld/pe.c index f2903ba0f..090d083f5 100644 --- a/src/cmd/ld/pe.c +++ b/src/cmd/ld/pe.c @@ -195,7 +195,7 @@ initdynimport(void) dr = nil; m = nil; for(s = allsym; s != S; s = s->allsym) { - if(!s->reachable || !s->dynimpname || s->dynexport) + if(!s->reachable || s->type != SDYNIMPORT) continue; for(d = dr; d != nil; d = d->next) { if(strcmp(d->name,s->dynimplib) == 0) { @@ -262,7 +262,7 @@ addimports(IMAGE_SECTION_HEADER *datsect) for(m = d->ms; m != nil; m = m->next) { m->off = nextsectoff + cpos() - startoff; wputl(0); // hint - strput(m->s->dynimpname); + strput(m->s->extname); } } @@ -325,7 +325,7 @@ scmp(const void *p1, const void *p2) s1 = *(Sym**)p1; s2 = *(Sym**)p2; - return strcmp(s1->dynimpname, s2->dynimpname); + return strcmp(s1->extname, s2->extname); } static void @@ -335,7 +335,7 @@ initdynexport(void) nexport = 0; for(s = allsym; s != S; s = s->allsym) { - if(!s->reachable || !s->dynimpname || !s->dynexport) + if(!s->reachable || !(s->cgoexport & CgoExportDynamic)) continue; if(nexport+1 > sizeof(dexport)/sizeof(dexport[0])) { diag("pe dynexport table is full"); @@ -358,7 +358,7 @@ addexports(void) size = sizeof e + 10*nexport + strlen(outfile) + 1; for(i=0; i<nexport; i++) - size += strlen(dexport[i]->dynimpname) + 1; + size += strlen(dexport[i]->extname) + 1; if (nexport == 0) return; @@ -394,7 +394,7 @@ addexports(void) v = e.Name + strlen(outfile)+1; for(i=0; i<nexport; i++) { lputl(v); - v += strlen(dexport[i]->dynimpname)+1; + v += strlen(dexport[i]->extname)+1; } // put EXPORT Ordinal Table for(i=0; i<nexport; i++) @@ -402,7 +402,7 @@ addexports(void) // put Names strnput(outfile, strlen(outfile)+1); for(i=0; i<nexport; i++) - strnput(dexport[i]->dynimpname, strlen(dexport[i]->dynimpname)+1); + strnput(dexport[i]->extname, strlen(dexport[i]->extname)+1); strnput("", sect->SizeOfRawData - size); } diff --git a/src/cmd/ld/symtab.c b/src/cmd/ld/symtab.c index 89a594872..7c8ba642f 100644 --- a/src/cmd/ld/symtab.c +++ b/src/cmd/ld/symtab.c @@ -121,18 +121,53 @@ putelfsym(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go) // One pass for each binding: STB_LOCAL, STB_GLOBAL, // maybe one day STB_WEAK. - bind = (ver || (x->type & SHIDDEN)) ? STB_LOCAL : STB_GLOBAL; + bind = STB_GLOBAL; + if(ver || (x->type & SHIDDEN)) + bind = STB_LOCAL; + + // In external linking mode, we have to invoke gcc with -rdynamic + // to get the exported symbols put into the dynamic symbol table. + // To avoid filling the dynamic table with lots of unnecessary symbols, + // mark all Go symbols local (not global) in the final executable. + if(linkmode == LinkExternal && !(x->cgoexport&CgoExportStatic)) + bind = STB_LOCAL; + if(bind != elfbind) return; off = putelfstr(s); - if(isobj) + if(linkmode == LinkExternal) 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 +putelfsectionsym(Sym* s, int shndx) +{ + putelfsyment(0, 0, 0, (STB_LOCAL<<4)|STT_SECTION, shndx, 0); + s->elfsym = numelfsym++; +} + +void +putelfsymshndx(vlong sympos, int shndx) +{ + vlong here; + + here = cpos(); + switch(thechar) { + case '6': + cseek(sympos+6); + break; + default: + cseek(sympos+14); + break; + } + WPUT(shndx); + cseek(here); +} + +void asmelfsym(void) { Sym *s; @@ -140,8 +175,30 @@ asmelfsym(void) // the first symbol entry is reserved putelfsyment(0, 0, 0, (STB_LOCAL<<4)|STT_NOTYPE, 0, 0); + dwarfaddelfsectionsyms(); + elfbind = STB_LOCAL; genasmsym(putelfsym); + + if(linkmode == LinkExternal && HEADTYPE != Hopenbsd) { + s = lookup("runtime.m", 0); + if(s->sect == nil) { + cursym = nil; + diag("missing section for %s", s->name); + errorexit(); + } + putelfsyment(putelfstr(s->name), 0, PtrSize, (STB_LOCAL<<4)|STT_TLS, s->sect->elfsect->shnum, 0); + s->elfsym = numelfsym++; + + s = lookup("runtime.g", 0); + if(s->sect == nil) { + cursym = nil; + diag("missing section for %s", s->name); + errorexit(); + } + putelfsyment(putelfstr(s->name), PtrSize, PtrSize, (STB_LOCAL<<4)|STT_TLS, s->sect->elfsect->shnum, 0); + s->elfsym = numelfsym++; + } elfbind = STB_GLOBAL; elfglobalsymndx = numelfsym; @@ -317,7 +374,7 @@ putsymb(Sym *s, char *name, int t, vlong v, vlong size, int ver, Sym *typ) // type byte if('A' <= t && t <= 'Z') - c = t - 'A'; + c = t - 'A' + (ver ? 26 : 0); else if('a' <= t && t <= 'z') c = t - 'a' + 26; else { @@ -424,10 +481,8 @@ symtab(void) 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("egcdata", STYPE, 0); + xdefine("egcbss", STYPE, 0); xdefine("noptrdata", SNOPTRDATA, 0); xdefine("enoptrdata", SNOPTRDATA, 0); xdefine("data", SDATA, 0); |