Index: ELF/Target.cpp =================================================================== --- ELF/Target.cpp +++ ELF/Target.cpp @@ -9,6 +9,7 @@ #include "Target.h" #include "Error.h" +#include "OutputSections.h" #include "Symbols.h" #include "llvm/ADT/ArrayRef.h" @@ -204,35 +205,234 @@ } } +// 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 inline uint16_t applyPPClo(uint64_t value) { return value & 0xffff; } + +static inline uint16_t applyPPChi(uint64_t value) { + return (value >> 16) & 0xffff; +} + +static inline uint16_t applyPPCha (uint64_t value) { + return ((value + 0x8000) >> 16) & 0xffff; +} + +static inline uint16_t applyPPChigher(uint64_t value) { + return (value >> 32) & 0xffff; +} + +static inline uint16_t applyPPChighera (uint64_t value) { + return ((value + 0x8000) >> 32) & 0xffff; +} + +static inline uint16_t applyPPChighest(uint64_t value) { + return (value >> 48) & 0xffff; +} + +static inline uint16_t applyPPChighesta (uint64_t value) { + return ((value + 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 = 0x10000; VAStart = 0x10000000; } void PPC64TargetInfo::writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr, - uint64_t PltEntryAddr) const {} + uint64_t PltEntryAddr) const { + std::fill(Buf, Buf + getPltEntrySize(), 0); + + // FIXME: See comments in relocateOne. + uint64_t GotVA = Out::Got->getVA(); + uint64_t TocVA = GotVA; + uint64_t TocBaseVA = TocVA - 0x8000; + + uint64_t EntryOffset = GotEntryAddr - TocBaseVA; + + // 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. + + const uint32_t Insts[] = { + 0xf8410000u, // std %r2, 40(%r1) + 0x3d620000u | applyPPCha(EntryOffset), // addis %r11, %r2, X@ha + 0xe98b0000u | applyPPClo(EntryOffset), // ld %r12, X@l(%r11) + 0xe96c0000u, // ld %r11,0(%r12) + 0x7d6903a6u, // mtctr %r11 + 0xe84c0008u, // ld %r2,8(%r12) + 0xe96c0010u, // ld %r11,16(%r12) + 0x4e800420u // bctr + }; + + for (auto InstVal : Insts) { + write32be(Buf, InstVal); + Buf += 4; + } +} 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; + switch (Type) { + default: return false; + case R_PPC64_REL24: + // 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, uint64_t GotVA) const { typedef ELFFile::Elf_Rela Elf_Rela; auto &Rel = *reinterpret_cast(RelP); + int64_t Addend = Rel.r_addend; + + // 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! + uint64_t TocVA = GotVA; + + // 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. + uint64_t TocBaseVA = TocVA - 0x8000; uint64_t Offset = Rel.r_offset; uint8_t *Loc = Buf + Offset; + + 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."); + } + + Addend -= TocBaseVA; + } else if (Type == R_PPC64_TOC) { + write64be(Loc, TocBaseVA); + return; + } + switch (Type) { - case R_PPC64_ADDR64: - write64be(Loc, SymVA + Rel.r_addend); + case R_PPC64_ADDR16: + write16be(Loc, applyPPClo(SymVA + Addend)); + break; + case R_PPC64_ADDR16_DS: { + uint64_t Result = SymVA + Addend; + if ((uint64_t) SignExtend32<16>(Result) != Result) + error("Relocation R_PPC64_ADDR16_DS overflow"); + + uint16_t Mask = ~3; + write16be(Loc, (read16be(Loc) & ~Mask) | + (Result & Mask)); + } break; + case R_PPC64_ADDR16_LO: + write16be(Loc, applyPPClo(SymVA + Addend)); + break; + case R_PPC64_ADDR16_LO_DS: { + uint16_t Mask = ~3; + write16be(Loc, (read16be(Loc) & ~Mask) | + (applyPPClo(SymVA + Addend) & Mask)); + } break; + case R_PPC64_ADDR16_HI: + write16be(Loc, applyPPChi(SymVA + Addend)); + break; + case R_PPC64_ADDR16_HA: + write16be(Loc, applyPPCha(SymVA + Addend)); break; - case R_PPC64_TOC: - // We don't create a TOC yet. + case R_PPC64_ADDR16_HIGHER: + write16be(Loc, applyPPChigher(SymVA + Addend)); + break; + case R_PPC64_ADDR16_HIGHERA: + write16be(Loc, applyPPChighera(SymVA + Addend)); + break; + case R_PPC64_ADDR16_HIGHEST: + write16be(Loc, applyPPChighest(SymVA + Addend)); + break; + case R_PPC64_ADDR16_HIGHESTA: + write16be(Loc, applyPPChighesta(SymVA + Addend)); + break; + case R_PPC64_ADDR14: { + assert(((SymVA + Addend) & 3) == 0); + // Preserve the AA/LK bits in the branch instruction + uint8_t aalk = *(Loc + 3); + write16be(Loc + 2, (aalk & 3) | ((SymVA + Addend) & 0xfffc)); + } break; + case R_PPC64_REL16_LO: { + uint64_t FinalAddress = (BaseAddr + Offset); + uint64_t Delta = SymVA - FinalAddress + Addend; + write16be(Loc, applyPPClo(Delta)); + } break; + case R_PPC64_REL16_HI: { + uint64_t FinalAddress = (BaseAddr + Offset); + uint64_t Delta = SymVA - FinalAddress + Addend; + write16be(Loc, applyPPChi(Delta)); + } break; + case R_PPC64_REL16_HA: { + uint64_t FinalAddress = (BaseAddr + Offset); + uint64_t Delta = SymVA - FinalAddress + Addend; + write16be(Loc, applyPPCha(Delta)); + } break; + case R_PPC64_ADDR32: { + int32_t Result = static_cast(SymVA + Addend); + if (SignExtend32<32>(Result) != Result) + error("Relocation R_PPC64_ADDR32 overflow"); + write32be(Loc, Result); + } break; + case R_PPC64_REL24: { + uint64_t FinalAddress = (BaseAddr + Offset); + int32_t delta = static_cast(SymVA - FinalAddress + Addend); + uint32_t Mask = 0x03FFFFFC; + if (SignExtend32<24>(delta) != delta) + error("Relocation R_PPC64_REL24 overflow"); + write32be(Loc, (read32be(Loc) & ~Mask) | (delta & Mask)); + } break; + case R_PPC64_REL32: { + uint64_t FinalAddress = (BaseAddr + Offset); + int32_t delta = static_cast(SymVA - FinalAddress + Addend); + if (SignExtend32<32>(delta) != delta) + error("Relocation R_PPC64_REL32 overflow"); + write32be(Loc, delta); + } break; + case R_PPC64_REL64: { + uint64_t FinalAddress = (BaseAddr + Offset); + uint64_t Delta = SymVA - FinalAddress + Addend; + write64be(Loc, Delta); + } break; + case R_PPC64_ADDR64: + write64be(Loc, SymVA + Addend); break; default: error(Twine("unrecognized reloc ") + Twine(Type));