Home | History | Annotate | Download | only in AArch64
      1 //===- AArch64LongBranchStub.cpp ------------------------------------------===//
      2 //
      3 //                     The MCLinker Project
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 
     10 #include "AArch64LongBranchStub.h"
     11 #include "AArch64LDBackend.h"
     12 #include "AArch64RelocationHelpers.h"
     13 
     14 #include "mcld/Fragment/Relocation.h"
     15 #include "mcld/LD/BranchIsland.h"
     16 #include "mcld/LD/LDSymbol.h"
     17 #include "mcld/LD/ResolveInfo.h"
     18 
     19 #include <llvm/Support/ELF.h>
     20 
     21 #include <cassert>
     22 
     23 namespace mcld {
     24 
     25 //===----------------------------------------------------------------------===//
     26 // AArch64LongBranchStub
     27 //===----------------------------------------------------------------------===//
     28 const uint32_t AArch64LongBranchStub::PIC_TEMPLATE[] = {
     29   0x58000090,  // ldr   ip0, 0x10
     30   0x10000011,  // adr   ip1, #0
     31   0x8b110210,  // add   ip0, ip0, ip1
     32   0xd61f0200,  // br    ip0
     33   0x00000000,  // .xword <-- R_AARCH64_PREL64(X+12)
     34   0x00000000,
     35 };
     36 
     37 const uint32_t AArch64LongBranchStub::TEMPLATE[] = {
     38   0x58000050,  // ldr   ip0, 0x8
     39   0xd61f0200,  // br    ip0
     40   0x00000000,  // .xword <-- R_AARCH64_PREL64(X)
     41   0x00000000,
     42 };
     43 
     44 const uint32_t AArch64LongBranchStub::ADRP_TEMPLATE[] = {
     45   0x90000010,  // adrp  ip0, X <-- R_AARCH64_ADR_PREL_PG_HI21(X)
     46   0x91000210,  // add   ip0, ip0, :lo12:X <-- R_AARCH64_ADD_ABS_LO12_NC(X)
     47   0xd61f0200,  // br    ip0
     48 };
     49 
     50 AArch64LongBranchStub::AArch64LongBranchStub(bool pIsOutputPIC)
     51     : m_pData(NULL),
     52       m_Name("ljmp_prototype"),
     53       m_Size(0x0) {
     54   if (pIsOutputPIC) {
     55     m_pData = PIC_TEMPLATE;
     56     m_Size = sizeof(PIC_TEMPLATE);
     57     addFixup(0x10, 12, llvm::ELF::R_AARCH64_PREL64);
     58   } else {
     59     m_pData = TEMPLATE;
     60     m_Size = sizeof(TEMPLATE);
     61     addFixup(0x8, 0, llvm::ELF::R_AARCH64_PREL64);
     62   }
     63 }
     64 
     65 /// for doClone
     66 AArch64LongBranchStub::AArch64LongBranchStub(const uint32_t* pData,
     67                                              size_t pSize,
     68                                              const_fixup_iterator pBegin,
     69                                              const_fixup_iterator pEnd)
     70     : m_pData(pData),
     71       m_Name("ljmp_veneer"),
     72       m_Size(pSize) {
     73   for (const_fixup_iterator it = pBegin, ie = pEnd; it != ie; ++it) {
     74     addFixup(**it);
     75   }
     76 }
     77 
     78 AArch64LongBranchStub::~AArch64LongBranchStub() {
     79 }
     80 
     81 bool AArch64LongBranchStub::isMyDuty(const Relocation& pReloc,
     82                                      uint64_t pSource,
     83                                      uint64_t pTargetSymValue) const {
     84   assert((pReloc.type() == llvm::ELF::R_AARCH64_CALL26) ||
     85          (pReloc.type() == llvm::ELF::R_AARCH64_JUMP26));
     86   int64_t dest = pTargetSymValue + pReloc.addend();
     87   int64_t branch_offset = dest - pSource;
     88   if ((branch_offset > AArch64GNULDBackend::MAX_FWD_BRANCH_OFFSET) ||
     89       (branch_offset < AArch64GNULDBackend::MAX_BWD_BRANCH_OFFSET)) {
     90     return true;
     91   }
     92   return false;
     93 }
     94 
     95 static bool isValidForADRP(uint64_t pSource, uint64_t pDest) {
     96   int64_t imm = static_cast<int64_t>((helper_get_page_address(pDest) -
     97                                       helper_get_page_address(pSource))) >> 12;
     98   return ((imm <= AArch64GNULDBackend::MAX_ADRP_IMM) &&
     99           (imm >= AArch64GNULDBackend::MIN_ADRP_IMM));
    100 }
    101 
    102 void AArch64LongBranchStub::applyFixup(Relocation& pSrcReloc,
    103                                        IRBuilder& pBuilder,
    104                                        BranchIsland& pIsland) {
    105   // Try to relax the stub itself.
    106   LDSymbol* symbol = pSrcReloc.symInfo()->outSymbol();
    107   uint64_t dest = symbol->fragRef()->frag()->getParent()->getSection().addr() +
    108                   symbol->fragRef()->getOutputOffset();
    109   uint64_t src = pIsland.getParent()->getSection().addr() +
    110                  pIsland.offset() +
    111                  pIsland.size();
    112   if (isValidForADRP(src, dest)) {
    113     m_pData = ADRP_TEMPLATE;
    114     m_Name = "adrp_veneer";
    115     m_Size = sizeof(ADRP_TEMPLATE);
    116 
    117     getFixupList().clear();
    118     addFixup(0x0, 0, llvm::ELF::R_AARCH64_ADR_PREL_PG_HI21);
    119     addFixup(0x4, 0, llvm::ELF::R_AARCH64_ADD_ABS_LO12_NC);
    120   }
    121 
    122   Stub::applyFixup(pSrcReloc, pBuilder, pIsland);
    123 }
    124 
    125 const std::string& AArch64LongBranchStub::name() const {
    126   return m_Name;
    127 }
    128 
    129 const uint8_t* AArch64LongBranchStub::getContent() const {
    130   return reinterpret_cast<const uint8_t*>(m_pData);
    131 }
    132 
    133 size_t AArch64LongBranchStub::size() const {
    134   return m_Size;
    135 }
    136 
    137 size_t AArch64LongBranchStub::alignment() const {
    138   return 8;
    139 }
    140 
    141 Stub* AArch64LongBranchStub::doClone() {
    142   return new AArch64LongBranchStub(m_pData, m_Size, fixup_begin(), fixup_end());
    143 }
    144 
    145 }  // namespace mcld
    146