1//===- AArch64PLT.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#include "AArch64GOT.h"
10#include "AArch64PLT.h"
11#include "AArch64RelocationHelpers.h"
12
13#include "mcld/LD/LDSection.h"
14#include "mcld/Support/MsgHandling.h"
15
16#include <llvm/Support/Casting.h>
17
18#include <new>
19
20namespace mcld {
21
22AArch64PLT0::AArch64PLT0(SectionData& pParent)
23    : PLT::Entry<sizeof(aarch64_plt0)>(pParent) {
24}
25
26AArch64PLT1::AArch64PLT1(SectionData& pParent)
27    : PLT::Entry<sizeof(aarch64_plt1)>(pParent) {
28}
29
30//===----------------------------------------------------------------------===//
31// AArch64PLT
32
33AArch64PLT::AArch64PLT(LDSection& pSection, AArch64GOT& pGOTPLT)
34    : PLT(pSection), m_GOT(pGOTPLT) {
35  new AArch64PLT0(*m_pSectionData);
36}
37
38AArch64PLT::~AArch64PLT() {
39}
40
41bool AArch64PLT::hasPLT1() const {
42  return (m_pSectionData->size() > 1);
43}
44
45void AArch64PLT::finalizeSectionSize() {
46  uint64_t size = (m_pSectionData->size() - 1) * sizeof(aarch64_plt1) +
47                  sizeof(aarch64_plt0);
48  m_Section.setSize(size);
49
50  uint32_t offset = 0;
51  SectionData::iterator frag, fragEnd = m_pSectionData->end();
52  for (frag = m_pSectionData->begin(); frag != fragEnd; ++frag) {
53    frag->setOffset(offset);
54    offset += frag->size();
55  }
56}
57
58AArch64PLT1* AArch64PLT::create() {
59  AArch64PLT1* plt1_entry = new (std::nothrow) AArch64PLT1(*m_pSectionData);
60  if (!plt1_entry)
61    fatal(diag::fail_allocate_memory_plt);
62  return plt1_entry;
63}
64
65void AArch64PLT::applyPLT0() {
66  // malloc plt0
67  iterator first = m_pSectionData->getFragmentList().begin();
68  assert(first != m_pSectionData->getFragmentList().end() &&
69         "FragmentList is empty, applyPLT0 failed!");
70  AArch64PLT0* plt0 = &(llvm::cast<AArch64PLT0>(*first));
71  uint32_t* data = NULL;
72  data = static_cast<uint32_t*>(malloc(AArch64PLT0::EntrySize));
73  if (data == NULL)
74    fatal(diag::fail_allocate_memory_plt);
75  memcpy(data, aarch64_plt0, AArch64PLT0::EntrySize);
76
77  // apply plt0
78  uint64_t plt_base = m_Section.addr();
79  assert(plt_base && ".plt base address is NULL!");
80  uint64_t got_base = m_GOT.addr();
81  assert(got_base && ".got base address is NULL!");
82
83  // apply 2nd instruction
84  // get the address of got entry 2
85  uint64_t got_ent2_base = got_base + sizeof(AArch64GOTEntry::EntrySize) * 2;
86  // compute the immediate
87  AArch64Relocator::DWord imm =
88      helper_get_page_address(got_ent2_base) -
89      helper_get_page_address(plt_base + (sizeof(AArch64PLT0::EntrySize) * 8));
90  data[1] = helper_reencode_adr_imm(data[1], imm >> 12);
91  // apply 3rd instruction
92  data[2] = helper_reencode_add_imm(data[2],
93                                    helper_get_page_offset(got_ent2_base) >> 3);
94  // apply 4th instruction
95  data[3] =
96      helper_reencode_add_imm(data[3], helper_get_page_offset(got_ent2_base));
97  plt0->setValue(reinterpret_cast<unsigned char*>(data));
98}
99
100void AArch64PLT::applyPLT1() {
101  uint64_t plt_base = m_Section.addr();
102  assert(plt_base && ".plt base address is NULL!");
103
104  uint64_t got_base = m_GOT.addr();
105  assert(got_base && ".got base address is NULL!");
106
107  AArch64PLT::iterator it = m_pSectionData->begin();
108  AArch64PLT::iterator ie = m_pSectionData->end();
109  assert(it != ie && "FragmentList is empty, applyPLT1 failed!");
110
111  uint32_t GOTEntrySize = AArch64GOTEntry::EntrySize;
112  // first gotplt1 address
113  uint32_t GOTEntryAddress = got_base + GOTEntrySize * 3;
114  // first plt1 address
115  uint32_t PLTEntryAddress = plt_base + AArch64PLT0::EntrySize;
116
117  ++it;  // skip PLT0
118  uint32_t PLT1EntrySize = AArch64PLT1::EntrySize;
119  AArch64PLT1* plt1 = NULL;
120
121  uint32_t* Out = NULL;
122  while (it != ie) {
123    plt1 = &(llvm::cast<AArch64PLT1>(*it));
124    Out = static_cast<uint32_t*>(malloc(AArch64PLT1::EntrySize));
125    memcpy(Out, aarch64_plt1, AArch64PLT1::EntrySize);
126    // apply 1st instruction
127    AArch64Relocator::DWord imm = helper_get_page_address(GOTEntryAddress) -
128                                  helper_get_page_address(PLTEntryAddress);
129    Out[0] = helper_reencode_adr_imm(Out[0], imm >> 12);
130    // apply 2nd instruction
131    Out[1] = helper_reencode_add_imm(
132        Out[1], helper_get_page_offset(GOTEntryAddress) >> 3);
133    // apply 3rd instruction
134    Out[2] = helper_reencode_add_imm(Out[2],
135                                     helper_get_page_offset(GOTEntryAddress));
136
137    plt1->setValue(reinterpret_cast<unsigned char*>(Out));
138    ++it;
139
140    GOTEntryAddress += GOTEntrySize;
141    PLTEntryAddress += PLT1EntrySize;
142  }
143
144  m_GOT.applyGOTPLT(plt_base);
145}
146
147uint64_t AArch64PLT::emit(MemoryRegion& pRegion) {
148  uint64_t result = 0x0;
149  iterator it = begin();
150
151  unsigned char* buffer = pRegion.begin();
152  memcpy(buffer,
153         llvm::cast<AArch64PLT0>((*it)).getValue(),
154         AArch64PLT0::EntrySize);
155  result += AArch64PLT0::EntrySize;
156  ++it;
157
158  AArch64PLT1* plt1 = NULL;
159  AArch64PLT::iterator ie = end();
160  while (it != ie) {
161    plt1 = &(llvm::cast<AArch64PLT1>(*it));
162    memcpy(buffer + result, plt1->getValue(), AArch64PLT1::EntrySize);
163    result += AArch64PLT1::EntrySize;
164    ++it;
165  }
166  return result;
167}
168
169}  // namespace mcld
170