ARMPLT.cpp revision 22add6ff3426df1a85089fe6a6e1597ee3b6f300
1//===- ARMPLT.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 "ARMGOT.h"
10#include "ARMPLT.h"
11
12#include <new>
13
14#include <llvm/Support/Casting.h>
15
16#include <mcld/LD/LDSection.h>
17#include <mcld/Support/MemoryRegion.h>
18#include <mcld/Support/MsgHandling.h>
19
20namespace {
21
22const uint32_t arm_plt0[] = {
23  0xe52de004, // str   lr, [sp, #-4]!
24  0xe59fe004, // ldr   lr, [pc, #4]
25  0xe08fe00e, // add   lr, pc, lr
26  0xe5bef008, // ldr   pc, [lr, #8]!
27  0x00000000, // &GOT[0] - .
28};
29
30const uint32_t arm_plt1[] = {
31  0xe28fc600, // add   ip, pc, #0xNN00000
32  0xe28cca00, // add   ip, ip, #0xNN000
33  0xe5bcf000, // ldr   pc, [ip, #0xNNN]!
34};
35
36} // anonymous namespace
37
38using namespace mcld;
39
40ARMPLT0::ARMPLT0(SectionData& pParent)
41  : PLT::Entry(sizeof(arm_plt0), pParent) {}
42
43ARMPLT1::ARMPLT1(SectionData& pParent)
44  : PLT::Entry(sizeof(arm_plt1), pParent) {}
45
46//===----------------------------------------------------------------------===//
47// ARMPLT
48
49ARMPLT::ARMPLT(LDSection& pSection,
50               ARMGOT &pGOTPLT)
51  : PLT(pSection), m_GOT(pGOTPLT), m_PLTEntryIterator() {
52  new ARMPLT0(*m_SectionData);
53  m_PLTEntryIterator = m_SectionData->begin();
54}
55
56ARMPLT::~ARMPLT()
57{
58}
59
60bool ARMPLT::hasPLT1() const
61{
62  return (m_SectionData->size() > 1);
63}
64
65void ARMPLT::finalizeSectionSize()
66{
67  uint64_t size = (m_SectionData->size() - 1) * sizeof(arm_plt1) +
68                     sizeof(arm_plt0);
69  m_Section.setSize(size);
70
71  uint32_t offset = 0;
72  SectionData::iterator frag, fragEnd = m_SectionData->end();
73  for (frag = m_SectionData->begin(); frag != fragEnd; ++frag) {
74    frag->setOffset(offset);
75    offset += frag->size();
76  }
77}
78
79void ARMPLT::reserveEntry(size_t pNum)
80{
81  ARMPLT1* plt1_entry = 0;
82
83  for (size_t i = 0; i < pNum; ++i) {
84    plt1_entry = new (std::nothrow) ARMPLT1(*m_SectionData);
85
86    if (!plt1_entry)
87      fatal(diag::fail_allocate_memory_plt);
88
89    m_GOT.reserveGOTPLT();
90  }
91}
92
93ARMPLT1* ARMPLT::consume()
94{
95  ++m_PLTEntryIterator;
96  assert(m_PLTEntryIterator != m_SectionData->end() &&
97         "The number of PLT Entries and ResolveInfo doesn't match");
98
99  return llvm::cast<ARMPLT1>(&(*m_PLTEntryIterator));
100}
101
102ARMPLT0* ARMPLT::getPLT0() const {
103
104  iterator first = m_SectionData->getFragmentList().begin();
105
106  assert(first != m_SectionData->getFragmentList().end() &&
107         "FragmentList is empty, getPLT0 failed!");
108
109  ARMPLT0* plt0 = &(llvm::cast<ARMPLT0>(*first));
110
111  return plt0;
112}
113
114void ARMPLT::applyPLT0() {
115
116  uint64_t plt_base = m_Section.addr();
117  assert(plt_base && ".plt base address is NULL!");
118
119  uint64_t got_base = m_GOT.addr();
120  assert(got_base && ".got base address is NULL!");
121
122  uint32_t offset = 0;
123
124  if (got_base > plt_base)
125    offset = got_base - (plt_base + 16);
126  else
127    offset = (plt_base + 16) - got_base;
128
129  iterator first = m_SectionData->getFragmentList().begin();
130
131  assert(first != m_SectionData->getFragmentList().end() &&
132         "FragmentList is empty, applyPLT0 failed!");
133
134  ARMPLT0* plt0 = &(llvm::cast<ARMPLT0>(*first));
135
136  uint32_t* data = 0;
137  data = static_cast<uint32_t*>(malloc(plt0->getEntrySize()));
138
139  if (!data)
140    fatal(diag::fail_allocate_memory_plt);
141
142  memcpy(data, arm_plt0, plt0->getEntrySize());
143  data[4] = offset;
144
145  plt0->setContent(reinterpret_cast<unsigned char*>(data));
146}
147
148void ARMPLT::applyPLT1() {
149
150  uint64_t plt_base = m_Section.addr();
151  assert(plt_base && ".plt base address is NULL!");
152
153  uint64_t got_base = m_GOT.addr();
154  assert(got_base && ".got base address is NULL!");
155
156  ARMPLT::iterator it = m_SectionData->begin();
157  ARMPLT::iterator ie = m_SectionData->end();
158  assert(it != ie && "FragmentList is empty, applyPLT1 failed!");
159
160  uint32_t GOTEntrySize = m_GOT.getEntrySize();
161  uint32_t GOTEntryAddress =
162    got_base +  GOTEntrySize * 3;
163
164  uint64_t PLTEntryAddress =
165    plt_base + llvm::cast<ARMPLT0>((*it)).getEntrySize(); //Offset of PLT0
166
167  ++it; //skip PLT0
168  uint64_t PLT1EntrySize = llvm::cast<ARMPLT1>((*it)).getEntrySize();
169  ARMPLT1* plt1 = NULL;
170
171  uint32_t* Out = NULL;
172  while (it != ie) {
173    plt1 = &(llvm::cast<ARMPLT1>(*it));
174    Out = static_cast<uint32_t*>(malloc(plt1->getEntrySize()));
175
176    if (!Out)
177      fatal(diag::fail_allocate_memory_plt);
178
179    // Offset is the distance between the last PLT entry and the associated
180    // GOT entry.
181    int32_t Offset = (GOTEntryAddress - (PLTEntryAddress + 8));
182
183    Out[0] = arm_plt1[0] | ((Offset >> 20) & 0xFF);
184    Out[1] = arm_plt1[1] | ((Offset >> 12) & 0xFF);
185    Out[2] = arm_plt1[2] | (Offset & 0xFFF);
186
187    plt1->setContent(reinterpret_cast<unsigned char*>(Out));
188    ++it;
189
190    GOTEntryAddress += GOTEntrySize;
191    PLTEntryAddress += PLT1EntrySize;
192  }
193
194  m_GOT.applyGOTPLT(plt_base);
195}
196
197uint64_t ARMPLT::emit(MemoryRegion& pRegion)
198{
199  uint64_t result = 0x0;
200  iterator it = begin();
201  unsigned int plt0_size = llvm::cast<ARMPLT0>((*it)).getEntrySize();
202
203  unsigned char* buffer = pRegion.getBuffer();
204  memcpy(buffer, llvm::cast<ARMPLT0>((*it)).getContent(), plt0_size);
205  result += plt0_size;
206  ++it;
207
208  ARMPLT1* plt1 = 0;
209  ARMPLT::iterator ie = end();
210  unsigned int entry_size = 0;
211  while (it != ie) {
212    plt1 = &(llvm::cast<ARMPLT1>(*it));
213    entry_size = plt1->getEntrySize();
214    memcpy(buffer + result, plt1->getContent(), entry_size);
215    result += entry_size;
216    ++it;
217  }
218  return result;
219}
220
221