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