[lld] r250101 - [ELF2] Add a base set of PPC64 relocations
Hal Finkel via llvm-commits
llvm-commits at lists.llvm.org
Mon Oct 12 13:56:18 PDT 2015
Author: hfinkel
Date: Mon Oct 12 15:56:18 2015
New Revision: 250101
URL: http://llvm.org/viewvc/llvm-project?rev=250101&view=rev
Log:
[ELF2] Add a base set of PPC64 relocations
This is mostly an adaptation of the code in LLVM's
lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp, and handles a sufficient
number of relocations to link a 'hello world' program on big-Endian PPC64/Linux
(ELF v1 ABI).
Added:
lld/trunk/test/elf2/ppc64-relocs.s
Modified:
lld/trunk/ELF/Target.cpp
Modified: lld/trunk/ELF/Target.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Target.cpp?rev=250101&r1=250100&r2=250101&view=diff
==============================================================================
--- lld/trunk/ELF/Target.cpp (original)
+++ lld/trunk/ELF/Target.cpp Mon Oct 12 15:56:18 2015
@@ -196,9 +196,31 @@ void X86_64TargetInfo::relocateOne(uint8
}
}
+// 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 uint16_t applyPPCLo(uint64_t V) { return V & 0xffff; }
+
+static uint16_t applyPPCHi(uint64_t V) { return (V >> 16) & 0xffff; }
+
+static uint16_t applyPPCHa(uint64_t V) { return ((V + 0x8000) >> 16) & 0xffff; }
+
+static uint16_t applyPPCHigher(uint64_t V) { return (V >> 32) & 0xffff; }
+
+static uint16_t applyPPCHighera(uint64_t V) {
+ return ((V + 0x8000) >> 32) & 0xffff;
+}
+
+static uint16_t applyPPCHighest(uint64_t V) { return V >> 48; }
+
+static uint16_t applyPPCHighesta(uint64_t V) { return (V + 0x8000) >> 48; }
+
PPC64TargetInfo::PPC64TargetInfo() {
- // PCRelReloc = FIXME
- // GotReloc = FIXME
+ PCRelReloc = R_PPC64_REL24;
+ GotReloc = R_PPC64_GLOB_DAT;
+ GotRefReloc = R_PPC64_REL64;
PltEntrySize = 32;
// We need 64K pages (at least under glibc/Linux, the loader won't
@@ -207,27 +229,171 @@ PPC64TargetInfo::PPC64TargetInfo() {
VAStart = 0x10000000;
}
+
+static uint64_t getPPC64TocBase() {
+ // 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, but there is a .toc or .tocbss section.
+ uint64_t TocVA = Out<ELF64BE>::Got->getVA();
+ if (!TocVA)
+ TocVA = Out<ELF64BE>::Plt->getVA();
+
+ // 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.
+ return TocVA + 0x8000;
+}
+
void PPC64TargetInfo::writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr,
- uint64_t PltEntryAddr) const {}
+ uint64_t PltEntryAddr) const {
+ uint64_t Off = GotEntryAddr - getPPC64TocBase();
+
+ // 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.
+
+ write32be(Buf, 0xf8410000); // std %r2, 40(%r1)
+ write32be(Buf + 4, 0x3d620000 | applyPPCHa(Off)); // addis %r11, %r2, X at ha
+ write32be(Buf + 8, 0xe98b0000 | applyPPCLo(Off)); // ld %r12, X at l(%r11)
+ write32be(Buf + 12, 0xe96c0000); // ld %r11,0(%r12)
+ write32be(Buf + 16, 0x7d6903a6); // mtctr %r11
+ write32be(Buf + 20, 0xe84c0008); // ld %r2,8(%r12)
+ write32be(Buf + 24, 0xe96c0010); // ld %r11,16(%r12)
+ write32be(Buf + 28, 0x4e800420); // bctr
+}
+
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;
+ if (Type != R_PPC64_REL24)
+ return false;
+
+ // 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) const {
typedef ELFFile<ELF64BE>::Elf_Rela Elf_Rela;
auto &Rel = *reinterpret_cast<const Elf_Rela *>(RelP);
- uint64_t Offset = Rel.r_offset;
- uint8_t *Loc = Buf + Offset;
+ uint8_t *L = Buf + Rel.r_offset;
+ uint64_t S = SymVA;
+ int64_t A = Rel.r_addend;
+ uint64_t P = BaseAddr + Rel.r_offset;
+ uint64_t TB = getPPC64TocBase();
+
+ if (Type == R_PPC64_TOC) {
+ write64be(L, TB);
+ return;
+ }
+
+ // For a TOC-relative relocation, adjust the addend and proceed in terms of
+ // the corresponding ADDR16 relocation type.
switch (Type) {
- case R_PPC64_ADDR64:
- write64be(Loc, SymVA + Rel.r_addend);
+ case R_PPC64_TOC16: Type = R_PPC64_ADDR16; A -= TB; break;
+ case R_PPC64_TOC16_DS: Type = R_PPC64_ADDR16_DS; A -= TB; break;
+ case R_PPC64_TOC16_LO: Type = R_PPC64_ADDR16_LO; A -= TB; break;
+ case R_PPC64_TOC16_LO_DS: Type = R_PPC64_ADDR16_LO_DS; A -= TB; break;
+ case R_PPC64_TOC16_HI: Type = R_PPC64_ADDR16_HI; A -= TB; break;
+ case R_PPC64_TOC16_HA: Type = R_PPC64_ADDR16_HA; A -= TB; break;
+ default: break;
+ }
+
+ uint64_t R = S + A;
+
+ switch (Type) {
+ case R_PPC64_ADDR16:
+ write16be(L, applyPPCLo(R));
+ break;
+ case R_PPC64_ADDR16_DS:
+ if (!isInt<16>(R))
+ error("Relocation R_PPC64_ADDR16_DS overflow");
+ write16be(L, (read16be(L) & 3) | (R & ~3));
+ break;
+ case R_PPC64_ADDR16_LO:
+ write16be(L, applyPPCLo(R));
+ break;
+ case R_PPC64_ADDR16_LO_DS:
+ write16be(L, (read16be(L) & 3) | (applyPPCLo(R) & ~3));
+ break;
+ case R_PPC64_ADDR16_HI:
+ write16be(L, applyPPCHi(R));
+ break;
+ case R_PPC64_ADDR16_HA:
+ write16be(L, applyPPCHa(R));
+ break;
+ case R_PPC64_ADDR16_HIGHER:
+ write16be(L, applyPPCHigher(R));
+ break;
+ case R_PPC64_ADDR16_HIGHERA:
+ write16be(L, applyPPCHighera(R));
+ break;
+ case R_PPC64_ADDR16_HIGHEST:
+ write16be(L, applyPPCHighest(R));
break;
- case R_PPC64_TOC:
- // We don't create a TOC yet.
+ case R_PPC64_ADDR16_HIGHESTA:
+ write16be(L, applyPPCHighesta(R));
+ break;
+ case R_PPC64_ADDR14: {
+ if ((R & 3) != 0)
+ error("Improper alignment for relocation R_PPC64_ADDR14");
+
+ // Preserve the AA/LK bits in the branch instruction
+ uint8_t AALK = L[3];
+ write16be(L + 2, (AALK & 3) | (R & 0xfffc));
+ break;
+ }
+ case R_PPC64_REL16_LO:
+ write16be(L, applyPPCLo(R - P));
+ break;
+ case R_PPC64_REL16_HI:
+ write16be(L, applyPPCHi(R - P));
+ break;
+ case R_PPC64_REL16_HA:
+ write16be(L, applyPPCHa(R - P));
+ break;
+ case R_PPC64_ADDR32:
+ if (!isInt<32>(R))
+ error("Relocation R_PPC64_ADDR32 overflow");
+ write32be(L, R);
+ break;
+ case R_PPC64_REL24: {
+ uint32_t Mask = 0x03FFFFFC;
+ if (!isInt<24>(R - P))
+ error("Relocation R_PPC64_REL24 overflow");
+ write32be(L, (read32be(L) & ~Mask) | ((R - P) & Mask));
+ break;
+ }
+ case R_PPC64_REL32:
+ if (!isInt<32>(R - P))
+ error("Relocation R_PPC64_REL32 overflow");
+ write32be(L, R - P);
+ break;
+ case R_PPC64_REL64:
+ write64be(L, R - P);
+ break;
+ case R_PPC64_ADDR64:
+ write64be(L, R);
break;
default:
error("unrecognized reloc " + Twine(Type));
Added: lld/trunk/test/elf2/ppc64-relocs.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/elf2/ppc64-relocs.s?rev=250101&view=auto
==============================================================================
--- lld/trunk/test/elf2/ppc64-relocs.s (added)
+++ lld/trunk/test/elf2/ppc64-relocs.s Mon Oct 12 15:56:18 2015
@@ -0,0 +1,130 @@
+# 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
+
+.section ".opd","aw"
+.global _start
+_start:
+.quad .Lfoo,.TOC. at tocbase,0
+
+.text
+.Lfoo:
+ li 0,1
+ li 3,42
+ sc
+
+.section ".toc","aw"
+.L1:
+.quad 22, 37, 89, 47
+
+.section .R_PPC64_TOC16_LO_DS,"ax", at progbits
+.globl .FR_PPC64_TOC16_LO_DS
+.FR_PPC64_TOC16_LO_DS:
+ ld 1, .L1 at toc@l(2)
+
+# CHECK: Disassembly of section .R_PPC64_TOC16_LO_DS:
+# CHECK: .FR_PPC64_TOC16_LO_DS:
+# CHECK: 1001000c: e8 22 80 00 ld 1, -32768(2)
+
+.section .R_PPC64_TOC16_LO,"ax", at progbits
+.globl .FR_PPC64_TOC16_LO
+.FR_PPC64_TOC16_LO:
+ addi 1, 2, .L1 at toc@l
+
+# CHECK: Disassembly of section .R_PPC64_TOC16_LO:
+# CHECK: .FR_PPC64_TOC16_LO:
+# CHECK: 10010010: 38 22 80 00 addi 1, 2, -32768
+
+.section .R_PPC64_TOC16_HI,"ax", at progbits
+.globl .FR_PPC64_TOC16_HI
+.FR_PPC64_TOC16_HI:
+ addis 1, 2, .L1 at toc@h
+
+# CHECK: Disassembly of section .R_PPC64_TOC16_HI:
+# CHECK: .FR_PPC64_TOC16_HI:
+# CHECK: 10010014: 3c 22 10 01 addis 1, 2, 4097
+
+.section .R_PPC64_TOC16_HA,"ax", at progbits
+.globl .FR_PPC64_TOC16_HA
+.FR_PPC64_TOC16_HA:
+ addis 1, 2, .L1 at toc@ha
+
+# CHECK: Disassembly of section .R_PPC64_TOC16_HA:
+# CHECK: .FR_PPC64_TOC16_HA:
+# CHECK: 10010018: 3c 22 10 02 addis 1, 2, 4098
+
+.section .R_PPC64_REL24,"ax", at progbits
+.globl .FR_PPC64_REL24
+.FR_PPC64_REL24:
+ b .Lfoox
+.section .R_PPC64_REL24_2,"ax", at progbits
+.Lfoox:
+
+# CHECK: Disassembly of section .R_PPC64_REL24:
+# CHECK: .FR_PPC64_REL24:
+# CHECK: 1001001c: 48 00 00 04 b .+4
+
+.section .R_PPC64_ADDR16_LO,"ax", at progbits
+.globl .FR_PPC64_ADDR16_LO
+.FR_PPC64_ADDR16_LO:
+ li 1, .Lfoo at l
+
+# CHECK: Disassembly of section .R_PPC64_ADDR16_LO:
+# CHECK: .FR_PPC64_ADDR16_LO:
+# CHECK: 10010020: 38 20 00 00 li 1, 0
+
+.section .R_PPC64_ADDR16_HI,"ax", at progbits
+.globl .FR_PPC64_ADDR16_HI
+.FR_PPC64_ADDR16_HI:
+ li 1, .Lfoo at h
+
+# CHECK: Disassembly of section .R_PPC64_ADDR16_HI:
+# CHECK: .FR_PPC64_ADDR16_HI:
+# CHECK: 10010024: 38 20 10 01 li 1, 4097
+
+.section .R_PPC64_ADDR16_HA,"ax", at progbits
+.globl .FR_PPC64_ADDR16_HA
+.FR_PPC64_ADDR16_HA:
+ li 1, .Lfoo at ha
+
+# CHECK: Disassembly of section .R_PPC64_ADDR16_HA:
+# CHECK: .FR_PPC64_ADDR16_HA:
+# CHECK: 10010028: 38 20 10 01 li 1, 4097
+
+.section .R_PPC64_ADDR16_HIGHER,"ax", at progbits
+.globl .FR_PPC64_ADDR16_HIGHER
+.FR_PPC64_ADDR16_HIGHER:
+ li 1, .Lfoo at higher
+
+# CHECK: Disassembly of section .R_PPC64_ADDR16_HIGHER:
+# CHECK: .FR_PPC64_ADDR16_HIGHER:
+# CHECK: 1001002c: 38 20 00 00 li 1, 0
+
+.section .R_PPC64_ADDR16_HIGHERA,"ax", at progbits
+.globl .FR_PPC64_ADDR16_HIGHERA
+.FR_PPC64_ADDR16_HIGHERA:
+ li 1, .Lfoo at highera
+
+# CHECK: Disassembly of section .R_PPC64_ADDR16_HIGHERA:
+# CHECK: .FR_PPC64_ADDR16_HIGHERA:
+# CHECK: 10010030: 38 20 00 00 li 1, 0
+
+.section .R_PPC64_ADDR16_HIGHEST,"ax", at progbits
+.globl .FR_PPC64_ADDR16_HIGHEST
+.FR_PPC64_ADDR16_HIGHEST:
+ li 1, .Lfoo at highest
+
+# CHECK: Disassembly of section .R_PPC64_ADDR16_HIGHEST:
+# CHECK: .FR_PPC64_ADDR16_HIGHEST:
+# CHECK: 10010034: 38 20 00 00 li 1, 0
+
+.section .R_PPC64_ADDR16_HIGHESTA,"ax", at progbits
+.globl .FR_PPC64_ADDR16_HIGHESTA
+.FR_PPC64_ADDR16_HIGHESTA:
+ li 1, .Lfoo at highesta
+
+# CHECK: Disassembly of section .R_PPC64_ADDR16_HIGHESTA:
+# CHECK: .FR_PPC64_ADDR16_HIGHESTA:
+# CHECK: 10010038: 38 20 00 00 li 1, 0
+
More information about the llvm-commits
mailing list