From daedc1217297315437a4e26e236e7c2069156f51 Mon Sep 17 00:00:00 2001 From: Hal Finkel Date: Mon, 12 Oct 2015 23:16:53 +0000 Subject: [PATCH] [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 --- lld/ELF/OutputSections.h | 4 ++++ lld/ELF/Target.cpp | 19 +++++++++++---- lld/ELF/Writer.cpp | 17 ++++++++++++- lld/test/elf2/ppc64-rel-calls.s | 42 +++++++++++++++++++++++++++++++++ 4 files changed, 77 insertions(+), 5 deletions(-) create mode 100644 lld/test/elf2/ppc64-rel-calls.s diff --git a/lld/ELF/OutputSections.h b/lld/ELF/OutputSections.h index cf6b2e9f7fc5..a8c597b03bf6 100644 --- a/lld/ELF/OutputSections.h +++ b/lld/ELF/OutputSections.h @@ -288,6 +288,8 @@ template struct Out { static HashTableSection *HashTab; static InterpSection *Interp; static OutputSection *Bss; + static OutputSection *Opd; + static uint8_t *OpdBuf; static PltSection *Plt; static RelocationSection *RelaDyn; static StringTableSection *DynStrTab; @@ -301,6 +303,8 @@ template GotSection *Out::Got; template HashTableSection *Out::HashTab; template InterpSection *Out::Interp; template OutputSection *Out::Bss; +template OutputSection *Out::Opd; +template uint8_t *Out::OpdBuf; template PltSection *Out::Plt; template RelocationSection *Out::RelaDyn; template StringTableSection *Out::DynStrTab; diff --git a/lld/ELF/Target.cpp b/lld/ELF/Target.cpp index 6f84ada243a7..838ba790b34e 100644 --- a/lld/ELF/Target.cpp +++ b/lld/ELF/Target.cpp @@ -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::Plt->getVA(); + uint64_t PltEnd = PltStart + Out::Plt->getSize(); + bool InPlt = PltStart <= S + A && S + A < PltEnd; + + if (!InPlt && Out::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::Opd->getVA(); + uint64_t OpdEnd = OpdStart + Out::Opd->getSize(); + bool InOpd = OpdStart <= S + A && S + A < OpdEnd; + + if (InOpd) + R = read64be(&Out::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) diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index fbf8d6b42e5d..ce25625fd768 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -472,6 +472,11 @@ template void Writer::createSections() { 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. + Out::Opd = Map.lookup({".opd", SHT_PROGBITS, SHF_WRITE | SHF_ALLOC}); } template @@ -644,8 +649,18 @@ template void Writer::openFile(StringRef Path) { // 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") { + Out::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 diff --git a/lld/test/elf2/ppc64-rel-calls.s b/lld/test/elf2/ppc64-rel-calls.s new file mode 100644 index 000000000000..6a5cb12d1a41 --- /dev/null +++ b/lld/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 +