[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