]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/link/internal/ppc64: link ELFv2 objects built with -mcpu=power10
authorPaul E. Murphy <murp@ibm.com>
Mon, 1 May 2023 15:06:22 +0000 (10:06 -0500)
committerPaul Murphy <murp@ibm.com>
Tue, 16 May 2023 19:16:51 +0000 (19:16 +0000)
Specifically, objects built via cgo using CGO_CFLAGS="-O2 -g -mcpu=power10".

These use new relocations defined by ELFv2 1.5, and the R_PPC64_REL24_NOTOC
relocation. These objects contain functions which may not use a TOC
pointer requiring the insertion of trampolines to use correctly.

The relocation targets of these ELFv2 objects may also contain non-zero
values. Clear the relocated bits before setting them.

Extra care is taken if GOPPC64 < power10. The R_PPC64_REL24_NOTOC reloc
existed prior to ELFv2 1.5. The presence of this relocation itself does
not imply a power10 target. Generate power8 compatible stubs if
GOPPC64 < power10.

Updates #44549

Change-Id: I06ff8c4e47ed9af835a7dcfbafcfa4c538f75544
Reviewed-on: https://go-review.googlesource.com/c/go/+/492617
Run-TryBot: Paul Murphy <murp@ibm.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Lynn Boger <laboger@linux.vnet.ibm.com>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
src/cmd/link/internal/ppc64/asm.go

