Index: ELF/Target.h =================================================================== --- ELF/Target.h +++ ELF/Target.h @@ -86,6 +86,9 @@ uint32_t Type, uint64_t BaseAddr, uint64_t SymVA) const override; bool isRelRelative(uint32_t Type) const override; + + static void *OpdPtr; + static uint8_t *OpdBuf; }; class PPCTargetInfo final : public TargetInfo { Index: ELF/Target.cpp =================================================================== --- ELF/Target.cpp +++ ELF/Target.cpp @@ -395,15 +395,28 @@ write32be(L, R); break; case R_PPC64_REL24: { + uint64_t PltStart = Out::Plt->getVA(); + uint64_t PltEnd = PltStart + Out::Plt->getSize(); + bool InPlt = PltStart <= S + A && S + A < PltEnd; + + if (!InPlt && OpdPtr) { + OutputSection *Opd = + static_cast *>(OpdPtr); + // If this is a local call, and we currently have the address of a + // function-descriptor, get the underlying code address instead. + uint64_t OpdStart = Opd->getVA(); + uint64_t OpdEnd = OpdStart + Opd->getSize(); + bool InOpd = OpdStart <= S + A && S + A < OpdEnd; + + if (InOpd) + R = read64be(&OpdBuf[S + A - OpdStart]); + } + uint32_t Mask = 0x03FFFFFC; if (!isInt<24>(R - P)) error("Relocation R_PPC64_REL24 overflow"); write32be(L, (read32be(L) & ~Mask) | ((R - P) & Mask)); - uint64_t PltStart = Out::Plt->getVA(); - uint64_t PltEnd = PltStart + Out::Plt->getSize(); - bool InPlt = PltStart <= S + A && S + A < PltEnd; - if (InPlt && L + 8 < BufEnd && read32be(L + 4) == 0x60000000 /* nop */) write32be(L + 4, 0xe8410028); // ld %r2, 40(%r1) @@ -425,6 +438,9 @@ } } +void *PPC64TargetInfo::OpdPtr; +uint8_t *PPC64TargetInfo::OpdBuf; + PPCTargetInfo::PPCTargetInfo() { // PCRelReloc = FIXME // GotReloc = FIXME Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -472,6 +472,12 @@ Out::StrTab->add(Sec->getName()); Sec->finalize(); } + + // If we have a .opd section (used under PPC64 for function descriptors), + // store a pointer to it here so that we can use it later when processing + // relocations. + PPC64TargetInfo::OpdPtr = + Map.lookup({".opd", SHT_PROGBITS, SHF_WRITE | SHF_ALLOC}); } template @@ -644,8 +650,18 @@ // Write section contents to a mmap'ed file. template void Writer::writeSections() { uint8_t *Buf = Buffer->getBufferStart(); + + // PPC64 needs to process relocations in the .opd section before processing + // relocations in code-containing sections. + for (OutputSectionBase *&Sec : OutputSections) + if (Sec->getName() == ".opd") { + PPC64TargetInfo::OpdBuf = Buf + Sec->getFileOff(); + Sec->writeTo(Buf + Sec->getFileOff()); + } + for (OutputSectionBase *Sec : OutputSections) - Sec->writeTo(Buf + Sec->getFileOff()); + if (Sec->getName() != ".opd") + Sec->writeTo(Buf + Sec->getFileOff()); } template Index: test/elf2/ppc64-rel-calls.s =================================================================== --- /dev/null +++ test/elf2/ppc64-rel-calls.s @@ -0,0 +1,42 @@ +# 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 + +# CHECK: Disassembly of section .text: + +.section ".opd","aw" +.global _start +_start: +.quad .Lfoo,.TOC.@tocbase,0 + +.text +.Lfoo: + li 0,1 + li 3,42 + sc + +# CHECK: 10010000: 38 00 00 01 li 0, 1 +# CHECK: 10010004: 38 60 00 2a li 3, 42 +# CHECK: 10010008: 44 00 00 02 sc + +.section ".opd","aw" +.global bar +bar: +.quad .Lbar,.TOC.@tocbase,0 + +.text +.Lbar: + bl _start + nop + bl .Lfoo + nop + blr + +# FIXME: The printing here is misleading, the branch offset here is negative. +# CHECK: 1001000c: 4b ff ff f5 bl .+67108852 +# CHECK: 10010010: 60 00 00 00 nop +# CHECK: 10010014: 4b ff ff ed bl .+67108844 +# CHECK: 10010018: 60 00 00 00 nop +# CHECK: 1001001c: 4e 80 00 20 blr +