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