index d14e9322cf36da602cdcdde89991e1cb08061596..5f1419c08ea401e5cf247b83b2da5da9ec1255f6 100644 (file)
@@ -48,6 +48,118 @@ import (
 // The build configuration supports PC-relative instructions and relocations (limited to tested targets).
 var hasPCrel = buildcfg.GOPPC64 >= 10 && buildcfg.GOOS == "linux"
 
+const (
+       // For genstub, the type of stub required by the caller.
+       STUB_TOC = iota
+       STUB_PCREL
+)
+
+var stubStrs = []string{
+       STUB_TOC:   "_callstub_toc",
+       STUB_PCREL: "_callstub_pcrel",
+}
+
+const (
+       OP_TOCRESTORE    = 0xe8410018 // ld r2,24(r1)
+       OP_TOCSAVE       = 0xf8410018 // std r2,24(r1)
+       OP_NOP           = 0x60000000 // nop
+       OP_BL            = 0x48000001 // bl 0
+       OP_BCTR          = 0x4e800420 // bctr
+       OP_BCTRL         = 0x4e800421 // bctrl
+       OP_BCL           = 0x40000001 // bcl
+       OP_ADDI          = 0x38000000 // addi
+       OP_ADDIS         = 0x3c000000 // addis
+       OP_LD            = 0xe8000000 // ld
+       OP_PLA_PFX       = 0x06100000 // pla (prefix instruction word)
+       OP_PLA_SFX       = 0x38000000 // pla (suffix instruction word)
+       OP_PLD_PFX_PCREL = 0x04100000 // pld (prefix instruction word, R=1)
+       OP_PLD_SFX       = 0xe4000000 // pld (suffix instruction word)
+       OP_MFLR          = 0x7c0802a6 // mflr
+       OP_MTLR          = 0x7c0803a6 // mtlr
+       OP_MFCTR         = 0x7c0902a6 // mfctr
+       OP_MTCTR         = 0x7c0903a6 // mtctr
+
+       OP_ADDIS_R12_R2  = OP_ADDIS | 12<<21 | 2<<16  // addis r12,r2,0
+       OP_ADDIS_R12_R12 = OP_ADDIS | 12<<21 | 12<<16 // addis  r12,r12,0
+       OP_ADDI_R12_R12  = OP_ADDI | 12<<21 | 12<<16  // addi  r12,r12,0
+       OP_PLD_SFX_R12   = OP_PLD_SFX | 12<<21        // pld   r12,0 (suffix instruction word)
+       OP_PLA_SFX_R12   = OP_PLA_SFX | 12<<21        // pla   r12,0 (suffix instruction word)
+       OP_LIS_R12       = OP_ADDIS | 12<<21          // lis r12,0
+       OP_LD_R12_R12    = OP_LD | 12<<21 | 12<<16    // ld r12,0(r12)
+       OP_MTCTR_R12     = OP_MTCTR | 12<<21          // mtctr r12
+       OP_MFLR_R12      = OP_MFLR | 12<<21           // mflr r12
+       OP_MFLR_R0       = OP_MFLR | 0<<21            // mflr r0
+       OP_MTLR_R0       = OP_MTLR | 0<<21            // mtlr r0
+
+       // This is a special, preferred form of bcl to obtain the next
+       // instruction address (NIA, aka PC+4) in LR.
+       OP_BCL_NIA = OP_BCL | 20<<21 | 31<<16 | 1<<2 // bcl 20,31,$+4
+
+       // Masks to match opcodes
+       MASK_PLD_PFX  = 0xfff70000
+       MASK_PLD_SFX  = 0xfc1f0000 // Also checks RA = 0 if check value is OP_PLD_SFX.
+       MASK_PLD_RT   = 0x03e00000 // Extract RT from the pld suffix.
+       MASK_OP_LD    = 0xfc000003
+       MASK_OP_ADDIS = 0xfc000000
+)
+
+// Generate a stub to call between TOC and NOTOC functions. See genpltstub for more details about calling stubs.
+// This is almost identical to genpltstub, except the location of the target symbol is known at link time.
+func genstub(ctxt *ld.Link, ldr *loader.Loader, r loader.Reloc, ri int, s loader.Sym, stubType int) (ssym loader.Sym, firstUse bool) {
+       addendStr := ""
+       if r.Add() != 0 {
+               addendStr = fmt.Sprintf("%+d", r.Add())
+       }
+
+       stubName := fmt.Sprintf("%s%s.%s", stubStrs[stubType], addendStr, ldr.SymName(r.Sym()))
+       stub := ldr.CreateSymForUpdate(stubName, 0)
+       firstUse = stub.Size() == 0
+       if firstUse {
+               switch stubType {
+               // A call from a function using a TOC pointer.
+               case STUB_TOC:
+                       stub.AddUint32(ctxt.Arch, OP_TOCSAVE) // std r2,24(r1)
+                       stub.AddSymRef(ctxt.Arch, r.Sym(), r.Add(), objabi.R_ADDRPOWER_TOCREL_DS, 8)
+                       stub.SetUint32(ctxt.Arch, stub.Size()-8, OP_ADDIS_R12_R2) // addis r12,r2,targ@toc@ha
+                       stub.SetUint32(ctxt.Arch, stub.Size()-4, OP_ADDI_R12_R12) // addi  r12,targ@toc@l(r12)
+
+               // A call from PC relative function.
+               case STUB_PCREL:
+                       if buildcfg.GOPPC64 >= 10 {
+                               // Set up address of targ in r12, PCrel
+                               stub.AddSymRef(ctxt.Arch, r.Sym(), r.Add(), objabi.R_ADDRPOWER_PCREL34, 8)
+                               stub.SetUint32(ctxt.Arch, stub.Size()-8, OP_PLA_PFX)
+                               stub.SetUint32(ctxt.Arch, stub.Size()-4, OP_PLA_SFX_R12) // pla r12, r
+                       } else {
+                               // The target may not be a P10. Generate a P8 compatible stub.
+                               stub.AddUint32(ctxt.Arch, OP_MFLR_R0)  // mflr r0
+                               stub.AddUint32(ctxt.Arch, OP_BCL_NIA)  // bcl 20,31,1f
+                               stub.AddUint32(ctxt.Arch, OP_MFLR_R12) // 1: mflr r12  (r12 is the address of this instruction)
+                               stub.AddUint32(ctxt.Arch, OP_MTLR_R0)  // mtlr r0
+                               stub.AddSymRef(ctxt.Arch, r.Sym(), r.Add()+8, objabi.R_ADDRPOWER_PCREL, 8)
+                               stub.SetUint32(ctxt.Arch, stub.Size()-8, OP_ADDIS_R12_R12) // addis r12,(r - 1b) + 8
+                               stub.SetUint32(ctxt.Arch, stub.Size()-4, OP_ADDI_R12_R12)  // addi  r12,(r - 1b) + 12
+                       }
+               }
+               // Jump to the loaded pointer
+               stub.AddUint32(ctxt.Arch, OP_MTCTR_R12) // mtctr r12
+               stub.AddUint32(ctxt.Arch, OP_BCTR)      // bctr
+               stub.SetType(sym.STEXT)
+       }
+
+       // Update the relocation to use the call stub
+       su := ldr.MakeSymbolUpdater(s)
+       su.SetRelocSym(ri, stub.Sym())
+
+       // Rewrite the TOC restore slot (a nop) if the caller uses a TOC pointer.
+       switch stubType {
+       case STUB_TOC:
+               rewritetoinsn(&ctxt.Target, ldr, su, int64(r.Off()+4), 0xFFFFFFFF, OP_NOP, OP_TOCRESTORE)
+       }
+
+       return stub.Sym(), firstUse
+}
+
 func genpltstub(ctxt *ld.Link, ldr *loader.Loader, r loader.Reloc, ri int, s loader.Sym) (sym loader.Sym, firstUse bool) {
        // The ppc64 ABI PLT has similar concepts to other
        // architectures, but is laid out quite differently. When we
@@ -104,18 +216,19 @@ func genpltstub(ctxt *ld.Link, ldr *loader.Loader, r loader.Reloc, ri int, s loa
        // the caller maintains a TOC pointer in R2. A TOC pointer implies
        // we can always generate a position independent stub.
        //
-       // For dynamic calls made from an external object, it is safe to
-       // assume a TOC pointer is maintained. These were imported from
-       // a R_PPC64_REL24 relocation.
+       // For dynamic calls made from an external object, a caller maintains
+       // a TOC pointer only when an R_PPC64_REL24 relocation is used.
+       // An R_PPC64_REL24_NOTOC relocation does not use or maintain
+       // a TOC pointer, and almost always implies a Power10 target.
        //
        // For dynamic calls made from a Go object, the shared attribute
        // indicates a PIC symbol, which requires a TOC pointer be
        // maintained. Otherwise, a simpler non-PIC stub suffices.
-       if ldr.AttrExternal(s) || ldr.AttrShared(s) {
+       if (r.Type() == objabi.ElfRelocOffset+objabi.RelocType(elf.R_PPC64_REL24)) || (!ldr.AttrExternal(s) && ldr.AttrShared(s)) {
                stubTypeStr = "_tocrel"
                stubType = 1
        } else {
-               stubTypeStr = "_nopic"
+               stubTypeStr = "_notoc"
                stubType = 3
        }
        n := fmt.Sprintf("_pltstub%s.%s", stubTypeStr, ldr.SymName(r.Sym()))
@@ -141,11 +254,10 @@ func genpltstub(ctxt *ld.Link, ldr *loader.Loader, r loader.Reloc, ri int, s loa
                if len(p) >= int(r.Off()+8) {
                        nop = ctxt.Arch.ByteOrder.Uint32(p[r.Off()+4:])
                }
-               if nop != 0x60000000 {
+               if nop != OP_NOP {
                        ldr.Errorf(s, "Symbol %s is missing toc restoration slot at offset %d", ldr.SymName(s), r.Off()+4)
                }
-               const o1 = 0xe8410018 // ld r2,24(r1)
-               ctxt.Arch.ByteOrder.PutUint32(p[r.Off()+4:], o1)
+               ctxt.Arch.ByteOrder.PutUint32(p[r.Off()+4:], OP_TOCRESTORE)
        }
 
        return stub.Sym(), firstUse
@@ -178,6 +290,42 @@ func genstubs(ctxt *ld.Link, ldr *loader.Loader) {
                                                        abifuncs = append(abifuncs, sym)
                                                }
                                        }
+                               case sym.STEXT:
+                                       targ := r.Sym()
+                                       if (ldr.AttrExternal(targ) && ldr.SymLocalentry(targ) != 1) || !ldr.AttrExternal(targ) {
+                                               // All local symbols share the same TOC pointer. This caller has a valid TOC
+                                               // pointer in R2. Calls into a Go symbol preserve R2. No call stub is needed.
+                                       } else {
+                                               // This caller has a TOC pointer. The callee might clobber it. R2 needs to be saved
+                                               // and restored.
+                                               if sym, firstUse := genstub(ctxt, ldr, r, i, s, STUB_TOC); firstUse {
+                                                       stubs = append(stubs, sym)
+                                               }
+                                       }
+                               }
+
+                       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_REL24_NOTOC):
+                               switch ldr.SymType(r.Sym()) {
+                               case sym.SDYNIMPORT:
+                                       // This call goes through the PLT, generate and call through a PLT stub.
+                                       if sym, firstUse := genpltstub(ctxt, ldr, r, i, s); firstUse {
+                                               stubs = append(stubs, sym)
+                                       }
+
+                               case sym.SXREF:
+                                       // TODO: This is not supported yet.
+                                       ldr.Errorf(s, "Unsupported NOTOC external reference call into %s", ldr.SymName(r.Sym()))
+
+                               case sym.STEXT:
+                                       targ := r.Sym()
+                                       if (ldr.AttrExternal(targ) && ldr.SymLocalentry(targ) <= 1) || (!ldr.AttrExternal(targ) && !ldr.AttrShared(targ)) {
+                                               // This is NOTOC to NOTOC call (st_other is 0 or 1). No call stub is needed.
+                                       } else {
+                                               // This is a NOTOC to TOC function. Generate a calling stub.
+                                               if sym, firstUse := genstub(ctxt, ldr, r, i, s, STUB_PCREL); firstUse {
+                                                       stubs = append(stubs, sym)
+                                               }
+                                       }
                                }
 
                        // Handle objects compiled with -fno-plt. Rewrite local calls to avoid indirect calling.
@@ -186,17 +334,12 @@ func genstubs(ctxt *ld.Link, ldr *loader.Loader) {
                                if ldr.SymType(r.Sym()) == sym.STEXT {
                                        // This should be an mtctr instruction. Turn it into a nop.
                                        su := ldr.MakeSymbolUpdater(s)
-                                       const OP_MTCTR = 31<<26 | 0x9<<16 | 467<<1
                                        const MASK_OP_MTCTR = 63<<26 | 0x3FF<<11 | 0x1FF<<1
                                        rewritetonop(&ctxt.Target, ldr, su, int64(r.Off()), MASK_OP_MTCTR, OP_MTCTR)
                                }
                        case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_PLTCALL):
                                if ldr.SymType(r.Sym()) == sym.STEXT {
                                        // This relocation should point to a bctrl followed by a ld r2, 24(41)
-                                       const OP_BL = 0x48000001         // bl 0
-                                       const OP_TOCRESTORE = 0xe8410018 // ld r2,24(r1)
-                                       const OP_BCTRL = 0x4e800421      // bctrl
-
                                        // Convert the bctrl into a bl.
                                        su := ldr.MakeSymbolUpdater(s)
                                        rewritetoinsn(&ctxt.Target, ldr, su, int64(r.Off()), 0xFFFFFFFF, OP_BCTRL, OP_BL)
@@ -250,8 +393,8 @@ func genaddmoduledata(ctxt *ld.Link, ldr *loader.Loader) {
        initfunc.SetUint32(ctxt.Arch, sz-4, 0x38420000) // addi r2, r2, .TOC.-func@l
 
        // This is Go ABI. Stack a frame and save LR.
-       o(0x7c0802a6) // mflr r31
-       o(0xf801ffe1) // stdu r31, -32(r1)
+       o(OP_MFLR_R0) // mflr r0
+       o(0xf801ffe1) // stdu r0, -32(r1)
 
        // Get the moduledata pointer from GOT and put into R3.
        var tgt loader.Sym
@@ -271,18 +414,18 @@ func genaddmoduledata(ctxt *ld.Link, ldr *loader.Loader) {
                sz = initfunc.AddSymRef(ctxt.Arch, tgt, 0, objabi.R_ADDRPOWER_GOT_PCREL34, 8)
                // Note, this is prefixed instruction. It must not cross a 64B boundary.
                // It is doubleworld aligned here, so it will never cross (this function is 16B aligned, minimum).
-               initfunc.SetUint32(ctxt.Arch, sz-8, 0x04100000)
-               initfunc.SetUint32(ctxt.Arch, sz-4, 0xe4600000) // pld r3, local.moduledata@got@pcrel
+               initfunc.SetUint32(ctxt.Arch, sz-8, OP_PLD_PFX_PCREL)
+               initfunc.SetUint32(ctxt.Arch, sz-4, OP_PLD_SFX|(3<<21)) // pld r3, local.moduledata@got@pcrel
        }
 
        // Call runtime.addmoduledata
        sz = initfunc.AddSymRef(ctxt.Arch, addmoduledata, 0, objabi.R_CALLPOWER, 4)
-       initfunc.SetUint32(ctxt.Arch, sz-4, 0x48000001) // bl runtime.addmoduledata
-       o(0x60000000)                                   // nop (for TOC restore)
+       initfunc.SetUint32(ctxt.Arch, sz-4, OP_BL) // bl runtime.addmoduledata
+       o(OP_NOP)                                  // nop (for TOC restore)
 
        // Pop stack frame and return.
-       o(0xe8010000) // ld r31, 0(r1)
-       o(0x7c0803a6) // mtlr r31
+       o(0xe8010000) // ld r0, 0(r1)
+       o(OP_MTLR_R0) // mtlr r0
        o(0x38210020) // addi r1,r1,32
        o(0x4e800020) // blr
 }
@@ -378,22 +521,42 @@ func gencallstub(ctxt *ld.Link, ldr *loader.Loader, stubType int, stub *loader.S
        switch stubType {
        case 1:
                // Save TOC, then load targ address from PLT using TOC.
-               stub.AddUint32(ctxt.Arch, 0xf8410018) // std r2,24(r1)
+               stub.AddUint32(ctxt.Arch, OP_TOCSAVE) // std r2,24(r1)
                stub.AddSymRef(ctxt.Arch, plt, int64(ldr.SymPlt(targ)), objabi.R_ADDRPOWER_TOCREL_DS, 8)
-               stub.SetUint32(ctxt.Arch, stub.Size()-8, 0x3d820000) // addis r12,r2,targ@plt@toc@ha
-               stub.SetUint32(ctxt.Arch, stub.Size()-4, 0xe98c0000) // ld r12,targ@plt@toc@l(r12)
+               stub.SetUint32(ctxt.Arch, stub.Size()-8, OP_ADDIS_R12_R2) // addis r12,r2,targ@plt@toc@ha
+               stub.SetUint32(ctxt.Arch, stub.Size()-4, OP_LD_R12_R12)   // ld r12,targ@plt@toc@l(r12)
        case 3:
-               // Load targ address from PLT. This is position dependent.
-               stub.AddSymRef(ctxt.Arch, plt, int64(ldr.SymPlt(targ)), objabi.R_ADDRPOWER_DS, 8)
-               stub.SetUint32(ctxt.Arch, stub.Size()-8, 0x3d800000) // lis r12,targ@plt@ha
-               stub.SetUint32(ctxt.Arch, stub.Size()-4, 0xe98c0000) // ld r12,targ@plt@l(r12)
+               // No TOC needs to be saved, but the stub may need to position-independent.
+               if buildcfg.GOPPC64 >= 10 {
+                       // Power10 is supported, load targ address into r12 using PCrel load.
+                       stub.AddSymRef(ctxt.Arch, plt, int64(ldr.SymPlt(targ)), objabi.R_ADDRPOWER_PCREL34, 8)
+                       stub.SetUint32(ctxt.Arch, stub.Size()-8, OP_PLD_PFX_PCREL)
+                       stub.SetUint32(ctxt.Arch, stub.Size()-4, OP_PLD_SFX_R12) // pld r12, targ@plt
+               } else if !isLinkingPIC(ctxt) {
+                       // This stub doesn't need to be PIC. Load targ address from the PLT via its absolute address.
+                       stub.AddSymRef(ctxt.Arch, plt, int64(ldr.SymPlt(targ)), objabi.R_ADDRPOWER_DS, 8)
+                       stub.SetUint32(ctxt.Arch, stub.Size()-8, OP_LIS_R12)    // lis r12,targ@plt@ha
+                       stub.SetUint32(ctxt.Arch, stub.Size()-4, OP_LD_R12_R12) // ld r12,targ@plt@l(r12)
+               } else {
+                       // Generate a PIC stub. This is ugly as the stub must determine its location using
+                       // POWER8 or older instruction. These stubs are likely the combination of using
+                       // GOPPC64 < 8 and linking external objects built with CFLAGS="... -mcpu=power10 ..."
+                       stub.AddUint32(ctxt.Arch, OP_MFLR_R0)  // mflr r0
+                       stub.AddUint32(ctxt.Arch, OP_BCL_NIA)  // bcl 20,31,1f
+                       stub.AddUint32(ctxt.Arch, OP_MFLR_R12) // 1: mflr r12  (r12 is the address of this instruction)
+                       stub.AddUint32(ctxt.Arch, OP_MTLR_R0)  // mtlr r0
+                       stub.AddSymRef(ctxt.Arch, plt, int64(ldr.SymPlt(targ))+8, objabi.R_ADDRPOWER_PCREL, 8)
+                       stub.SetUint32(ctxt.Arch, stub.Size()-8, OP_ADDIS_R12_R12) // addis r12,(targ@plt - 1b) + 8
+                       stub.SetUint32(ctxt.Arch, stub.Size()-4, OP_ADDI_R12_R12)  // addi  r12,(targ@plt - 1b) + 12
+                       stub.AddUint32(ctxt.Arch, OP_LD_R12_R12)                   // ld r12, 0(r12)
+               }
        default:
                log.Fatalf("gencallstub does not support ELFv2 ABI property %d", stubType)
        }
 
        // Jump to the loaded pointer
-       stub.AddUint32(ctxt.Arch, 0x7d8903a6) // mtctr r12
-       stub.AddUint32(ctxt.Arch, 0x4e800420) // bctr
+       stub.AddUint32(ctxt.Arch, OP_MTCTR_R12) // mtctr r12
+       stub.AddUint32(ctxt.Arch, OP_BCTR)      // bctr
 }
 
 // Rewrite the instruction at offset into newinsn. Also, verify the
@@ -410,8 +573,7 @@ func rewritetoinsn(target *ld.Target, ldr *loader.Loader, su *loader.SymbolBuild
 // Rewrite the instruction at offset into a hardware nop instruction. Also, verify the
 // existing instruction under mask matches the check value.
 func rewritetonop(target *ld.Target, ldr *loader.Loader, su *loader.SymbolBuilder, offset int64, mask, check uint32) {
-       const NOP = 0x60000000
-       rewritetoinsn(target, ldr, su, offset, mask, check, NOP)
+       rewritetoinsn(target, ldr, su, offset, mask, check, OP_NOP)
 }
 
 func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool {
@@ -438,6 +600,16 @@ func addelfdynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s lo
                }
 
                // Handle relocations found in ELF object files.
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_REL24_NOTOC):
+               su := ldr.MakeSymbolUpdater(s)
+               su.SetRelocType(rIdx, objabi.R_CALLPOWER)
+
+               if targType == sym.SDYNIMPORT {
+                       // Should have been handled in elfsetupplt
+                       ldr.Errorf(s, "unexpected R_PPC64_REL24_NOTOC for dyn import")
+               }
+               return true
+
        case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_REL24):
                su := ldr.MakeSymbolUpdater(s)
                su.SetRelocType(rIdx, objabi.R_CALLPOWER)
@@ -460,6 +632,26 @@ func addelfdynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s lo
 
                return true
 
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_PCREL34):
+               su := ldr.MakeSymbolUpdater(s)
+               su.SetRelocType(rIdx, objabi.R_ADDRPOWER_PCREL34)
+               return true
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_GOT_PCREL34):
+               su := ldr.MakeSymbolUpdater(s)
+               su.SetRelocType(rIdx, objabi.R_ADDRPOWER_PCREL34)
+               if targType != sym.STEXT {
+                       ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_PPC64_GLOB_DAT))
+                       su.SetRelocSym(rIdx, syms.GOT)
+                       su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ)))
+               } else {
+                       // The address of targ is known at link time. Rewrite to "pla rt,targ" from "pld rt,targ@got"
+                       rewritetoinsn(target, ldr, su, int64(r.Off()), MASK_PLD_PFX, OP_PLD_PFX_PCREL, OP_PLA_PFX)
+                       pla_sfx := target.Arch.ByteOrder.Uint32(su.Data()[r.Off()+4:])&MASK_PLD_RT | OP_PLA_SFX
+                       rewritetoinsn(target, ldr, su, int64(r.Off()+4), MASK_PLD_SFX, OP_PLD_SFX, pla_sfx)
+               }
+               return true
+
        case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC_REL32):
                su := ldr.MakeSymbolUpdater(s)
                su.SetRelocType(rIdx, objabi.R_PCREL)
