]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/9l: support internal linking
authorAustin Clements <austin@google.com>
Tue, 16 Dec 2014 19:59:59 +0000 (14:59 -0500)
committerAustin Clements <austin@google.com>
Wed, 7 Jan 2015 20:35:54 +0000 (20:35 +0000)
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 <rsc@golang.org>
include/link.h
src/cmd/9l/asm.c
src/cmd/9l/obj.c
src/cmd/ld/data.c
src/cmd/ld/elf.c
src/cmd/ld/elf.h
src/cmd/ld/ldelf.c

index bc163d6e4be674089de73d9d74ae6516d8076d73..15a28787928d68a16ea04b0cc92b6c4c24e35620 100644 (file)
@@ -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
index 00651d5714edbaa44cca6b02d03bba550acb26c9..391f9562cf570a71dc1feeb5821842a32c8bd125 100644 (file)
@@ -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; r<s->r+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
index dab1ffb3154a61beaf527a887b9a84ae6291922a..77f665e5a2d4bc3e65dde21bcdfb0ab7531a6c5e 100644 (file)
@@ -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)
index 48e8a5886637d7dcaea9056c6bafdca86b762f33..22843b894834a46d9d90572d6fdaffe48a623eed 100644 (file)
@@ -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);
index b17d66890f2ecb4e832d835f01fc36cd675688d2..89a0a5e87f893a6eac7bfefb18db09e423ad276a 100644 (file)
@@ -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;
index e84d996f259671e0826ef671a719f1e023b1b72d..16c052fcb5c39f27c9934e0df6aaec7dc5b07c15 100644 (file)
@@ -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);
index f289fb38c7d591f0a322886a530a7bc5c69c69d0..29d32d283da61772cebebcc845c45dd069f85be5 100644 (file)
@@ -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;
        }