blob: 479abca77797c2e18e1be2c862d2d190fde7fa26 [file] [log] [blame]
Stephen Hinescfcb2242016-03-08 00:18:09 -08001//===- 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
23namespace mcld {
24
25//===----------------------------------------------------------------------===//
26// AArch64LongBranchStub
27//===----------------------------------------------------------------------===//
28const 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
37const uint32_t AArch64LongBranchStub::TEMPLATE[] = {
38 0x58000050, // ldr ip0, 0x8
39 0xd61f0200, // br ip0
40 0x00000000, // .xword <-- R_AARCH64_PREL64(X)
41 0x00000000,
42};
43
44const 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
50AArch64LongBranchStub::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
66AArch64LongBranchStub::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
78AArch64LongBranchStub::~AArch64LongBranchStub() {
79}
80
81bool 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
95static 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
102void 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
125const std::string& AArch64LongBranchStub::name() const {
126 return m_Name;
127}
128
129const uint8_t* AArch64LongBranchStub::getContent() const {
130 return reinterpret_cast<const uint8_t*>(m_pData);
131}
132
133size_t AArch64LongBranchStub::size() const {
134 return m_Size;
135}
136
137size_t AArch64LongBranchStub::alignment() const {
138 return 8;
139}
140
141Stub* AArch64LongBranchStub::doClone() {
142 return new AArch64LongBranchStub(m_pData, m_Size, fixup_begin(), fixup_end());
143}
144
145} // namespace mcld