@@ -577,13 +769,9 @@ func addelfdynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s lo
                } else if targType == sym.STEXT {
                        if isPLT16_LO_DS {
                                // Expect an ld opcode to nop
-                               const MASK_OP_LD = 63<<26 | 0x3
-                               const OP_LD = 58 << 26
                                rewritetonop(target, ldr, su, int64(r.Off()), MASK_OP_LD, OP_LD)
                        } else {
                                // Expect an addis opcode to nop
-                               const MASK_OP_ADDIS = 63 << 26
-                               const OP_ADDIS = 15 << 26
                                rewritetonop(target, ldr, su, int64(r.Off()), MASK_OP_ADDIS, OP_ADDIS)
                        }
                        // And we can ignore this reloc now.
@@ -933,19 +1121,29 @@ func archrelocaddr(ldr *loader.Loader, target *ld.Target, syms *ld.ArchSyms, r l
                ldr.Errorf(s, "relocation for %s is too big (>=2G): 0x%x", ldr.SymName(s), ldr.SymValue(rs))
        }
 
+       // Note, relocations imported from external objects may not have cleared bits
+       // within a relocatable field. They need cleared before applying the relocation.
        switch r.Type() {
        case objabi.R_ADDRPOWER_PCREL34:
                // S + A - P
                t -= (ldr.SymValue(s) + int64(r.Off()))
+               o1 &^= 0x3ffff
+               o2 &^= 0x0ffff
                o1 |= computePrefix34HI(t)
                o2 |= computeLO(int32(t))
        case objabi.R_ADDRPOWER_D34:
+               o1 &^= 0x3ffff
+               o2 &^= 0x0ffff
                o1 |= computePrefix34HI(t)
                o2 |= computeLO(int32(t))
        case objabi.R_ADDRPOWER:
+               o1 &^= 0xffff
+               o2 &^= 0xffff
                o1 |= computeHA(int32(t))
                o2 |= computeLO(int32(t))
        case objabi.R_ADDRPOWER_DS:
+               o1 &^= 0xffff
+               o2 &^= 0xfffc
                o1 |= computeHA(int32(t))
                o2 |= computeLO(int32(t))
                if t&3 != 0 {
@@ -960,6 +1158,11 @@ func archrelocaddr(ldr *loader.Loader, target *ld.Target, syms *ld.ArchSyms, r l
 
 // Determine if the code was compiled so that the TOC register R2 is initialized and maintained.
 func r2Valid(ctxt *ld.Link) bool {
+       return isLinkingPIC(ctxt)
+}
+
+// Determine if this is linking a position-independent binary.
+func isLinkingPIC(ctxt *ld.Link) bool {
        switch ctxt.BuildMode {
        case ld.BuildModeCArchive, ld.BuildModeCShared, ld.BuildModePIE, ld.BuildModeShared, ld.BuildModePlugin:
                return true
@@ -1063,8 +1266,8 @@ func gentramp(ctxt *ld.Link, ldr *loader.Loader, tramp *loader.SymbolBuilder, ta
                // However, all text symbols are accessed with a TOC symbol as
                // text relocations aren't supposed to be possible.
                // So, keep using the external linking way to be more AIX friendly.
-               o1 = uint32(0x3c000000) | 12<<21 | 2<<16  // addis r12,  r2, toctargetaddr hi
-               o2 = uint32(0xe8000000) | 12<<21 | 12<<16 // ld    r12, r12, toctargetaddr lo
+               o1 = uint32(OP_ADDIS_R12_R2) // addis r12,  r2, toctargetaddr hi
+               o2 = uint32(OP_LD_R12_R12)   // ld    r12, r12, toctargetaddr lo
 
                toctramp := ldr.CreateSymForUpdate("TOC."+ldr.SymName(tramp.Sym()), 0)
                toctramp.SetType(sym.SXCOFFTOC)
@@ -1076,8 +1279,8 @@ func gentramp(ctxt *ld.Link, ldr *loader.Loader, tramp *loader.SymbolBuilder, ta
                r.SetSym(toctramp.Sym())
        } else if hasPCrel {
                // pla r12, addr (PCrel). This works for static or PIC, with or without a valid TOC pointer.
-               o1 = uint32(0x06100000)
-               o2 = uint32(0x39800000) // pla r12, addr
+               o1 = uint32(OP_PLA_PFX)
+               o2 = uint32(OP_PLA_SFX_R12) // pla r12, addr
 
                // The trampoline's position is not known yet, insert a relocation.
                r, _ := tramp.AddRel(objabi.R_ADDRPOWER_PCREL34)
@@ -1089,8 +1292,8 @@ func gentramp(ctxt *ld.Link, ldr *loader.Loader, tramp *loader.SymbolBuilder, ta
                // Used for default build mode for an executable
                // Address of the call target is generated using
                // relocation and doesn't depend on r2 (TOC).
-               o1 = uint32(0x3c000000) | 12<<21          // lis  r12,targetaddr hi
-               o2 = uint32(0x38000000) | 12<<21 | 12<<16 // addi r12,r12,targetaddr lo
+               o1 = uint32(OP_LIS_R12)      // lis  r12,targetaddr hi
+               o2 = uint32(OP_ADDI_R12_R12) // addi r12,r12,targetaddr lo
 
                t := ldr.SymValue(target)
                if t == 0 || r2Valid(ctxt) || ctxt.IsExternal() {
@@ -1113,8 +1316,8 @@ func gentramp(ctxt *ld.Link, ldr *loader.Loader, tramp *loader.SymbolBuilder, ta
                }
        }
 
-       o3 := uint32(0x7c0903a6) | 12<<21 // mtctr r12
-       o4 := uint32(0x4e800420)          // bctr
+       o3 := uint32(OP_MTCTR_R12) // mtctr r12
+       o4 := uint32(OP_BCTR)      // bctr
        ctxt.Arch.ByteOrder.PutUint32(P, o1)
        ctxt.Arch.ByteOrder.PutUint32(P[4:], o2)
        ctxt.Arch.ByteOrder.PutUint32(P[8:], o3)
@@ -1201,7 +1404,7 @@ func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loade
                                        ldr.Errorf(s, "relocation for %s+%d is not an addis/addi pair: %16x", ldr.SymName(rs), r.Off(), uint64(val))
                                }
                                nval := (int64(uint32(0x380d0000)) | val&0x03e00000) << 32 // addi rX, r13, $0
-                               nval |= int64(0x60000000)                                  // nop
+                               nval |= int64(OP_NOP)                                      // nop
                                val = nval
                                nExtReloc = 1
                        } else {
@@ -1294,7 +1497,6 @@ func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loade
                // addis to, r0, x@tprel@ha
                // addi to, to, x@tprel@l(to)
 
-               const OP_ADDI = 14 << 26
                const OP_MASK = 0x3F << 26
                const OP_RA_MASK = 0x1F << 16
                // convert r2 to r0, and ld to addi
@@ -1515,10 +1717,10 @@ func ensureglinkresolver(ctxt *ld.Link, ldr *loader.Loader) *loader.SymbolBuilde
        // resolver is known.
        //
        // This stub is PIC, so first get the PC of label 1 into r11.
-       glink.AddUint32(ctxt.Arch, 0x7c0802a6) // mflr r0
-       glink.AddUint32(ctxt.Arch, 0x429f0005) // bcl 20,31,1f
+       glink.AddUint32(ctxt.Arch, OP_MFLR_R0) // mflr r0
+       glink.AddUint32(ctxt.Arch, OP_BCL_NIA) // bcl 20,31,1f
        glink.AddUint32(ctxt.Arch, 0x7d6802a6) // 1: mflr r11
-       glink.AddUint32(ctxt.Arch, 0x7c0803a6) // mtlr r0
+       glink.AddUint32(ctxt.Arch, OP_MTLR_R0) // mtlr r0
 
        // Compute the .plt array index from the entry point address
        // into r0. This is computed relative to label 1 above.
@@ -1538,8 +1740,8 @@ func ensureglinkresolver(ctxt *ld.Link, ldr *loader.Loader) *loader.SymbolBuilde
        glink.AddUint32(ctxt.Arch, 0xe96b0008) // ld r11,8(r11)
 
        // Jump to the dynamic resolver
-       glink.AddUint32(ctxt.Arch, 0x7d8903a6) // mtctr r12
-       glink.AddUint32(ctxt.Arch, 0x4e800420) // bctr
+       glink.AddUint32(ctxt.Arch, OP_MTCTR_R12) // mtctr r12
+       glink.AddUint32(ctxt.Arch, OP_BCTR)      // bctr
 
        // Store the PC-rel offset to the PLT
        r, _ := glink.AddRel(objabi.R_PCREL)