Index: ELF/Target.h =================================================================== --- ELF/Target.h +++ ELF/Target.h @@ -83,6 +83,9 @@ bool relocNeedsPlt(uint32_t Type, const SymbolBody &S) const override; void relocateOne(uint8_t *Buf, const void *RelP, uint32_t Type, uint64_t BaseAddr, uint64_t SymVA) const override; + +protected: + uint64_t getTocBase() const; }; class PPCTargetInfo final : public TargetInfo { Index: ELF/Target.cpp =================================================================== --- ELF/Target.cpp +++ ELF/Target.cpp @@ -198,34 +198,213 @@ } } +// Relocation masks following the #lo(value), #hi(value), #ha(value), +// #higher(value), #highera(value), #highest(value), and #highesta(value) +// macros defined in section 4.5.1. Relocation Types of the PPC-elf64abi +// document. + +static uint16_t applyPPCLo(uint64_t V) { return V & 0xffff; } + +static uint16_t applyPPCHi(uint64_t V) { return (V >> 16) & 0xffff; } + +static uint16_t applyPPCHa(uint64_t V) { return ((V + 0x8000) >> 16) & 0xffff; } + +static uint16_t applyPPCHigher(uint64_t V) { return (V >> 32) & 0xffff; } + +static uint16_t applyPPCHighera(uint64_t V) { + return ((V + 0x8000) >> 32) & 0xffff; +} + +static uint16_t applyPPCHighest(uint64_t V) { return (V >> 48) & 0xffff; } + +static uint16_t applyPPCHighesta(uint64_t V) { + return ((V + 0x8000) >> 48) & 0xffff; +} + PPC64TargetInfo::PPC64TargetInfo() { - // PCRelReloc = FIXME - // GotReloc = FIXME + PCRelReloc = R_PPC64_REL24; + GotReloc = R_PPC64_GLOB_DAT; + GotRefReloc = R_PPC64_REL64; PltEntrySize = 32; PageSize = 65536; VAStart = 0x10000000; } +uint64_t PPC64TargetInfo::getTocBase() const { + // The TOC consists of sections .got, .toc, .tocbss, .plt in that + // order. The TOC starts where the first of these sections starts. + + // FIXME: This obviously does not do the right thing when there is no .got + // section, but there is a .toc or .tocbss section. + uint64_t TocVA = Out::Got->getVA(); + if (!TocVA) + TocVA = Out::Plt->getVA(); + + // Per the ppc64-elf-linux ABI, The TOC base is TOC value plus 0x8000 + // thus permitting a full 64 Kbytes segment. Note that the glibc startup + // code (crt1.o) assumes that you can get from the TOC base to the + // start of the .toc section with only a single (signed) 16-bit relocation. + return TocVA + 0x8000; +} void PPC64TargetInfo::writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr, - uint64_t PltEntryAddr) const {} + uint64_t PltEntryAddr) const { + uint64_t EntryOffset = GotEntryAddr - getTocBase(); + + // FIXME: What we should do, in theory, is get the offset of the function + // descriptor in the .opd section, and use that as the offset from %r2 (the + // TOC-base pointer). Instead, we have the GOT-entry offset, and that will + // be a pointer to the function descriptor in the .opd section. Using + // this scheme is simpler, but requires an extra indirection per PLT dispatch. + + write32be(Buf, 0xf8410000u); // std %r2, 40(%r1) + write32be(Buf + 4, 0x3d620000u | + applyPPCHa(EntryOffset)); // addis %r11, %r2, X@ha + write32be(Buf + 8, 0xe98b0000u | + applyPPCLo(EntryOffset)); // ld %r12, X@l(%r11) + write32be(Buf + 12, 0xe96c0000u); // ld %r11,0(%r12) + write32be(Buf + 16, 0x7d6903a6u); // mtctr %r11 + write32be(Buf + 20, 0xe84c0008u); // ld %r2,8(%r12) + write32be(Buf + 24, 0xe96c0010u); // ld %r11,16(%r12) + write32be(Buf + 28, 0x4e800420u); // bctr +} bool PPC64TargetInfo::relocNeedsGot(uint32_t Type, const SymbolBody &S) const { - return false; + if (relocNeedsPlt(Type, S)) + return true; + + switch (Type) { + default: return false; + case R_PPC64_GOT16: + case R_PPC64_GOT16_LO: + case R_PPC64_GOT16_HI: + case R_PPC64_GOT16_HA: + case R_PPC64_GOT16_DS: + case R_PPC64_GOT16_LO_DS: + return true; + } } bool PPC64TargetInfo::relocNeedsPlt(uint32_t Type, const SymbolBody &S) const { - return false; + if (Type != R_PPC64_REL24) + return false; + + // These are function calls that need to be redirected through a PLT stub. + return S.isShared() || (S.isUndefined() && S.isWeak()); } void PPC64TargetInfo::relocateOne(uint8_t *Buf, const void *RelP, uint32_t Type, uint64_t BaseAddr, uint64_t SymVA) const { typedef ELFFile::Elf_Rela Elf_Rela; auto &Rel = *reinterpret_cast(RelP); - uint64_t Offset = Rel.r_offset; - uint8_t *Loc = Buf + Offset; + uint8_t *L = Buf + Rel.r_offset; + uint64_t S = SymVA; + int64_t A = Rel.r_addend; + uint64_t P = BaseAddr + Rel.r_offset; + + if (Type == R_PPC64_TOC) { + write64be(L, getTocBase()); + return; + } + + if (Type == R_PPC64_TOC16 || + Type == R_PPC64_TOC16_DS || + Type == R_PPC64_TOC16_LO || + Type == R_PPC64_TOC16_LO_DS || + Type == R_PPC64_TOC16_HI || + Type == R_PPC64_TOC16_HA) { + switch (Type) { + case R_PPC64_TOC16: Type = R_PPC64_ADDR16; break; + case R_PPC64_TOC16_DS: Type = R_PPC64_ADDR16_DS; break; + case R_PPC64_TOC16_LO: Type = R_PPC64_ADDR16_LO; break; + case R_PPC64_TOC16_LO_DS: Type = R_PPC64_ADDR16_LO_DS; break; + case R_PPC64_TOC16_HI: Type = R_PPC64_ADDR16_HI; break; + case R_PPC64_TOC16_HA: Type = R_PPC64_ADDR16_HA; break; + default: llvm_unreachable("Wrong relocation type."); + } + + A -= getTocBase(); + } + + uint64_t R = S + A; + switch (Type) { - case R_PPC64_ADDR64: - write64be(Loc, SymVA + Rel.r_addend); + case R_PPC64_ADDR16: + write16be(L, applyPPCLo(R)); + break; + case R_PPC64_ADDR16_DS: { + if (!isInt<16>(R)) + error("Relocation R_PPC64_ADDR16_DS overflow"); + write16be(L, (read16be(L) & 3) | ((R) & ~3)); + break; + } + case R_PPC64_ADDR16_LO: + write16be(L, applyPPCLo(R)); + break; + case R_PPC64_ADDR16_LO_DS: { + write16be(L, (read16be(L) & 3) | (applyPPCLo(R) & ~3)); + break; + } + case R_PPC64_ADDR16_HI: + write16be(L, applyPPCHi(R)); + break; + case R_PPC64_ADDR16_HA: + write16be(L, applyPPCHa(R)); + break; + case R_PPC64_ADDR16_HIGHER: + write16be(L, applyPPCHigher(R)); + break; + case R_PPC64_ADDR16_HIGHERA: + write16be(L, applyPPCHighera(R)); + break; + case R_PPC64_ADDR16_HIGHEST: + write16be(L, applyPPCHighest(R)); + break; + case R_PPC64_ADDR16_HIGHESTA: + write16be(L, applyPPCHighesta(R)); + break; + case R_PPC64_ADDR14: { + if ((R & 3) != 0) + error("Improper alignment for relocation R_PPC64_ADDR14"); + + // Preserve the AA/LK bits in the branch instruction + uint8_t AALK = *(L + 3); + write16be(L + 2, (AALK & 3) | (R & 0xfffc)); break; - case R_PPC64_TOC: - // We don't create a TOC yet. + } + case R_PPC64_REL16_LO: { + write16be(L, applyPPCLo(R - P)); + break; + } + case R_PPC64_REL16_HI: { + write16be(L, applyPPCHi(R - P)); + break; + } + case R_PPC64_REL16_HA: { + write16be(L, applyPPCHa(R - P)); + break; + } + case R_PPC64_ADDR32: { + if (!isInt<32>(R)) + error("Relocation R_PPC64_ADDR32 overflow"); + write32be(L, R); + break; + } + case R_PPC64_REL24: { + uint32_t Mask = 0x03FFFFFC; + if (!isInt<24>(R - P)) + error("Relocation R_PPC64_REL24 overflow"); + write32be(L, (read32be(L) & ~Mask) | ((R - P) & Mask)); + break; + } + case R_PPC64_REL32: { + if (!isInt<32>(R - P)) + error("Relocation R_PPC64_REL32 overflow"); + write32be(L, R - P); + break; + } + case R_PPC64_REL64: { + write64be(L, R - P); + break; + } + case R_PPC64_ADDR64: + write64be(L, R); break; default: error(Twine("unrecognized reloc ") + Twine(Type)); Index: test/elf2/ppc64-relocs.s =================================================================== --- /dev/null +++ test/elf2/ppc64-relocs.s @@ -0,0 +1,130 @@ +# RUN: llvm-mc -filetype=obj -triple=powerpc64-unknown-linux %s -o %t +# RUN: ld.lld2 %t -o %t2 +# RUN: llvm-objdump -d %t2 | FileCheck %s +# REQUIRES: ppc + +.section ".opd","aw" +.global _start +_start: +.quad .Lfoo,.TOC.@tocbase,0 + +.text +.Lfoo: + li 0,1 + li 3,42 + sc + +.section ".toc","aw" +.L1: +.quad 22, 37, 89, 47 + +.section .R_PPC64_TOC16_LO_DS,"ax",@progbits +.globl .FR_PPC64_TOC16_LO_DS +.FR_PPC64_TOC16_LO_DS: + ld 1, .L1@toc@l(2) + +# CHECK: Disassembly of section .R_PPC64_TOC16_LO_DS: +# CHECK: .FR_PPC64_TOC16_LO_DS: +# CHECK: 1001000c: e8 22 80 00 ld 1, -32768(2) + +.section .R_PPC64_TOC16_LO,"ax",@progbits +.globl .FR_PPC64_TOC16_LO +.FR_PPC64_TOC16_LO: + addi 1, 2, .L1@toc@l + +# CHECK: Disassembly of section .R_PPC64_TOC16_LO: +# CHECK: .FR_PPC64_TOC16_LO: +# CHECK: 10010010: 38 22 80 00 addi 1, 2, -32768 + +.section .R_PPC64_TOC16_HI,"ax",@progbits +.globl .FR_PPC64_TOC16_HI +.FR_PPC64_TOC16_HI: + addis 1, 2, .L1@toc@h + +# CHECK: Disassembly of section .R_PPC64_TOC16_HI: +# CHECK: .FR_PPC64_TOC16_HI: +# CHECK: 10010014: 3c 22 10 01 addis 1, 2, 4097 + +.section .R_PPC64_TOC16_HA,"ax",@progbits +.globl .FR_PPC64_TOC16_HA +.FR_PPC64_TOC16_HA: + addis 1, 2, .L1@toc@ha + +# CHECK: Disassembly of section .R_PPC64_TOC16_HA: +# CHECK: .FR_PPC64_TOC16_HA: +# CHECK: 10010018: 3c 22 10 02 addis 1, 2, 4098 + +.section .R_PPC64_REL24,"ax",@progbits +.globl .FR_PPC64_REL24 +.FR_PPC64_REL24: + b .Lfoox +.section .R_PPC64_REL24_2,"ax",@progbits +.Lfoox: + +# CHECK: Disassembly of section .R_PPC64_REL24: +# CHECK: .FR_PPC64_REL24: +# CHECK: 1001001c: 48 00 00 04 b .+4 + +.section .R_PPC64_ADDR16_LO,"ax",@progbits +.globl .FR_PPC64_ADDR16_LO +.FR_PPC64_ADDR16_LO: + li 1, .Lfoo@l + +# CHECK: Disassembly of section .R_PPC64_ADDR16_LO: +# CHECK: .FR_PPC64_ADDR16_LO: +# CHECK: 10010020: 38 20 00 00 li 1, 0 + +.section .R_PPC64_ADDR16_HI,"ax",@progbits +.globl .FR_PPC64_ADDR16_HI +.FR_PPC64_ADDR16_HI: + li 1, .Lfoo@h + +# CHECK: Disassembly of section .R_PPC64_ADDR16_HI: +# CHECK: .FR_PPC64_ADDR16_HI: +# CHECK: 10010024: 38 20 10 01 li 1, 4097 + +.section .R_PPC64_ADDR16_HA,"ax",@progbits +.globl .FR_PPC64_ADDR16_HA +.FR_PPC64_ADDR16_HA: + li 1, .Lfoo@ha + +# CHECK: Disassembly of section .R_PPC64_ADDR16_HA: +# CHECK: .FR_PPC64_ADDR16_HA: +# CHECK: 10010028: 38 20 10 01 li 1, 4097 + +.section .R_PPC64_ADDR16_HIGHER,"ax",@progbits +.globl .FR_PPC64_ADDR16_HIGHER +.FR_PPC64_ADDR16_HIGHER: + li 1, .Lfoo@higher + +# CHECK: Disassembly of section .R_PPC64_ADDR16_HIGHER: +# CHECK: .FR_PPC64_ADDR16_HIGHER: +# CHECK: 1001002c: 38 20 00 00 li 1, 0 + +.section .R_PPC64_ADDR16_HIGHERA,"ax",@progbits +.globl .FR_PPC64_ADDR16_HIGHERA +.FR_PPC64_ADDR16_HIGHERA: + li 1, .Lfoo@highera + +# CHECK: Disassembly of section .R_PPC64_ADDR16_HIGHERA: +# CHECK: .FR_PPC64_ADDR16_HIGHERA: +# CHECK: 10010030: 38 20 00 00 li 1, 0 + +.section .R_PPC64_ADDR16_HIGHEST,"ax",@progbits +.globl .FR_PPC64_ADDR16_HIGHEST +.FR_PPC64_ADDR16_HIGHEST: + li 1, .Lfoo@highest + +# CHECK: Disassembly of section .R_PPC64_ADDR16_HIGHEST: +# CHECK: .FR_PPC64_ADDR16_HIGHEST: +# CHECK: 10010034: 38 20 00 00 li 1, 0 + +.section .R_PPC64_ADDR16_HIGHESTA,"ax",@progbits +.globl .FR_PPC64_ADDR16_HIGHESTA +.FR_PPC64_ADDR16_HIGHESTA: + li 1, .Lfoo@highesta + +# CHECK: Disassembly of section .R_PPC64_ADDR16_HIGHESTA: +# CHECK: .FR_PPC64_ADDR16_HIGHESTA: +# CHECK: 10010038: 38 20 00 00 li 1, 0 +