From db923390a01d4c992116161bed1328bd5bb32a24 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Tue, 16 Dec 2014 14:59:59 -0500 Subject: [PATCH] cmd/9l: support internal linking This implements the ELF relocations and dynamic linking tables necessary to support internal linking on ppc64. It also marks ppc64le ELF files as ABI v2; failing to do this doesn't seem to confuse the loader, but it does confuse libbfd (and hence gdb, objdump, etc). Change-Id: I559dddf89b39052e1b6288a4dd5e72693b5355e4 Reviewed-on: https://go-review.googlesource.com/2006 Reviewed-by: Russ Cox --- include/link.h | 9 + src/cmd/9l/asm.c | 511 +++++++++++++++++++++++++++++++++++++++++++-- src/cmd/9l/obj.c | 3 +- src/cmd/ld/data.c | 37 +++- src/cmd/ld/elf.c | 83 ++++++-- src/cmd/ld/elf.h | 16 ++ src/cmd/ld/ldelf.c | 29 +++ 7 files changed, 651 insertions(+), 37 deletions(-) diff --git a/include/link.h b/include/link.h index bc163d6e4b..15a2878792 100644 --- a/include/link.h +++ b/include/link.h @@ -212,6 +212,7 @@ enum SMACHO, /* Mach-O __nl_symbol_ptr */ SMACHOGOT, SWINDOWS, + SELFGOT, /* also .toc in ppc64 ABI */ SNOPTRDATA, SINITARR, SDATA, @@ -256,12 +257,20 @@ enum R_PLT1, R_PLT2, R_USEFIELD, + R_POWER_TOC, // ELF R_PPC64_TOC16* }; // Reloc.variant enum { RV_NONE, // identity variant + RV_POWER_LO, // x & 0xFFFF + RV_POWER_HI, // x >> 16 + RV_POWER_HA, // (x + 0x8000) >> 16 + RV_POWER_DS, // x & 0xFFFC, check x&0x3 == 0 + + RV_CHECK_OVERFLOW = 1<<8, // check overflow flag + RV_TYPE_MASK = (RV_CHECK_OVERFLOW - 1), }; // Auto.type diff --git a/src/cmd/9l/asm.c b/src/cmd/9l/asm.c index 00651d5714..391f9562cf 100644 --- a/src/cmd/9l/asm.c +++ b/src/cmd/9l/asm.c @@ -36,6 +36,7 @@ #include "../ld/dwarf.h" +// TODO(austin): ABI v1 uses /usr/lib/ld.so.1 char linuxdynld[] = "/lib64/ld64.so.1"; char freebsddynld[] = "XXX"; char openbsddynld[] = "XXX"; @@ -65,27 +66,264 @@ needlib(char *name) int nelfsym = 1; +static void gencallstub(int abicase, LSym *stub, LSym *targ); +static void addpltsym(Link*, LSym*); +static LSym* ensureglinkresolver(void); + void gentext(void) { + LSym *s, *stub, **pprevtextp; + Reloc *r; + char *n; + uint32 o1; + uchar *cast; + int i; + + // The ppc64 ABI PLT has similar concepts to other + // architectures, but is laid out quite differently. When we + // see an R_PPC64_REL24 relocation to a dynamic symbol + // (indicating that the call needs to go through the PLT), we + // generate up to three stubs and reserve a PLT slot. + // + // 1) The call site will be bl x; nop (where the relocation + // applies to the bl). We rewrite this to bl x_stub; ld + // r2,24(r1). The ld is necessary because x_stub will save + // r2 (the TOC pointer) at 24(r1) (the "TOC save slot"). + // + // 2) We reserve space for a pointer in the .plt section (once + // per referenced dynamic function). .plt is a data + // section filled solely by the dynamic linker (more like + // .plt.got on other architectures). Initially, the + // dynamic linker will fill each slot with a pointer to the + // corresponding x@plt entry point. + // + // 3) We generate the "call stub" x_stub (once per dynamic + // function/object file pair). This saves the TOC in the + // TOC save slot, reads the function pointer from x's .plt + // slot and calls it like any other global entry point + // (including setting r12 to the function address). + // + // 4) We generate the "symbol resolver stub" x@plt (once per + // dynamic function). This is solely a branch to the glink + // resolver stub. + // + // 5) We generate the glink resolver stub (only once). This + // computes which symbol resolver stub we came through and + // invokes the dynamic resolver via a pointer provided by + // the dynamic linker. This will patch up the .plt slot to + // point directly at the function so future calls go + // straight from the call stub to the real function, and + // then call the function. + + // NOTE: It's possible we could make ppc64 closer to other + // architectures: ppc64's .plt is like .plt.got on other + // platforms and ppc64's .glink is like .plt on other + // platforms. + + // Find all R_PPC64_REL24 relocations that reference dynamic + // imports. Reserve PLT entries for these symbols and + // generate call stubs. The call stubs need to live in .text, + // which is why we need to do this pass this early. + // + // This assumes "case 1" from the ABI, where the caller needs + // us to save and restore the TOC pointer. + pprevtextp = &ctxt->textp; + for(s=*pprevtextp; s!=S; pprevtextp=&s->next, s=*pprevtextp) { + for(r=s->r; rr+s->nr; r++) { + if(!(r->type == 256 + R_PPC64_REL24 && + r->sym->type == SDYNIMPORT)) + continue; + + // Reserve PLT entry and generate symbol + // resolver + addpltsym(ctxt, r->sym); + + // Generate call stub + n = smprint("%s.%s", s->name, r->sym->name); + stub = linklookup(ctxt, n, 0); + free(n); + stub->reachable |= s->reachable; + if(stub->size == 0) { + // Need outer to resolve .TOC. + stub->outer = s; + + // Link in to textp before s (we could + // do it after, but would have to skip + // the subsymbols) + *pprevtextp = stub; + stub->next = s; + pprevtextp = &stub->next; + + gencallstub(1, stub, r->sym); + } + + // Update the relocation to use the call stub + r->sym = stub; + + // Restore TOC after bl. The compiler put a + // nop here for us to overwrite. + o1 = 0xe8410018; // ld r2,24(r1) + cast = (uchar*)&o1; + for(i=0; i<4; i++) + s->p[r->off+4+i] = cast[inuxi4[i]]; + } + } +} + +// Construct a call stub in stub that calls symbol targ via its PLT +// entry. +static void +gencallstub(int abicase, LSym *stub, LSym *targ) +{ + LSym *plt; + Reloc *r; + + if(abicase != 1) + // If we see R_PPC64_TOCSAVE or R_PPC64_REL24_NOTOC + // relocations, we'll need to implement cases 2 and 3. + sysfatal("gencallstub only implements case 1 calls"); + + plt = linklookup(ctxt, ".plt", 0); + + stub->type = STEXT; + + // Save TOC pointer in TOC save slot + adduint32(ctxt, stub, 0xf8410018); // std r2,24(r1) + + // Load the function pointer from the PLT. + r = addrel(stub); + r->off = stub->size; + r->sym = plt; + r->add = targ->plt; + r->siz = 2; + if(ctxt->arch->endian == BigEndian) + r->off += r->siz; + r->type = R_POWER_TOC; + r->variant = RV_POWER_HA; + adduint32(ctxt, stub, 0x3d820000); // addis r12,r2,targ@plt@toc@ha + r = addrel(stub); + r->off = stub->size; + r->sym = plt; + r->add = targ->plt; + r->siz = 2; + if(ctxt->arch->endian == BigEndian) + r->off += r->siz; + r->type = R_POWER_TOC; + r->variant = RV_POWER_LO; + adduint32(ctxt, stub, 0xe98c0000); // ld r12,targ@plt@toc@l(r12) + + // Jump to the loaded pointer + adduint32(ctxt, stub, 0x7d8903a6); // mtctr r12 + adduint32(ctxt, stub, 0x4e800420); // bctr } void adddynrela(LSym *rel, LSym *s, Reloc *r) { - // TODO(minux) USED(rel); USED(s); USED(r); + sysfatal("adddynrela not implemented"); } void adddynrel(LSym *s, Reloc *r) { - LSym *targ; - - // TODO(minux) + LSym *targ, *rela; targ = r->sym; ctxt->cursym = s; + + switch(r->type) { + default: + if(r->type >= 256) { + diag("unexpected relocation type %d", r->type); + return; + } + break; + + // Handle relocations found in ELF object files. + case 256 + R_PPC64_REL24: + r->type = R_CALLPOWER; + // This is a local call, so the caller isn't setting + // up r12 and r2 is the same for the caller and + // callee. Hence, we need to go to the local entry + // point. (If we don't do this, the callee will try + // to use r12 to compute r2.) + r->add += r->sym->localentry * 4; + if(targ->type == SDYNIMPORT) + // Should have been handled in elfsetupplt + diag("unexpected R_PPC64_REL24 for dyn import"); + return; + + case 256 + R_PPC64_ADDR64: + r->type = R_ADDR; + if(targ->type == SDYNIMPORT) { + // These happen in .toc sections + adddynsym(ctxt, targ); + + rela = linklookup(ctxt, ".rela", 0); + addaddrplus(ctxt, rela, s, r->off); + adduint64(ctxt, rela, ELF64_R_INFO(targ->dynid, R_PPC64_ADDR64)); + adduint64(ctxt, rela, r->add); + r->type = 256; // ignore during relocsym + } + return; + + case 256 + R_PPC64_TOC16: + r->type = R_POWER_TOC; + r->variant = RV_POWER_LO | RV_CHECK_OVERFLOW; + return; + + case 256 + R_PPC64_TOC16_LO: + r->type = R_POWER_TOC; + r->variant = RV_POWER_LO; + return; + + case 256 + R_PPC64_TOC16_HA: + r->type = R_POWER_TOC; + r->variant = RV_POWER_HA | RV_CHECK_OVERFLOW; + return; + + case 256 + R_PPC64_TOC16_HI: + r->type = R_POWER_TOC; + r->variant = RV_POWER_HI | RV_CHECK_OVERFLOW; + return; + + case 256 + R_PPC64_TOC16_DS: + r->type = R_POWER_TOC; + r->variant = RV_POWER_DS | RV_CHECK_OVERFLOW; + return; + + case 256 + R_PPC64_TOC16_LO_DS: + r->type = R_POWER_TOC; + r->variant = RV_POWER_DS; + return; + + case 256 + R_PPC64_REL16_LO: + r->type = R_PCREL; + r->variant = RV_POWER_LO; + r->add += 2; // Compensate for relocation size of 2 + return; + + case 256 + R_PPC64_REL16_HI: + r->type = R_PCREL; + r->variant = RV_POWER_HI | RV_CHECK_OVERFLOW; + r->add += 2; + return; + + case 256 + R_PPC64_REL16_HA: + r->type = R_PCREL; + r->variant = RV_POWER_HA | RV_CHECK_OVERFLOW; + r->add += 2; + return; + } + + // Handle references to ELF symbols from our own object files. + if(targ->type != SDYNIMPORT) + return; + + // TODO(austin): Translate our relocations to ELF + diag("unsupported relocation for dynamic symbol %s (type=%d stype=%d)", targ->name, r->type, targ->type); } @@ -100,8 +338,16 @@ elfreloc1(Reloc *r, vlong sectoff) void elfsetupplt(void) { - // TODO(minux) - return; + LSym *plt; + + plt = linklookup(ctxt, ".plt", 0); + if(plt->size == 0) { + // The dynamic linker stores the address of the + // dynamic resolver and the DSO identifier in the two + // doublewords at the beginning of the .plt section + // before the PLT array. Reserve space for these. + plt->size = 16; + } } int @@ -113,12 +359,29 @@ machoreloc1(Reloc *r, vlong sectoff) return -1; } +// Return the value of .TOC. for symbol s +static vlong +symtoc(LSym *s) +{ + LSym *toc; + + if(s->outer != nil) + toc = linkrlookup(ctxt, ".TOC.", s->outer->version); + else + toc = linkrlookup(ctxt, ".TOC.", s->version); + + if(toc == nil) { + diag("TOC-relative relocation in object without .TOC."); + return 0; + } + return toc->value; +} int archreloc(Reloc *r, LSym *s, vlong *val) { uint32 o1, o2; - int32 t; + vlong t; if(linkmode == LinkExternal) { // TODO(minux): translate R_ADDRPOWER and R_CALLPOWER into standard ELF relocations. @@ -166,13 +429,16 @@ archreloc(Reloc *r, LSym *s, vlong *val) t = symaddr(r->sym) + r->add - (s->value + r->off); if(t & 3) ctxt->diag("relocation for %s+%d is not aligned: %lld", r->sym->name, r->off, t); - if(t << 6 >> 6 != t) + if((int32)(t << 6) >> 6 != t) // TODO(austin) This can happen if text > 32M. // Add a call trampoline to .text in that case. ctxt->diag("relocation for %s+%d is too big: %lld", r->sym->name, r->off, t); *val = (o1 & 0xfc000003U) | (t & ~0xfc000003U); return 0; + case R_POWER_TOC: // S + A - .TOC. + *val = symaddr(r->sym) + r->add - symtoc(s); + return 0; } return -1; } @@ -180,18 +446,235 @@ archreloc(Reloc *r, LSym *s, vlong *val) vlong archrelocvariant(Reloc *r, LSym *s, vlong t) { - USED(r); - USED(s); - sysfatal("unexpected relocation variant"); + uint32 o1; + switch(r->variant & RV_TYPE_MASK) { + default: + diag("unexpected relocation variant %d", r->variant); + + case RV_NONE: + return t; + + case RV_POWER_LO: + if(r->variant & RV_CHECK_OVERFLOW) { + // Whether to check for signed or unsigned + // overflow depends on the instruction + if(ctxt->arch->endian == BigEndian) + o1 = be32(s->p + r->off - 2); + else + o1 = le32(s->p + r->off); + switch(o1 >> 26) { + case 24: // ori + case 26: // xori + case 28: // andi + if((t >> 16) != 0) + goto overflow; + break; + default: + if((int16)t != t) + goto overflow; + break; + } + } + return (int16)t; + + case RV_POWER_HA: + t += 0x8000; + // Fallthrough + case RV_POWER_HI: + t >>= 16; + if(r->variant & RV_CHECK_OVERFLOW) { + // Whether to check for signed or unsigned + // overflow depends on the instruction + if(ctxt->arch->endian == BigEndian) + o1 = be32(s->p + r->off - 2); + else + o1 = le32(s->p + r->off); + switch(o1 >> 26) { + case 25: // oris + case 27: // xoris + case 29: // andis + if((t >> 16) != 0) + goto overflow; + break; + default: + if((int16)t != t) + goto overflow; + break; + } + } + return (int16)t; + + case RV_POWER_DS: + if(ctxt->arch->endian == BigEndian) + o1 = be16(s->p + r->off); + else + o1 = le16(s->p + r->off); + if(t & 3) + diag("relocation for %s+%d is not aligned: %lld", r->sym->name, r->off, t); + if((r->variant & RV_CHECK_OVERFLOW) && (int16)t != t) + goto overflow; + return (o1 & 0x3) | (vlong)(int16)t; + } + +overflow: + diag("relocation for %s+%d is too big: %lld", r->sym->name, r->off, t); return t; } +static void +addpltsym(Link *ctxt, LSym *s) +{ + if(s->plt >= 0) + return; + + adddynsym(ctxt, s); + + if(iself) { + LSym *plt, *rela, *glink; + Reloc *r; + + plt = linklookup(ctxt, ".plt", 0); + rela = linklookup(ctxt, ".rela.plt", 0); + if(plt->size == 0) + elfsetupplt(); + + // Create the glink resolver if necessary + glink = ensureglinkresolver(); + + // Write symbol resolver stub (just a branch to the + // glink resolver stub) + r = addrel(glink); + r->sym = glink; + r->off = glink->size; + r->siz = 4; + r->type = R_CALLPOWER; + adduint32(ctxt, glink, 0x48000000); // b .glink + + // In the ppc64 ABI, the dynamic linker is responsible + // for writing the entire PLT. We just need to + // reserve 8 bytes for each PLT entry and generate a + // JMP_SLOT dynamic relocation for it. + // + // TODO(austin): ABI v1 is different + s->plt = plt->size; + plt->size += 8; + + addaddrplus(ctxt, rela, plt, s->plt); + adduint64(ctxt, rela, ELF64_R_INFO(s->dynid, R_PPC64_JMP_SLOT)); + adduint64(ctxt, rela, 0); + } else { + diag("addpltsym: unsupported binary format"); + } +} + +// Generate the glink resolver stub if necessary and return the .glink section +static LSym* +ensureglinkresolver(void) +{ + LSym *glink, *s; + Reloc *r; + + glink = linklookup(ctxt, ".glink", 0); + if(glink->size != 0) + return glink; + + // This is essentially the resolver from the ppc64 ELF ABI. + // At entry, r12 holds the address of the symbol resolver stub + // for the target routine and the argument registers hold the + // arguments for the target routine. + // + // This stub is PIC, so first get the PC of label 1 into r11. + // Other things will be relative to this. + adduint32(ctxt, glink, 0x7c0802a6); // mflr r0 + adduint32(ctxt, glink, 0x429f0005); // bcl 20,31,1f + adduint32(ctxt, glink, 0x7d6802a6); // 1: mflr r11 + adduint32(ctxt, glink, 0x7c0803a6); // mtlf r0 + + // Compute the .plt array index from the entry point address. + // Because this is PIC, everything is relative to label 1b (in + // r11): + // r0 = ((r12 - r11) - (res_0 - r11)) / 4 = (r12 - res_0) / 4 + adduint32(ctxt, glink, 0x3800ffd0); // li r0,-(res_0-1b)=-48 + adduint32(ctxt, glink, 0x7c006214); // add r0,r0,r12 + adduint32(ctxt, glink, 0x7c0b0050); // sub r0,r0,r11 + adduint32(ctxt, glink, 0x7800f082); // srdi r0,r0,2 + + // r11 = address of the first byte of the PLT + r = addrel(glink); + r->off = glink->size; + r->sym = linklookup(ctxt, ".plt", 0); + r->siz = 8; + r->type = R_ADDRPOWER; + // addis r11,0,.plt@ha; addi r11,r11,.plt@l + r->add = (0x3d600000ull << 32) | 0x396b0000; + glink->size += 8; + + // Load r12 = dynamic resolver address and r11 = DSO + // identifier from the first two doublewords of the PLT. + adduint32(ctxt, glink, 0xe98b0000); // ld r12,0(r11) + adduint32(ctxt, glink, 0xe96b0008); // ld r11,8(r11) + + // Jump to the dynamic resolver + adduint32(ctxt, glink, 0x7d8903a6); // mtctr r12 + adduint32(ctxt, glink, 0x4e800420); // bctr + + // The symbol resolvers must immediately follow. + // res_0: + + // Add DT_PPC64_GLINK .dynamic entry, which points to 32 bytes + // before the first symbol resolver stub. + s = linklookup(ctxt, ".dynamic", 0); + elfwritedynentsymplus(s, DT_PPC64_GLINK, glink, glink->size - 32); + + return glink; +} + void adddynsym(Link *ctxt, LSym *s) { - USED(ctxt); USED(s); - // TODO(minux) - return; + LSym *d; + int t; + char *name; + + if(s->dynid >= 0) + return; + + if(iself) { + s->dynid = nelfsym++; + + d = linklookup(ctxt, ".dynsym", 0); + + name = s->extname; + adduint32(ctxt, d, addstring(linklookup(ctxt, ".dynstr", 0), name)); + + /* type */ + t = STB_GLOBAL << 4; + if(s->cgoexport && (s->type&SMASK) == STEXT) + t |= STT_FUNC; + else + t |= STT_OBJECT; + adduint8(ctxt, d, t); + + /* reserved */ + adduint8(ctxt, d, 0); + + /* section where symbol is defined */ + if(s->type == SDYNIMPORT) + adduint16(ctxt, d, SHN_UNDEF); + else + adduint16(ctxt, d, 1); + + /* value */ + if(s->type == SDYNIMPORT) + adduint64(ctxt, d, 0); + else + addaddr(ctxt, d, s); + + /* size of object */ + adduint64(ctxt, d, s->size); + } else { + diag("adddynsym: unsupported binary format"); + } } void diff --git a/src/cmd/9l/obj.c b/src/cmd/9l/obj.c index dab1ffb315..77f665e5a2 100644 --- a/src/cmd/9l/obj.c +++ b/src/cmd/9l/obj.c @@ -80,7 +80,8 @@ archinit(void) INITRND = 4096; break; case Hlinux: /* ppc64 elf */ - debug['d'] = 1; // TODO(minux): dynamic linking is not supported yet. + if(strcmp(thestring, "ppc64") == 0) + debug['d'] = 1; // TODO(austin): ELF ABI v1 not supported yet elfinit(); HEADR = ELFRESERVE; if(INITTEXT == -1) diff --git a/src/cmd/ld/data.c b/src/cmd/ld/data.c index 48e8a58866..22843b8948 100644 --- a/src/cmd/ld/data.c +++ b/src/cmd/ld/data.c @@ -49,7 +49,11 @@ datcmp(LSym *s1, LSym *s2) { if(s1->type != s2->type) return (int)s1->type - (int)s2->type; - if(s1->size != s2->size) { + // For ppc64, we want to interleave the .got and .toc sections + // from input files. Both are type SELFGOT, so in that case + // fall through to the name comparison (conveniently, .got + // sorts before .toc). + if(s1->type != SELFGOT && s1->size != s2->size) { if(s1->size < s2->size) return -1; return +1; @@ -920,7 +924,7 @@ dodata(void) vlong datsize; Section *sect; Segment *segro; - LSym *s, *last, **l; + LSym *s, *last, **l, *toc; LSym *gcdata, *gcbss; ProgGen gen; @@ -994,7 +998,7 @@ dodata(void) /* writable ELF sections */ datsize = 0; - for(; s != nil && s->type < SNOPTRDATA; s = s->next) { + for(; s != nil && s->type < SELFGOT; s = s->next) { sect = addsection(&segdata, s->name, 06); sect->align = symalign(s); datsize = rnd(datsize, sect->align); @@ -1006,6 +1010,33 @@ dodata(void) sect->len = datsize - sect->vaddr; } + /* .got (and .toc on ppc64) */ + if(s->type == SELFGOT) { + sect = addsection(&segdata, ".got", 06); + sect->align = maxalign(s, SELFGOT); + datsize = rnd(datsize, sect->align); + sect->vaddr = datsize; + for(; s != nil && s->type == SELFGOT; s = s->next) { + datsize = aligndatsize(datsize, s); + s->sect = sect; + s->type = SDATA; + s->value = datsize - sect->vaddr; + + // Resolve .TOC. symbol for this object file (ppc64) + toc = linkrlookup(ctxt, ".TOC.", s->version); + if(toc != nil) { + toc->sect = sect; + toc->outer = s; + toc->sub = s->sub; + s->sub = toc; + + toc->value = 0x8000; + } + growdatsize(&datsize, s); + } + sect->len = datsize - sect->vaddr; + } + /* pointer-free data */ sect = addsection(&segdata, ".noptrdata", 06); sect->align = maxalign(s, SINITARR-1); diff --git a/src/cmd/ld/elf.c b/src/cmd/ld/elf.c index b17d66890f..89a0a5e87f 100644 --- a/src/cmd/ld/elf.c +++ b/src/cmd/ld/elf.c @@ -306,12 +306,18 @@ elfwritedynent(LSym *s, int tag, uint64 val) void elfwritedynentsym(LSym *s, int tag, LSym *t) +{ + elfwritedynentsymplus(s, tag, t, 0); +} + +void +elfwritedynentsymplus(LSym *s, int tag, LSym *t, vlong add) { if(elf64) adduint64(ctxt, s, tag); else adduint32(ctxt, s, tag); - addaddr(ctxt, s, t); + addaddrplus(ctxt, s, t, add); } void @@ -977,6 +983,8 @@ doelf(void) addstring(shstrtab, ".interp"); addstring(shstrtab, ".hash"); addstring(shstrtab, ".got"); + if(thechar == '9') + addstring(shstrtab, ".glink"); addstring(shstrtab, ".got.plt"); addstring(shstrtab, ".dynamic"); addstring(shstrtab, ".dynsym"); @@ -1020,7 +1028,14 @@ doelf(void) /* global offset table */ s = linklookup(ctxt, ".got", 0); s->reachable = 1; - s->type = SELFSECT; // writable + s->type = SELFGOT; // writable + + /* ppc64 glink resolver */ + if(thechar == '9') { + s = linklookup(ctxt, ".glink", 0); + s->reachable = 1; + s->type = SELFRXSECT; + } /* hash */ s = linklookup(ctxt, ".hash", 0); @@ -1033,7 +1048,12 @@ doelf(void) s = linklookup(ctxt, ".plt", 0); s->reachable = 1; - s->type = SELFRXSECT; + if(thechar == '9') + // In the ppc64 ABI, .plt is a data section + // written by the dynamic linker. + s->type = SELFSECT; + else + s->type = SELFRXSECT; elfsetupplt(); @@ -1079,8 +1099,14 @@ doelf(void) } if(rpath) elfwritedynent(s, DT_RUNPATH, addstring(dynstr, rpath)); - - elfwritedynentsym(s, DT_PLTGOT, linklookup(ctxt, ".got.plt", 0)); + + if(thechar == '9') + elfwritedynentsym(s, DT_PLTGOT, linklookup(ctxt, ".plt", 0)); + else + elfwritedynentsym(s, DT_PLTGOT, linklookup(ctxt, ".got.plt", 0)); + + if(thechar == '9') + elfwritedynent(s, DT_PPC64_OPT, 0); // Solaris dynamic linker can't handle an empty .rela.plt if // DT_JMPREL is emitted so we have to defer generation of DT_PLTREL, @@ -1309,6 +1335,7 @@ asmbelf(vlong symo) switch(eh->machine) { case EM_X86_64: + case EM_PPC64: sh = elfshname(".rela.plt"); sh->type = SHT_RELA; sh->flags = SHF_ALLOC; @@ -1345,29 +1372,47 @@ asmbelf(vlong symo) break; } + if(eh->machine == EM_PPC64) { + sh = elfshname(".glink"); + sh->type = SHT_PROGBITS; + sh->flags = SHF_ALLOC+SHF_EXECINSTR; + sh->addralign = 4; + shsym(sh, linklookup(ctxt, ".glink", 0)); + } + sh = elfshname(".plt"); sh->type = SHT_PROGBITS; sh->flags = SHF_ALLOC+SHF_EXECINSTR; if(eh->machine == EM_X86_64) sh->entsize = 16; - else + else if(eh->machine == EM_PPC64) { + // On ppc64, this is just a table of addresses + // filled by the dynamic linker + sh->type = SHT_NOBITS; + sh->flags = SHF_ALLOC+SHF_WRITE; + sh->entsize = 8; + } else sh->entsize = 4; - sh->addralign = 4; + sh->addralign = sh->entsize; shsym(sh, linklookup(ctxt, ".plt", 0)); - sh = elfshname(".got"); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC+SHF_WRITE; - sh->entsize = RegSize; - sh->addralign = RegSize; - shsym(sh, linklookup(ctxt, ".got", 0)); + // On ppc64, .got comes from the input files, so don't + // create it here, and .got.plt is not used. + if(eh->machine != EM_PPC64) { + sh = elfshname(".got"); + sh->type = SHT_PROGBITS; + sh->flags = SHF_ALLOC+SHF_WRITE; + sh->entsize = RegSize; + sh->addralign = RegSize; + shsym(sh, linklookup(ctxt, ".got", 0)); - sh = elfshname(".got.plt"); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC+SHF_WRITE; - sh->entsize = RegSize; - sh->addralign = RegSize; - shsym(sh, linklookup(ctxt, ".got.plt", 0)); + sh = elfshname(".got.plt"); + sh->type = SHT_PROGBITS; + sh->flags = SHF_ALLOC+SHF_WRITE; + sh->entsize = RegSize; + sh->addralign = RegSize; + shsym(sh, linklookup(ctxt, ".got.plt", 0)); + } sh = elfshname(".hash"); sh->type = SHT_HASH; diff --git a/src/cmd/ld/elf.h b/src/cmd/ld/elf.h index e84d996f25..16c052fcb5 100644 --- a/src/cmd/ld/elf.h +++ b/src/cmd/ld/elf.h @@ -317,6 +317,9 @@ typedef struct { #define DT_VERNEEDNUM 0x6fffffff #define DT_VERSYM 0x6ffffff0 +#define DT_PPC64_GLINK (DT_LOPROC + 0) +#define DT_PPC64_OPT (DT_LOPROC + 3) + /* Values for DT_FLAGS */ /* Indicates that the object being loaded may make reference to the $ORIGIN substitution string */ @@ -700,6 +703,18 @@ typedef struct { /* Count of defined relocation types. */ #define R_PPC_EMB_COUNT (R_PPC_EMB_RELSDA - R_PPC_EMB_NADDR32 + 1) +#define R_PPC64_REL24 R_PPC_REL24 +#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT +#define R_PPC64_ADDR64 38 +#define R_PPC64_TOC16 47 +#define R_PPC64_TOC16_LO 48 +#define R_PPC64_TOC16_HI 49 +#define R_PPC64_TOC16_HA 50 +#define R_PPC64_TOC16_DS 63 +#define R_PPC64_TOC16_LO_DS 64 +#define R_PPC64_REL16_LO 250 +#define R_PPC64_REL16_HI 251 +#define R_PPC64_REL16_HA 252 #define R_SPARC_NONE 0 #define R_SPARC_8 1 @@ -970,6 +985,7 @@ uint32 elfwritephdrs(void); uint32 elfwriteshdrs(void); void elfwritedynent(LSym*, int, uint64); void elfwritedynentsym(LSym*, int, LSym*); +void elfwritedynentsymplus(LSym*, int, LSym*, vlong); void elfwritedynentsymsize(LSym*, int, LSym*); uint32 elfhash(uchar*); uint64 startelf(void); diff --git a/src/cmd/ld/ldelf.c b/src/cmd/ld/ldelf.c index f289fb38c7..29d32d283d 100644 --- a/src/cmd/ld/ldelf.c +++ b/src/cmd/ld/ldelf.c @@ -554,6 +554,9 @@ ldelf(Biobuf *f, char *pkg, int64 len, char *pn) s->type = STEXT; break; } + if(strcmp(sect->name, ".got") == 0 || + strcmp(sect->name, ".toc") == 0) + s->type = SELFGOT; if(sect->type == ElfSectProgbits) { s->p = sect->base; s->np = sect->size; @@ -812,6 +815,10 @@ readsym(ElfObj *obj, int i, ElfSym *sym, int needSym) s = nil; if(strcmp(sym->name, "_GLOBAL_OFFSET_TABLE_") == 0) sym->name = ".got"; + if(strcmp(sym->name, ".TOC.") == 0) + // Magic symbol on ppc64. Will be set to this object + // file's .got+0x8000. + sym->bind = ElfSymBindLocal; switch(sym->type) { case ElfSymTypeSection: s = obj->sect[sym->shndx].sym; @@ -842,6 +849,15 @@ readsym(ElfObj *obj, int i, ElfSym *sym, int needSym) // symbols, ignore these break; } + if(strcmp(sym->name, ".TOC.") == 0) { + // We need to be able to look this up, + // so put it in the hash table. + if(needSym) { + s = linklookup(ctxt, sym->name, ctxt->version); + s->type |= SHIDDEN; + } + break; + } if(needSym) { // local names and hidden visiblity global names are unique // and should only reference by its index, not name, so we @@ -892,6 +908,17 @@ reltype(char *pn, int elftype, uchar *siz) switch(R(thechar, elftype)) { default: diag("%s: unknown relocation type %d; compiled without -fpic?", pn, elftype); + case R('9', R_PPC64_TOC16): + case R('9', R_PPC64_TOC16_LO): + case R('9', R_PPC64_TOC16_HI): + case R('9', R_PPC64_TOC16_HA): + case R('9', R_PPC64_TOC16_DS): + case R('9', R_PPC64_TOC16_LO_DS): + case R('9', R_PPC64_REL16_LO): + case R('9', R_PPC64_REL16_HI): + case R('9', R_PPC64_REL16_HA): + *siz = 2; + break; case R('5', R_ARM_ABS32): case R('5', R_ARM_GOT32): case R('5', R_ARM_PLT32): @@ -913,9 +940,11 @@ reltype(char *pn, int elftype, uchar *siz) case R('8', R_386_PLT32): case R('8', R_386_GOTOFF): case R('8', R_386_GOTPC): + case R('9', R_PPC64_REL24): *siz = 4; break; case R('6', R_X86_64_64): + case R('9', R_PPC64_ADDR64): *siz = 8; break; }