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