]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/link: support PPC64 prefixed relocations for power10
authorPaul E. Murphy <murp@ibm.com>
Mon, 12 Jul 2021 21:53:22 +0000 (16:53 -0500)
committerPaul Murphy <murp@ibm.com>
Thu, 3 Nov 2022 21:13:39 +0000 (21:13 +0000)
Handle emitting (to ld) or resolving commonly used ELFv2 1.5
relocations. The new ISA provides PC relative addressing with
34 bit signed addresses, and many other relocations which can
replace addis + d-form type relocations with a single prefixed
instruction.

Updates #44549

Change-Id: I7d4f4314d1082daa3938f4353826739be35b0e81
Reviewed-on: https://go-review.googlesource.com/c/go/+/355149
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: Lynn Boger <laboger@linux.vnet.ibm.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Paul Murphy <murp@ibm.com>

src/cmd/internal/objabi/reloctype.go
src/cmd/internal/objabi/reloctype_string.go
src/cmd/link/internal/ppc64/asm.go

index e5d1d5f9e045f5247bc2b62b6f232556ba11c5ea..590eedb025c554a7720a7164a1375d482d1bc2b0 100644 (file)
@@ -208,6 +208,15 @@ const (
        // (usually called RB in X-form instructions) is assumed to be R13.
        R_POWER_TLS
 
+       // R_POWER_TLS_IE_PCREL34 is similar to R_POWER_TLS_IE, but marks a single MOVD
+       // which has been assembled as a single prefixed load doubleword without using the
+       // TOC.
+       R_POWER_TLS_IE_PCREL34
+
+       // R_POWER_TLS_LE_TPREL34 is similar to R_POWER_TLS_LE, but computes an offset from
+       // the thread pointer in one prefixed instruction.
+       R_POWER_TLS_LE_TPREL34
+
        // R_ADDRPOWER_DS is similar to R_ADDRPOWER above, but assumes the second
        // instruction is a "DS-form" instruction, which has an immediate field occupying
        // bits [15:2] of the instruction word. Bits [15:2] of the address of the
@@ -215,11 +224,14 @@ const (
        // bits of the address are not 0.
        R_ADDRPOWER_DS
 
-       // R_ADDRPOWER_PCREL relocates a D-form, DS-form instruction sequence like
-       // R_ADDRPOWER_DS but inserts the offset of the GOT slot for the referenced symbol
-       // from the TOC rather than the symbol's address.
+       // R_ADDRPOWER_GOT relocates a D-form + DS-form instruction sequence by inserting
+       // a relative displacement of referenced symbol's GOT entry to the TOC pointer.
        R_ADDRPOWER_GOT
 
+       // R_ADDRPOWER_GOT_PCREL34 is identical to R_ADDRPOWER_GOT, but uses a PC relative
+       // sequence to generate a GOT symbol addresss.
+       R_ADDRPOWER_GOT_PCREL34
+
        // R_ADDRPOWER_PCREL relocates two D-form instructions like R_ADDRPOWER, but
        // inserts the displacement from the place being relocated to the address of the
        // relocated symbol instead of just its address.
@@ -235,6 +247,16 @@ const (
        // relocated symbol rather than the symbol's address.
        R_ADDRPOWER_TOCREL_DS
 
+       // R_ADDRPOWER_D34 relocates a single prefixed D-form load/store operation.  All
+       // prefixed forms are D form. The high 18 bits are stored in the prefix,
+       // and the low 16 are stored in the suffix. The address is absolute.
+       R_ADDRPOWER_D34
+
+       // R_ADDPOWER_PCREL34 relates a single prefixed D-form load/store/add operation.
+       // All prefixed forms are D form. The resulting address is relative to the
+       // PC. It is a signed 34 bit offset.
+       R_ADDRPOWER_PCREL34
+
        // RISC-V.
 
        // R_RISCV_CALL relocates a J-type instruction with a 21 bit PC-relative
index ccc755e4bba553aad5e9f911ee647b3d3074a246..9ce37d00dea942245a338da59ab36bfadf39aa41 100644 (file)
@@ -57,34 +57,39 @@ func _() {
        _ = x[R_POWER_TLS_LE-47]
        _ = x[R_POWER_TLS_IE-48]
        _ = x[R_POWER_TLS-49]
-       _ = x[R_ADDRPOWER_DS-50]
-       _ = x[R_ADDRPOWER_GOT-51]
-       _ = x[R_ADDRPOWER_PCREL-52]
-       _ = x[R_ADDRPOWER_TOCREL-53]
-       _ = x[R_ADDRPOWER_TOCREL_DS-54]
-       _ = x[R_RISCV_CALL-55]
-       _ = x[R_RISCV_CALL_TRAMP-56]
-       _ = x[R_RISCV_PCREL_ITYPE-57]
-       _ = x[R_RISCV_PCREL_STYPE-58]
-       _ = x[R_RISCV_TLS_IE_ITYPE-59]
-       _ = x[R_RISCV_TLS_IE_STYPE-60]
-       _ = x[R_PCRELDBL-61]
-       _ = x[R_ADDRLOONG64-62]
-       _ = x[R_ADDRLOONG64U-63]
-       _ = x[R_ADDRLOONG64TLS-64]
-       _ = x[R_ADDRLOONG64TLSU-65]
-       _ = x[R_CALLLOONG64-66]
-       _ = x[R_JMPLOONG64-67]
-       _ = x[R_ADDRMIPSU-68]
-       _ = x[R_ADDRMIPSTLS-69]
-       _ = x[R_ADDRCUOFF-70]
-       _ = x[R_WASMIMPORT-71]
-       _ = x[R_XCOFFREF-72]
+       _ = x[R_POWER_TLS_IE_PCREL34-50]
+       _ = x[R_POWER_TLS_LE_TPREL34-51]
+       _ = x[R_ADDRPOWER_DS-52]
+       _ = x[R_ADDRPOWER_GOT-53]
+       _ = x[R_ADDRPOWER_GOT_PCREL34-54]
+       _ = x[R_ADDRPOWER_PCREL-55]
+       _ = x[R_ADDRPOWER_TOCREL-56]
+       _ = x[R_ADDRPOWER_TOCREL_DS-57]
+       _ = x[R_ADDRPOWER_D34-58]
+       _ = x[R_ADDRPOWER_PCREL34-59]
+       _ = x[R_RISCV_CALL-60]
+       _ = x[R_RISCV_CALL_TRAMP-61]
+       _ = x[R_RISCV_PCREL_ITYPE-62]
+       _ = x[R_RISCV_PCREL_STYPE-63]
+       _ = x[R_RISCV_TLS_IE_ITYPE-64]
+       _ = x[R_RISCV_TLS_IE_STYPE-65]
+       _ = x[R_PCRELDBL-66]
+       _ = x[R_ADDRLOONG64-67]
+       _ = x[R_ADDRLOONG64U-68]
+       _ = x[R_ADDRLOONG64TLS-69]
+       _ = x[R_ADDRLOONG64TLSU-70]
+       _ = x[R_CALLLOONG64-71]
+       _ = x[R_JMPLOONG64-72]
+       _ = x[R_ADDRMIPSU-73]
+       _ = x[R_ADDRMIPSTLS-74]
+       _ = x[R_ADDRCUOFF-75]
+       _ = x[R_WASMIMPORT-76]
+       _ = x[R_XCOFFREF-77]
 }
 
-const _RelocType_name = "R_ADDRR_ADDRPOWERR_ADDRARM64R_ADDRMIPSR_ADDROFFR_SIZER_CALLR_CALLARMR_CALLARM64R_CALLINDR_CALLPOWERR_CALLMIPSR_CONSTR_PCRELR_TLS_LER_TLS_IER_GOTOFFR_PLT0R_PLT1R_PLT2R_USEFIELDR_USETYPER_USEIFACER_USEIFACEMETHODR_USEGENERICIFACEMETHODR_METHODOFFR_KEEPR_POWER_TOCR_GOTPCRELR_JMPMIPSR_DWARFSECREFR_DWARFFILEREFR_ARM64_TLS_LER_ARM64_TLS_IER_ARM64_GOTPCRELR_ARM64_GOTR_ARM64_PCRELR_ARM64_PCREL_LDST8R_ARM64_PCREL_LDST16R_ARM64_PCREL_LDST32R_ARM64_PCREL_LDST64R_ARM64_LDST8R_ARM64_LDST16R_ARM64_LDST32R_ARM64_LDST64R_ARM64_LDST128R_POWER_TLS_LER_POWER_TLS_IER_POWER_TLSR_ADDRPOWER_DSR_ADDRPOWER_GOTR_ADDRPOWER_PCRELR_ADDRPOWER_TOCRELR_ADDRPOWER_TOCREL_DSR_RISCV_CALLR_RISCV_CALL_TRAMPR_RISCV_PCREL_ITYPER_RISCV_PCREL_STYPER_RISCV_TLS_IE_ITYPER_RISCV_TLS_IE_STYPER_PCRELDBLR_ADDRLOONG64R_ADDRLOONG64UR_ADDRLOONG64TLSR_ADDRLOONG64TLSUR_CALLLOONG64R_JMPLOONG64R_ADDRMIPSUR_ADDRMIPSTLSR_ADDRCUOFFR_WASMIMPORTR_XCOFFREF"
+const _RelocType_name = "R_ADDRR_ADDRPOWERR_ADDRARM64R_ADDRMIPSR_ADDROFFR_SIZER_CALLR_CALLARMR_CALLARM64R_CALLINDR_CALLPOWERR_CALLMIPSR_CONSTR_PCRELR_TLS_LER_TLS_IER_GOTOFFR_PLT0R_PLT1R_PLT2R_USEFIELDR_USETYPER_USEIFACER_USEIFACEMETHODR_USEGENERICIFACEMETHODR_METHODOFFR_KEEPR_POWER_TOCR_GOTPCRELR_JMPMIPSR_DWARFSECREFR_DWARFFILEREFR_ARM64_TLS_LER_ARM64_TLS_IER_ARM64_GOTPCRELR_ARM64_GOTR_ARM64_PCRELR_ARM64_PCREL_LDST8R_ARM64_PCREL_LDST16R_ARM64_PCREL_LDST32R_ARM64_PCREL_LDST64R_ARM64_LDST8R_ARM64_LDST16R_ARM64_LDST32R_ARM64_LDST64R_ARM64_LDST128R_POWER_TLS_LER_POWER_TLS_IER_POWER_TLSR_POWER_TLS_IE_PCREL34R_POWER_TLS_LE_TPREL34R_ADDRPOWER_DSR_ADDRPOWER_GOTR_ADDRPOWER_GOT_PCREL34R_ADDRPOWER_PCRELR_ADDRPOWER_TOCRELR_ADDRPOWER_TOCREL_DSR_ADDRPOWER_D34R_ADDRPOWER_PCREL34R_RISCV_CALLR_RISCV_CALL_TRAMPR_RISCV_PCREL_ITYPER_RISCV_PCREL_STYPER_RISCV_TLS_IE_ITYPER_RISCV_TLS_IE_STYPER_PCRELDBLR_ADDRLOONG64R_ADDRLOONG64UR_ADDRLOONG64TLSR_ADDRLOONG64TLSUR_CALLLOONG64R_JMPLOONG64R_ADDRMIPSUR_ADDRMIPSTLSR_ADDRCUOFFR_WASMIMPORTR_XCOFFREF"
 
-var _RelocType_index = [...]uint16{0, 6, 17, 28, 38, 47, 53, 59, 68, 79, 88, 99, 109, 116, 123, 131, 139, 147, 153, 159, 165, 175, 184, 194, 210, 233, 244, 250, 261, 271, 280, 293, 307, 321, 335, 351, 362, 375, 394, 414, 434, 454, 467, 481, 495, 509, 524, 538, 552, 563, 577, 592, 609, 627, 648, 660, 678, 697, 716, 736, 756, 766, 779, 793, 809, 826, 839, 851, 862, 875, 886, 898, 908}
+var _RelocType_index = [...]uint16{0, 6, 17, 28, 38, 47, 53, 59, 68, 79, 88, 99, 109, 116, 123, 131, 139, 147, 153, 159, 165, 175, 184, 194, 210, 233, 244, 250, 261, 271, 280, 293, 307, 321, 335, 351, 362, 375, 394, 414, 434, 454, 467, 481, 495, 509, 524, 538, 552, 563, 585, 607, 621, 636, 659, 676, 694, 715, 730, 749, 761, 779, 798, 817, 837, 857, 867, 880, 894, 910, 927, 940, 952, 963, 976, 987, 999, 1009}
 
 func (i RelocType) String() string {
        i -= 1
index bfa7c618e0f2ff5fc0b6f6514eff4bbbf62a8b3c..db486f29ddaab59df255510d972104a239474d7d 100644 (file)
@@ -727,6 +727,10 @@ func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym,
                default:
                        return false
                }
+       case objabi.R_ADDRPOWER_D34:
+               out.Write64(uint64(elf.R_PPC64_D34) | uint64(elfsym)<<32)
+       case objabi.R_ADDRPOWER_PCREL34:
+               out.Write64(uint64(elf.R_PPC64_PCREL34) | uint64(elfsym)<<32)
        case objabi.R_POWER_TLS:
                out.Write64(uint64(elf.R_PPC64_TLS) | uint64(elfsym)<<32)
        case objabi.R_POWER_TLS_LE:
@@ -734,6 +738,10 @@ func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym,
                out.Write64(uint64(r.Xadd))
                out.Write64(uint64(sectoff + 4))
                out.Write64(uint64(elf.R_PPC64_TPREL16_LO) | uint64(elfsym)<<32)
+       case objabi.R_POWER_TLS_LE_TPREL34:
+               out.Write64(uint64(elf.R_PPC64_TPREL34) | uint64(elfsym)<<32)
+       case objabi.R_POWER_TLS_IE_PCREL34:
+               out.Write64(uint64(elf.R_PPC64_GOT_TPREL_PCREL34) | uint64(elfsym)<<32)
        case objabi.R_POWER_TLS_IE:
                out.Write64(uint64(elf.R_PPC64_GOT_TPREL16_HA) | uint64(elfsym)<<32)
                out.Write64(uint64(r.Xadd))
@@ -888,48 +896,41 @@ func archrelocaddr(ldr *loader.Loader, target *ld.Target, syms *ld.ArchSyms, r l
        if target.IsAIX() {
                ldr.Errorf(s, "archrelocaddr called for %s relocation\n", ldr.SymName(rs))
        }
-       var o1, o2 uint32
-       if target.IsBigEndian() {
-               o1 = uint32(val >> 32)
-               o2 = uint32(val)
-       } else {
-               o1 = uint32(val)
-               o2 = uint32(val >> 32)
-       }
-
-       // We are spreading a 31-bit address across two instructions, putting the
-       // high (adjusted) part in the low 16 bits of the first instruction and the
-       // low part in the low 16 bits of the second instruction, or, in the DS case,
-       // bits 15-2 (inclusive) of the address into bits 15-2 of the second
-       // instruction (it is an error in this case if the low 2 bits of the address
-       // are non-zero).
+       o1, o2 := unpackInstPair(target, val)
 
+       // Verify resulting address fits within a 31 bit (2GB) address space.
+       // This is a restriction arising  from the usage of lis (HA) + d-form
+       // (LO) instruction sequences used to implement absolute relocations
+       // on PPC64 prior to ISA 3.1 (P10). For consistency, maintain this
+       // restriction for ISA 3.1 unless it becomes problematic.
        t := ldr.SymAddr(rs) + r.Add()
        if t < 0 || t >= 1<<31 {
                ldr.Errorf(s, "relocation for %s is too big (>=2G): 0x%x", ldr.SymName(s), ldr.SymValue(rs))
        }
-       if t&0x8000 != 0 {
-               t += 0x10000
-       }
 
        switch r.Type() {
+       case objabi.R_ADDRPOWER_PCREL34:
+               // S + A - P
+               t -= (ldr.SymValue(s) + int64(r.Off()))
+               o1 |= computePrefix34HI(t)
+               o2 |= computeLO(int32(t))
+       case objabi.R_ADDRPOWER_D34:
+               o1 |= computePrefix34HI(t)
+               o2 |= computeLO(int32(t))
        case objabi.R_ADDRPOWER:
-               o1 |= (uint32(t) >> 16) & 0xffff
-               o2 |= uint32(t) & 0xffff
+               o1 |= computeHA(int32(t))
+               o2 |= computeLO(int32(t))
        case objabi.R_ADDRPOWER_DS:
-               o1 |= (uint32(t) >> 16) & 0xffff
+               o1 |= computeHA(int32(t))
+               o2 |= computeLO(int32(t))
                if t&3 != 0 {
                        ldr.Errorf(s, "bad DS reloc for %s: %d", ldr.SymName(s), ldr.SymValue(rs))
                }
-               o2 |= uint32(t) & 0xfffc
        default:
                return -1
        }
 
-       if target.IsBigEndian() {
-               return int64(o1)<<32 | int64(o2)
-       }
-       return int64(o2)<<32 | int64(o1)
+       return packInstPair(target, o1, o2)
 }
 
 // Determine if the code was compiled so that the TOC register R2 is initialized and maintained
@@ -1084,6 +1085,61 @@ func gentramp(ctxt *ld.Link, ldr *loader.Loader, tramp *loader.SymbolBuilder, ta
        tramp.SetData(P)
 }
 
+// Unpack a pair of 32 bit instruction words from
+// a 64 bit relocation into instN and instN+1 in endian order.
+func unpackInstPair(target *ld.Target, r int64) (uint32, uint32) {
+       if target.IsBigEndian() {
+               return uint32(r >> 32), uint32(r)
+       }
+       return uint32(r), uint32(r >> 32)
+}
+
+// Pack a pair of 32 bit instruction words o1, o2 into 64 bit relocation
+// in endian order.
+func packInstPair(target *ld.Target, o1, o2 uint32) int64 {
+       if target.IsBigEndian() {
+               return (int64(o1) << 32) | int64(o2)
+       }
+       return int64(o1) | (int64(o2) << 32)
+}
+
+// Compute the high-adjusted value (always a signed 32b value) per the ELF ABI.
+// The returned value is always 0 <= x <= 0xFFFF.
+func computeHA(val int32) uint32 {
+       return uint32(uint16((val + 0x8000) >> 16))
+}
+
+// Compute the low value (the lower 16 bits of any 32b value) per the ELF ABI.
+// The returned value is always 0 <= x <= 0xFFFF.
+func computeLO(val int32) uint32 {
+       return uint32(uint16(val))
+}
+
+// Compute the high 18 bits of a signed 34b constant. Used to pack the high 18 bits
+// of a prefix34 relocation field. This assumes the input is already restricted to
+// 34 bits.
+func computePrefix34HI(val int64) uint32 {
+       return uint32((val >> 16) & 0x3FFFF)
+}
+
+func computeTLSLEReloc(target *ld.Target, ldr *loader.Loader, rs, s loader.Sym) int64 {
+       // The thread pointer points 0x7000 bytes after the start of the
+       // thread local storage area as documented in section "3.7.2 TLS
+       // Runtime Handling" of "Power Architecture 64-Bit ELF V2 ABI
+       // Specification".
+       v := ldr.SymValue(rs) - 0x7000
+       if target.IsAIX() {
+               // On AIX, the thread pointer points 0x7800 bytes after
+               // the TLS.
+               v -= 0x800
+       }
+
+       if int64(int32(v)) != v {
+               ldr.Errorf(s, "TLS offset out of range %d", v)
+       }
+       return v
+}
+
 func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loader.Reloc, s loader.Sym, val int64) (relocatedOffset int64, nExtReloc int, ok bool) {
        rs := r.Sym()
        if target.IsExternal() {
@@ -1094,7 +1150,7 @@ func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loade
                        if !target.IsAIX() {
                                return val, nExtReloc, false
                        }
-               case objabi.R_POWER_TLS:
+               case objabi.R_POWER_TLS, objabi.R_POWER_TLS_IE_PCREL34, objabi.R_POWER_TLS_LE_TPREL34:
                        nExtReloc = 1
                        return val, nExtReloc, true
                case objabi.R_POWER_TLS_LE, objabi.R_POWER_TLS_IE:
@@ -1125,7 +1181,7 @@ func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loade
                        if !target.IsAIX() {
                                return val, nExtReloc, true
                        }
-               case objabi.R_CALLPOWER:
+               case objabi.R_CALLPOWER, objabi.R_ADDRPOWER_D34, objabi.R_ADDRPOWER_PCREL34:
                        nExtReloc = 1
                        if !target.IsAIX() {
                                return val, nExtReloc, true
@@ -1136,7 +1192,7 @@ func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loade
        switch r.Type() {
        case objabi.R_ADDRPOWER_TOCREL, objabi.R_ADDRPOWER_TOCREL_DS:
                return archreloctoc(ldr, target, syms, r, s, val), nExtReloc, true
-       case objabi.R_ADDRPOWER, objabi.R_ADDRPOWER_DS:
+       case objabi.R_ADDRPOWER, objabi.R_ADDRPOWER_DS, objabi.R_ADDRPOWER_D34, objabi.R_ADDRPOWER_PCREL34:
                return archrelocaddr(ldr, target, syms, r, s, val), nExtReloc, true
        case objabi.R_CALLPOWER:
                // Bits 6 through 29 = (S + A - P) >> 2
@@ -1169,16 +1225,10 @@ func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loade
 
        case objabi.R_ADDRPOWER_PCREL: // S + A - P
                t := ldr.SymValue(rs) + r.Add() - (ldr.SymValue(s) + int64(r.Off()))
-               ha := uint16(((t + 0x8000) >> 16) & 0xFFFF)
-               l := uint16(t)
-               if target.IsBigEndian() {
-                       val |= int64(l)
-                       val |= int64(ha) << 32
-               } else {
-                       val |= int64(ha)
-                       val |= int64(l) << 32
-               }
-               return val, nExtReloc, true
+               ha, l := unpackInstPair(target, val)
+               l |= computeLO(int32(t))
+               ha |= computeHA(int32(t))
+               return packInstPair(target, ha, l), nExtReloc, true
 
        case objabi.R_POWER_TLS:
                const OP_ADD = 31<<26 | 266<<1
@@ -1210,50 +1260,48 @@ func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loade
                const OP_ADDI = 14 << 26
                const OP_MASK = 0x3F << 26
                const OP_RA_MASK = 0x1F << 16
-               uval := uint64(val)
                // convert r2 to r0, and ld to addi
-               if target.IsBigEndian() {
-                       uval = uval &^ (OP_RA_MASK << 32)
-                       uval = (uval &^ OP_MASK) | OP_ADDI
-               } else {
-                       uval = uval &^ (OP_RA_MASK)
-                       uval = (uval &^ (OP_MASK << 32)) | (OP_ADDI << 32)
-               }
-               val = int64(uval)
-               // Treat this like an R_POWER_TLS_LE relocation now.
+               mask := packInstPair(target, OP_RA_MASK, OP_MASK)
+               addi_op := packInstPair(target, 0, OP_ADDI)
+               val &^= mask
+               val |= addi_op
                fallthrough
 
        case objabi.R_POWER_TLS_LE:
-               // The thread pointer points 0x7000 bytes after the start of the
-               // thread local storage area as documented in section "3.7.2 TLS
-               // Runtime Handling" of "Power Architecture 64-Bit ELF V2 ABI
-               // Specification".
-               v := ldr.SymValue(rs) - 0x7000
-               if target.IsAIX() {
-                       // On AIX, the thread pointer points 0x7800 bytes after
-                       // the TLS.
-                       v -= 0x800
-               }
+               v := computeTLSLEReloc(target, ldr, rs, s)
+               o1, o2 := unpackInstPair(target, val)
+               o1 |= computeHA(int32(v))
+               o2 |= computeLO(int32(v))
+               return packInstPair(target, o1, o2), nExtReloc, true
 
-               var o1, o2 uint32
-               if int64(int32(v)) != v {
-                       ldr.Errorf(s, "TLS offset out of range %d", v)
-               }
-               if target.IsBigEndian() {
-                       o1 = uint32(val >> 32)
-                       o2 = uint32(val)
-               } else {
-                       o1 = uint32(val)
-                       o2 = uint32(val >> 32)
+       case objabi.R_POWER_TLS_IE_PCREL34:
+               // Convert TLS_IE relocation to TLS_LE if supported.
+               if !(target.IsPIE() && target.IsElf()) {
+                       log.Fatalf("cannot handle R_POWER_TLS_IE (sym %s) when linking non-PIE, non-ELF binaries internally", ldr.SymName(s))
                }
 
-               o1 |= uint32(((v + 0x8000) >> 16) & 0xFFFF)
-               o2 |= uint32(v & 0xFFFF)
+               // We are an ELF binary, we can safely convert to TLS_LE_TPREL34 from:
+               // pld rX, x@got@tprel@pcrel
+               //
+               // to TLS_LE_TPREL32 by converting to:
+               // pla rX, x@tprel
+
+               const OP_MASK_PFX = 0xFFFFFFFF        // Discard prefix word
+               const OP_MASK = (0x3F << 26) | 0xFFFF // Preserve RT, RA
+               const OP_PFX = 1<<26 | 2<<24
+               const OP_PLA = 14 << 26
+               mask := packInstPair(target, OP_MASK_PFX, OP_MASK)
+               pla_op := packInstPair(target, OP_PFX, OP_PLA)
+               val &^= mask
+               val |= pla_op
+               fallthrough
 
-               if target.IsBigEndian() {
-                       return int64(o1)<<32 | int64(o2), nExtReloc, true
-               }
-               return int64(o2)<<32 | int64(o1), nExtReloc, true
+       case objabi.R_POWER_TLS_LE_TPREL34:
+               v := computeTLSLEReloc(target, ldr, rs, s)
+               o1, o2 := unpackInstPair(target, val)
+               o1 |= computePrefix34HI(v)
+               o2 |= computeLO(int32(v))
+               return packInstPair(target, o1, o2), nExtReloc, true
        }
 
        return val, nExtReloc, false
@@ -1354,14 +1402,16 @@ overflow:
 
 func extreloc(target *ld.Target, ldr *loader.Loader, r loader.Reloc, s loader.Sym) (loader.ExtReloc, bool) {
        switch r.Type() {
-       case objabi.R_POWER_TLS, objabi.R_POWER_TLS_LE, objabi.R_POWER_TLS_IE, objabi.R_CALLPOWER:
+       case objabi.R_POWER_TLS, objabi.R_POWER_TLS_LE, objabi.R_POWER_TLS_IE, objabi.R_POWER_TLS_IE_PCREL34, objabi.R_POWER_TLS_LE_TPREL34, objabi.R_CALLPOWER:
                return ld.ExtrelocSimple(ldr, r), true
        case objabi.R_ADDRPOWER,
                objabi.R_ADDRPOWER_DS,
                objabi.R_ADDRPOWER_TOCREL,
                objabi.R_ADDRPOWER_TOCREL_DS,
                objabi.R_ADDRPOWER_GOT,
-               objabi.R_ADDRPOWER_PCREL:
+               objabi.R_ADDRPOWER_PCREL,
+               objabi.R_ADDRPOWER_D34,
+               objabi.R_ADDRPOWER_PCREL34:
                return ld.ExtrelocViaOuterSym(ldr, r, s), true
        }
        return loader.ExtReloc{}, false