Index: ELF/Target.h =================================================================== --- ELF/Target.h +++ ELF/Target.h @@ -10,6 +10,7 @@ #ifndef LLD_ELF_TARGET_H #define LLD_ELF_TARGET_H +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringRef.h" #include @@ -88,6 +89,11 @@ uint32_t Type, uint64_t BaseAddr, uint64_t SymVA) const override; bool isRelRelative(uint32_t Type) const override; + +protected: + // Record local .opd function-pointer-relocation results so they can be used + // to resolve local calls. + mutable llvm::DenseMap Addr64Relocs; }; class PPCTargetInfo final : public TargetInfo { Index: ELF/Target.cpp =================================================================== --- ELF/Target.cpp +++ ELF/Target.cpp @@ -396,7 +396,15 @@ case R_PPC64_REL24: { uint64_t PltStart = Out::Plt->getVA(); uint64_t PltEnd = PltStart + Out::Plt->getSize(); - bool ForPltEntry = S + A >= PltStart && S + A < PltEnd; + bool ForPltEntry = R >= PltStart && R < PltEnd; + + if (!ForPltEntry) { + // If this is a local call, and we currently have the address of a + // function-descriptor, get the underlying code address instead. + auto ARI = Addr64Relocs.find(R); + if (ARI != Addr64Relocs.end()) + R = ARI->second; + } uint32_t Mask = 0x03FFFFFC; if (!isInt<24>(R - P)) @@ -418,6 +426,10 @@ break; case R_PPC64_ADDR64: write64be(L, R); + + // Record this relocation so that we might find it later to resolve local + // calls. + Addr64Relocs[P] = R; break; default: error(Twine("unrecognized reloc ") + Twine(Type)); Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -651,6 +651,14 @@ // Write section contents to a mmap'ed file. template void Writer::writeSections() { + // PPC64 needs to process relocations in the .opd section before processing + // relocations in code-containing sections. + for (OutputSectionBase *&Sec : OutputSections) + if (Sec->getName() == ".opd") { + std::swap(OutputSections[0], Sec); + break; + } + uint8_t *Buf = Buffer->getBufferStart(); for (OutputSectionBase *Sec : OutputSections) Sec->writeTo(Buf + Sec->getFileOff()); 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 +