Index: lld/ELF/Arch/PPC64.cpp =================================================================== --- lld/ELF/Arch/PPC64.cpp +++ lld/ELF/Arch/PPC64.cpp @@ -376,6 +376,20 @@ return read32(config->isLE ? loc : loc - 2); } +// The prefixed instruction is always a 4 byte prefix followed by a 4 byte +// instruction. Therefore, the prefix is always in lower memory than the +// instruction (regardless of endianness). +// As a result, we need to shift the pieces around on little endian machines. +static void writePrefixedInstruction(uint8_t *loc, uint64_t insn) { + insn = config->isLE ? insn << 32 | insn >> 32 : insn; + write64(loc, insn); +} + +static uint64_t readPrefixedInstruction(const uint8_t *loc) { + uint64_t fullInstr = read64(loc); + return config->isLE ? (fullInstr << 32 | fullInstr >> 32) : fullInstr; +} + PPC64::PPC64() { copyRel = R_PPC64_COPY; gotRel = R_PPC64_GLOB_DAT; @@ -670,6 +684,7 @@ case R_PPC64_REL16_HI: case R_PPC64_REL32: case R_PPC64_REL64: + case R_PPC64_PCREL34: return R_PC; case R_PPC64_GOT_TLSGD16: case R_PPC64_GOT_TLSGD16_HA: @@ -986,6 +1001,17 @@ case R_PPC64_DTPREL64: write64(loc, val - dynamicThreadPointerOffset); break; + case R_PPC64_PCREL34: { + const uint64_t si0Mask = 0x00000003ffff0000; + const uint64_t si1Mask = 0x000000000000ffff; + const uint64_t fullMask = 0x0003ffff0000ffff; + checkInt(loc, val, 34, rel); + + uint64_t instr = readPrefixedInstruction(loc) & ~fullMask; + writePrefixedInstruction(loc, instr | ((val & si0Mask) << 16) | + (val & si1Mask)); + break; + } default: llvm_unreachable("unknown relocation"); } Index: lld/test/ELF/ppc64-reloc-pcrel34-overflow.s =================================================================== --- /dev/null +++ lld/test/ELF/ppc64-reloc-pcrel34-overflow.s @@ -0,0 +1,21 @@ +# REQUIRES: ppc +# RUN: echo 'SECTIONS { \ +# RUN: .text_low 0x10010000: { *(.text_low) } \ +# RUN: .text_overflow 0x1000000000 : { *(.text_overflow) } \ +# RUN: }' > %t.script + +# RUN: llvm-mc -filetype=obj -triple=powerpc64le %s -o %t.o +# RUN: not ld.lld -T %t.script %t.o -o %t + +# RUN: llvm-mc -filetype=obj -triple=powerpc64 %s -o %t.o +# RUN: not ld.lld -T %t.script %t.o -o %t + +.section .text_low, "ax", %progbits +# CHECK: relocation R_PPC64_PCREL34 out of range +GlobIntOverflow: + plwa 3, glob_overflow@PCREL(0), 1 + blr +.section .text_overflow, "ax", %progbits +glob_overflow: + .long 0 + .size glob_overflow, 4 Index: lld/test/ELF/ppc64-reloc-pcrel34.s =================================================================== --- /dev/null +++ lld/test/ELF/ppc64-reloc-pcrel34.s @@ -0,0 +1,49 @@ +# REQUIRES: ppc +# RUN: echo 'SECTIONS { \ +# RUN: .text_low 0x10010000: { *(.text_low) } \ +# RUN: .text_high 0x10080000 : { *(.text_high) } \ +# RUN: }' > %t.script + +# RUN: llvm-mc -filetype=obj -triple=powerpc64le %s -o %t.o +# RUN: ld.lld -T %t.script %t.o -o %t +# RUN: llvm-readelf -s %t | FileCheck %s --check-prefix=SYMBOL +# RUN: llvm-objdump -d --no-show-raw-insn --mcpu=future %t | FileCheck %s + +# RUN: llvm-mc -filetype=obj -triple=powerpc64 %s -o %t.o +# RUN: ld.lld -T %t.script %t.o -o %t +# RUN: llvm-readelf -s %t | FileCheck %s --check-prefix=SYMBOL +# RUN: llvm-objdump -d --no-show-raw-insn --mcpu=future %t | FileCheck %s + +.section .text_low, "ax", %progbits +# CHECK-LABEL: : +# CHECK-NEXT: 10010000: plwa 3, 12(0), 1 +# SYMBOL: 1001000c 4 NOTYPE LOCAL DEFAULT 1 glob_int +GlobIntPCRel: + plwa 3, glob_int@PCREL(0), 1 + blr +glob_int: + .long 0 + .size glob_int, 4 + + +# CHECK-LABEL: : +# CHECK-NEXT: 10010010: plwa 3, 16(0), 1 +# SYMBOL: 1001001c 8 NOTYPE LOCAL DEFAULT 1 glob_int8 +GlobIntPCRelOffset: + plwa 3, glob_int8@PCREL+4(0), 1 + blr +glob_int8: + .quad 0 + .size glob_int8, 8 + + +# CHECK-LABEL: : +# CHECK-NEXT: 10010024: plwa 3, 458720(0), 1 +# SYMBOL: 10080000 8 NOTYPE LOCAL DEFAULT 2 glob_int8_big +GlobIntPCRelBigOffset: + plwa 3, glob_int8_big@PCREL+4(0), 1 + blr +.section .text_high, "ax", %progbits +glob_int8_big: + .quad 0 + .size glob_int8_big, 8