[ELF2/PPC64] Resolve local-call relocations using the correct function-descriptor values

Under PPC64 ELF v1 ABI, the symbols associated with each function name don't
point directly to the code in the .text section (or similar), but rather to a
function descriptor structure in a special data section named .opd. The
elements in the .opd structure include a pointer to the actual code, and a the
relevant TOC base value. Both of these are themselves set by relocations.

When we have a local call, we need the relevant relocation to refer directly to
the target code, not to the function-descriptor in the .opd section. Only when
we have a .plt stub do we care about the address of the .opd function
descriptor itself.

So we make a few changes here:

 1. Always write .opd first, so that its relocated data values are available
    for later use when writing the text sections. Record a pointer to the .opd
    structure, and its corresponding buffer.

 2. When processing a relative branch relocation under ppc64, if the
    destination points into the .opd section, read the code pointer out of the
    function descriptor structure and use that instead.

This this, I can link, and run, a dynamically-compiled "hello world"
application on big-Endian PPC64/Linux (ELF v1 ABI) using lld.

llvm-svn: 250122
This commit is contained in:
Hal Finkel 2015-10-12 23:16:53 +00:00
parent aa0ec7f45c
commit daedc12172
4 changed files with 77 additions and 5 deletions

View File

@ -288,6 +288,8 @@ template <class ELFT> struct Out {
static HashTableSection<ELFT> *HashTab;
static InterpSection<ELFT::Is64Bits> *Interp;
static OutputSection<ELFT> *Bss;
static OutputSection<ELFT> *Opd;
static uint8_t *OpdBuf;
static PltSection<ELFT> *Plt;
static RelocationSection<ELFT> *RelaDyn;
static StringTableSection<ELFT::Is64Bits> *DynStrTab;
@ -301,6 +303,8 @@ template <class ELFT> GotSection<ELFT> *Out<ELFT>::Got;
template <class ELFT> HashTableSection<ELFT> *Out<ELFT>::HashTab;
template <class ELFT> InterpSection<ELFT::Is64Bits> *Out<ELFT>::Interp;
template <class ELFT> OutputSection<ELFT> *Out<ELFT>::Bss;
template <class ELFT> OutputSection<ELFT> *Out<ELFT>::Opd;
template <class ELFT> uint8_t *Out<ELFT>::OpdBuf;
template <class ELFT> PltSection<ELFT> *Out<ELFT>::Plt;
template <class ELFT> RelocationSection<ELFT> *Out<ELFT>::RelaDyn;
template <class ELFT> StringTableSection<ELFT::Is64Bits> *Out<ELFT>::DynStrTab;

View File

@ -395,15 +395,26 @@ void PPC64TargetInfo::relocateOne(uint8_t *Buf, uint8_t *BufEnd,
write32be(L, R);
break;
case R_PPC64_REL24: {
uint64_t PltStart = Out<ELF64BE>::Plt->getVA();
uint64_t PltEnd = PltStart + Out<ELF64BE>::Plt->getSize();
bool InPlt = PltStart <= S + A && S + A < PltEnd;
if (!InPlt && Out<ELF64BE>::Opd) {
// 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 = Out<ELF64BE>::Opd->getVA();
uint64_t OpdEnd = OpdStart + Out<ELF64BE>::Opd->getSize();
bool InOpd = OpdStart <= S + A && S + A < OpdEnd;
if (InOpd)
R = read64be(&Out<ELF64BE>::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<ELF64BE>::Plt->getVA();
uint64_t PltEnd = PltStart + Out<ELF64BE>::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)

View File

@ -472,6 +472,11 @@ template <class ELFT> void Writer<ELFT>::createSections() {
Out<ELFT>::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.
Out<ELFT>::Opd = Map.lookup({".opd", SHT_PROGBITS, SHF_WRITE | SHF_ALLOC});
}
template <class ELFT>
@ -644,8 +649,18 @@ template <class ELFT> void Writer<ELFT>::openFile(StringRef Path) {
// Write section contents to a mmap'ed file.
template <class ELFT> void Writer<ELFT>::writeSections() {
uint8_t *Buf = Buffer->getBufferStart();
// PPC64 needs to process relocations in the .opd section before processing
// relocations in code-containing sections.
for (OutputSectionBase<ELFT::Is64Bits> *&Sec : OutputSections)
if (Sec->getName() == ".opd") {
Out<ELFT>::OpdBuf = Buf + Sec->getFileOff();
Sec->writeTo(Buf + Sec->getFileOff());
}
for (OutputSectionBase<ELFT::Is64Bits> *Sec : OutputSections)
Sec->writeTo(Buf + Sec->getFileOff());
if (Sec->getName() != ".opd")
Sec->writeTo(Buf + Sec->getFileOff());
}
template <class ELFT>

View File

@ -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