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 
20 namespace mcld {
21 
AArch64PLT0(SectionData & pParent)22 AArch64PLT0::AArch64PLT0(SectionData& pParent)
23     : PLT::Entry<sizeof(aarch64_plt0)>(pParent) {
24 }
25 
AArch64PLT1(SectionData & pParent)26 AArch64PLT1::AArch64PLT1(SectionData& pParent)
27     : PLT::Entry<sizeof(aarch64_plt1)>(pParent) {
28 }
29 
30 //===----------------------------------------------------------------------===//
31 // AArch64PLT
32 
AArch64PLT(LDSection & pSection,AArch64GOT & pGOTPLT)33 AArch64PLT::AArch64PLT(LDSection& pSection, AArch64GOT& pGOTPLT)
34     : PLT(pSection), m_GOT(pGOTPLT) {
35   new AArch64PLT0(*m_pSectionData);
36 }
37 
~AArch64PLT()38 AArch64PLT::~AArch64PLT() {
39 }
40 
hasPLT1() const41 bool AArch64PLT::hasPLT1() const {
42   return (m_pSectionData->size() > 1);
43 }
44 
finalizeSectionSize()45 void 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 
create()58 AArch64PLT1* 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 
applyPLT0()65 void 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 
applyPLT1()100 void 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 
emit(MemoryRegion & pRegion)147 uint64_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