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 
AArch64LongBranchStub(bool pIsOutputPIC)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
AArch64LongBranchStub(const uint32_t * pData,size_t pSize,const_fixup_iterator pBegin,const_fixup_iterator pEnd)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 
~AArch64LongBranchStub()78 AArch64LongBranchStub::~AArch64LongBranchStub() {
79 }
80 
isMyDuty(const Relocation & pReloc,uint64_t pSource,uint64_t pTargetSymValue) const81 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 
isValidForADRP(uint64_t pSource,uint64_t pDest)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 
applyFixup(Relocation & pSrcReloc,IRBuilder & pBuilder,BranchIsland & pIsland)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 
name() const125 const std::string& AArch64LongBranchStub::name() const {
126   return m_Name;
127 }
128 
getContent() const129 const uint8_t* AArch64LongBranchStub::getContent() const {
130   return reinterpret_cast<const uint8_t*>(m_pData);
131 }
132 
size() const133 size_t AArch64LongBranchStub::size() const {
134   return m_Size;
135 }
136 
alignment() const137 size_t AArch64LongBranchStub::alignment() const {
138   return 8;
139 }
140 
doClone()141 Stub* AArch64LongBranchStub::doClone() {
142   return new AArch64LongBranchStub(m_pData, m_Size, fixup_begin(), fixup_end());
143 }
144 
145 }  // namespace mcld
146