diff options
Diffstat (limited to 'src/cmd/ld')
-rw-r--r-- | src/cmd/ld/data.c | 334 | ||||
-rw-r--r-- | src/cmd/ld/dwarf.c | 294 | ||||
-rw-r--r-- | src/cmd/ld/dwarf_defs.h | 1 | ||||
-rw-r--r-- | src/cmd/ld/elf.c | 54 | ||||
-rw-r--r-- | src/cmd/ld/elf.h | 2 | ||||
-rw-r--r-- | src/cmd/ld/go.c | 107 | ||||
-rw-r--r-- | src/cmd/ld/ldelf.c | 4 | ||||
-rw-r--r-- | src/cmd/ld/ldmacho.c | 4 | ||||
-rw-r--r-- | src/cmd/ld/ldpe.c | 10 | ||||
-rw-r--r-- | src/cmd/ld/lib.c | 334 | ||||
-rw-r--r-- | src/cmd/ld/lib.h | 47 | ||||
-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 | 20 |
15 files changed, 1217 insertions, 613 deletions
diff --git a/src/cmd/ld/data.c b/src/cmd/ld/data.c index 6c6b1be43..b70d4636c 100644 --- a/src/cmd/ld/data.c +++ b/src/cmd/ld/data.c @@ -33,6 +33,7 @@ #include "l.h" #include "../ld/lib.h" #include "../ld/elf.h" +#include "../ld/macho.h" #include "../ld/pe.h" #include "../../pkg/runtime/mgc0.h" @@ -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) { @@ -180,35 +178,72 @@ relocsym(Sym *s) 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_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; - } break; case D_SIZE: o = r->sym->size + r->add; @@ -300,7 +335,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 +377,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 +591,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 +614,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, r->add, 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 @@ -883,36 +939,41 @@ 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 int32 aligndatsize(int32 datsize, Sym *s) { - int32 t; + return rnd(datsize, symalign(s)); +} - 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); +// maxalign returns the maximum required alignment for +// the list of symbols s; the list stops when s->type exceeds type. +static int32 +maxalign(Sym *s, int type) +{ + 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 @@ -946,7 +1007,7 @@ gcaddsym(Sym *gc, Sym *s, int32 off) void dodata(void) { - int32 t, datsize; + int32 n, datsize; Section *sect; Sym *s, *last, **l; Sym *gcdata1, *gcbss1; @@ -956,11 +1017,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 +1056,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 +1099,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); + datsize += s->size; 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; + datsize += s->size; } 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); + datsize += s->size; } 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,39 +1157,39 @@ 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; + datsize += s->size; } 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; + datsize += s->size; } 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; @@ -1131,117 +1198,101 @@ dodata(void) cursym = s; diag("unexpected symbol type %d", s->type); } - s->sect = sect; - t = alignsymsize(s->size); datsize = aligndatsize(datsize, s); + s->sect = sect; s->value = datsize; - datsize += t; + datsize += s->size; } sect->len = datsize - sect->vaddr; lookup("end", 0)->sect = sect; /* we finished segdata, begin segtext */ + s = datap; + datsize = 0; /* 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); + datsize += s->size; } 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; - } - 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; } 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; } 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); + datsize += s->size; sect->len = datsize - sect->vaddr; } + + /* 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 +1310,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 +1334,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 +1342,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 +1375,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 +1392,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 +1420,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/dwarf.c b/src/cmd/ld/dwarf.c index d6a357e49..4bf788e64 100644 --- a/src/cmd/ld/dwarf.c +++ b/src/cmd/ld/dwarf.c @@ -41,9 +41,13 @@ static vlong arangeso; static vlong arangessize; static vlong gdbscripto; static vlong gdbscriptsize; +static vlong inforeloco; +static vlong inforelocsize; static char gdbscript[1024]; +static Sym *dsym; + /* * Basic I/O */ @@ -150,6 +154,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 +308,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 +496,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 +550,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,7 +567,7 @@ 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; @@ -548,14 +584,33 @@ 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) { + Reloc *r; + switch(form) { case DW_FORM_addr: // address addrput(value); break; case DW_FORM_block1: // block + if(cls == DW_CLS_ADDRESS) { + cput(1+PtrSize); + cput(DW_OP_addr); + if(linkmode == LinkExternal) { + r = addrel(dsym); + r->sym = (Sym*)data; + r->xsym = r->sym; + r->off = cpos() - infoo; + r->siz = PtrSize; + r->type = D_ADDR; + r->add = value - r->sym->value; + r->xadd = r->add; + value = r->add; + } + addrput(value); + break; + } value &= 0xff; cput(value); while(value--) @@ -615,13 +670,23 @@ 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) fwdcount++; - LPUT(((DWDie*)data)->offs); + if(PtrSize == 8) + VPUT(((DWDie*)data)->offs); + else + LPUT(((DWDie*)data)->offs); } break; @@ -654,12 +719,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 +794,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 +824,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 +923,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 +941,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 +961,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 +981,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 +1001,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 +1016,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 +1086,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 +1102,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; @@ -1043,33 +1131,17 @@ mkinternaltypename(char *base, char *arg1, char *arg2) } -// synthesizemaptypes is way too closely married to runtime/hashmap.c -enum { - MaxValsize = 256 - 64 -}; - static void synthesizemaptypes(DWDie *die) { - DWDie *hash, *hash_subtable, *hash_entry, - *dwh, *dwhs, *dwhe, *dwhash, *keytype, *valtype, *fld; - int hashsize, keysize, valsize, datsize, valsize_in_hash, datavo; - DWAttr *a; + DWDie *hash, *dwh, *keytype, *valtype; 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) + if (hash == nil) return; - dwhash = (DWDie*)getattr(find_or_diag(hash_entry, "hash"), DW_AT_type)->data; - if (dwhash == nil) - return; - - hashsize = getattr(dwhash, DW_AT_byte_size)->value; - for (; die != nil; die = die->link) { if (die->abbrev != DW_ABRV_MAPTYPE) continue; @@ -1077,63 +1149,12 @@ synthesizemaptypes(DWDie *die) keytype = (DWDie*) getattr(die, DW_AT_internal_key_type)->data; valtype = (DWDie*) getattr(die, DW_AT_internal_val_type)->data; - a = getattr(keytype, DW_AT_byte_size); - keysize = a ? a->value : PtrSize; // We don't store size with Pointers - - a = getattr(valtype, DW_AT_byte_size); - valsize = a ? a->value : PtrSize; - - // This is what happens in hash_init and makemap_c - valsize_in_hash = valsize; - if (valsize > MaxValsize) - valsize_in_hash = PtrSize; - datavo = keysize; - if (valsize_in_hash >= PtrSize) - datavo = rnd(keysize, PtrSize); - datsize = datavo + valsize_in_hash; - if (datsize < PtrSize) - datsize = PtrSize; - datsize = rnd(datsize, PtrSize); - - // Construct struct hash_entry<K,V> - dwhe = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, - mkinternaltypename("hash_entry", - getattr(keytype, DW_AT_name)->data, - getattr(valtype, DW_AT_name)->data)); - - fld = newdie(dwhe, DW_ABRV_STRUCTFIELD, "hash"); - newrefattr(fld, DW_AT_type, dwhash); - newmemberoffsetattr(fld, 0); - - fld = newdie(dwhe, DW_ABRV_STRUCTFIELD, "key"); - newrefattr(fld, DW_AT_type, keytype); - newmemberoffsetattr(fld, hashsize); - - fld = newdie(dwhe, DW_ABRV_STRUCTFIELD, "val"); - if (valsize > MaxValsize) - valtype = defptrto(valtype); - newrefattr(fld, DW_AT_type, valtype); - newmemberoffsetattr(fld, hashsize + datavo); - newattr(dwhe, DW_AT_byte_size, DW_CLS_CONSTANT, hashsize + datsize, nil); - - // Construct hash_subtable<hash_entry<K,V>> - dwhs = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, - mkinternaltypename("hash_subtable", - getattr(keytype, DW_AT_name)->data, - getattr(valtype, DW_AT_name)->data)); - copychildren(dwhs, hash_subtable); - substitutetype(dwhs, "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 hash<K,V> dwh = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, mkinternaltypename("hash", getattr(keytype, DW_AT_name)->data, getattr(valtype, DW_AT_name)->data)); copychildren(dwh, hash); - substitutetype(dwh, "st", defptrto(dwhs)); newattr(dwh, DW_AT_byte_size, DW_CLS_CONSTANT, getattr(hash, DW_AT_byte_size)->value, nil); @@ -1149,9 +1170,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 +1241,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 +1281,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 +1363,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 +1433,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 +1602,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 +1618,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 +1628,7 @@ static void writelines(void) { Prog *q; - Sym *s; + Sym *s, *epcs; Auto *a; vlong unitstart, headerend, offs; vlong pc, epc, lc, llc, lline; @@ -1622,6 +1643,7 @@ writelines(void) headerend = -1; pc = 0; epc = 0; + epcs = S; lc = 1; llc = 1; currfile = -1; @@ -1637,7 +1659,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 +1673,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 +1705,7 @@ writelines(void) pc = s->text->pc; epc = pc; + epcs = s; currfile = 1; lc = 1; llc = 1; @@ -1701,9 +1724,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 +1808,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 +1932,9 @@ writeinfo(void) vlong unitstart, here; fwdcount = 0; + if (dsym == S) + dsym = lookup(".dwarfinfo", 0); + dsym->nr = 0; for (compunit = dwroot.child; compunit; compunit = compunit->link) { unitstart = cpos(); @@ -1917,7 +1943,7 @@ 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) + WPUT(2); // dwarf version (appendix F) LPUT(0); // debug_abbrev_offset (*) cput(PtrSize); // address_size @@ -2057,6 +2083,27 @@ align(vlong size) strnput("", rnd(size, PEFILEALIGN) - size); } +static vlong +writeinforeloc(void) +{ + int i; + vlong start; + Reloc *r; + + start = cpos(); + for(r = dsym->r; r < dsym->r+dsym->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 @@ -2085,8 +2132,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 +2203,10 @@ dwarfemitdebugsections(void) gdbscripto = writegdbscript(); gdbscriptsize = cpos() - gdbscripto; align(gdbscriptsize); + + inforeloco = writeinforeloc(); + inforelocsize = cpos() - inforeloco; + align(inforelocsize); } /* @@ -2298,42 +2348,44 @@ 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; + msect->reloc = inforeloco; + msect->nreloc = inforelocsize / 8; 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 +2393,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..f5cce9c52 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,9 @@ elfshbits(Section *sect) sh->flags |= SHF_EXECINSTR; if(sect->rwx & 2) sh->flags |= SHF_WRITE; - if(!isobj) + 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; @@ -807,10 +805,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 +833,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); } } @@ -915,7 +899,7 @@ doelf(void) addstring(shstrtab, ".gosymtab"); addstring(shstrtab, ".gopclntab"); - if(isobj) { + if(linkmode == LinkExternal) { debug['s'] = 0; debug['d'] = 1; @@ -1147,7 +1131,7 @@ asmbelf(vlong symo) resoff = ELFRESERVE; pph = nil; - if(isobj) { + if(linkmode == LinkExternal) { /* skip program headers */ eh->phoff = 0; eh->phentsize = 0; @@ -1408,7 +1392,7 @@ 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) @@ -1431,8 +1415,8 @@ elfobj: sh->size = elfstrsize; sh->addralign = 1; - // TODO(rsc): Enable for isobj too, once we know it works. - if(!isobj) + // TODO(rsc): Enable for linkmode == LinkExternal too, once we know it works. + if(linkmode != LinkExternal) dwarfaddelfheaders(); } @@ -1456,12 +1440,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 +1462,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..336fab4b4 100644 --- a/src/cmd/ld/elf.h +++ b/src/cmd/ld/elf.h @@ -1005,7 +1005,7 @@ 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); EXTERN int elfstrsize; EXTERN char* elfstrdat; diff --git a/src/cmd/ld/go.c b/src/cmd/ld/go.c index b2527b13e..f933cbba3 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) { @@ -467,7 +465,7 @@ loadcgo(char *file, char *pkg, char *p, int n) free(local); if(s->type == 0 || s->type == SXREF) { s->dynimplib = lib; - s->dynimpname = remote; + s->extname = remote; s->dynimpvers = q; s->type = SDYNIMPORT; havedynamic = 1; @@ -478,16 +476,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 +497,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) { + if(strcmp(f[0], "cgo_export_static") == 0) + s->cgoexport |= CgoExportStatic; + else + s->cgoexport |= CgoExportDynamic; + 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(local != f[1]) free(local); continue; @@ -524,10 +537,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 +773,9 @@ addexport(void) { int i; + if(HEADTYPE == Hdarwin) + return; + for(i=0; i<ndynexp; i++) adddynsym(dynexp[i]); } @@ -829,7 +854,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 +878,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 +924,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..098cb7bef 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; diff --git a/src/cmd/ld/ldpe.c b/src/cmd/ld/ldpe.c index 39c15e6a1..98923bfbf 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 diff --git a/src/cmd/ld/lib.c b/src/cmd/ld/lib.c index 26fa4f2ac..18cae3175 100644 --- a/src/cmd/ld/lib.c +++ b/src/cmd/ld/lib.c @@ -44,6 +44,8 @@ int nlibdir = 0; static int maxlibdir = 0; static int cout = -1; +static void hostlinksetup(void); + char* goroot; char* goarch; char* goos; @@ -59,11 +61,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 +93,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 +240,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,7 +286,8 @@ loadinternal(char *name) void loadlib(void) { - int i; + int i, w, x; + Sym *s; loadinternal("runtime"); if(thechar == '5') @@ -303,6 +302,37 @@ loadlib(void) objfile(library[i].file, library[i].pkg); } + if(linkmode == LinkExternal && !iscgo) + linkmode = LinkInternal; + + // If we got this far in automatic mode, there were no + // cgo uses that suggest we need external mode. + // Switch to internal. + if(linkmode == LinkAuto) { + 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) + 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 +346,6 @@ loadlib(void) debug['d'] = 1; importcycles(); - sortdynexp(); } /* @@ -375,7 +404,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 +463,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 +471,229 @@ 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 +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 && linkmode == LinkAuto) + linkmode = LinkExternal; + + 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 -ldobj(Biobuf *f, char *pkg, int64 len, char *pn, int whence) +hostlink(void) +{ + char *p, **argv; + int i, w, n, argc, len; + Hostobj *h; + Biobuf *f; + static char buf[64<<10]; + + if(linkmode != LinkExternal || nerrors > 0) + return; + + argv = malloc((10+nhostobj+nldflag)*sizeof argv[0]); + argc = 0; + // TODO: Add command-line flag to override gcc path and specify additional leading options. + // TODO: Add command-line flag to specify additional trailing options. + argv[argc++] = "gcc"; + 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; + + // Force global symbols to be exported for dlopen, etc. + // NOTE: May not work on OS X or Windows. We'll see. + 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) { + 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) { + 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]; + 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) { + 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 +703,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 +716,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 +771,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); @@ -615,6 +862,7 @@ _lookup(char *symb, int v, int creat) return nil; s = newsym(symb, v); + s->extname = s->name; s->hash = hash[h]; hash[h] = s; @@ -1027,6 +1275,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 +1709,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 +1808,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 +1823,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); @@ -1664,3 +1894,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..a5ca7d3c3 100644 --- a/src/cmd/ld/lib.h +++ b/src/cmd/ld/lib.h @@ -34,30 +34,28 @@ 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, SXREF, - SMACHODYNSTR, - SMACHODYNSYM, + SMACHOSYMSTR, + SMACHOSYMTAB, SMACHOINDIRECTPLT, SMACHOINDIRECTGOT, SFILE, @@ -104,6 +102,8 @@ struct Segment struct Section { uchar rwx; + int16 extnum; + int32 align; char *name; uvlong vaddr; uvlong len; @@ -138,20 +138,38 @@ 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; + +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 +203,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,6 +225,7 @@ 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); @@ -240,6 +259,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 +382,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..698194f84 100644 --- a/src/cmd/ld/symtab.c +++ b/src/cmd/ld/symtab.c @@ -121,12 +121,22 @@ 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++; @@ -424,10 +434,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); |