1//===- EhFrameHdr.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 <mcld/LD/EhFrameHdr.h>
10
11#include <mcld/LD/EhFrame.h>
12#include <mcld/LD/LDSection.h>
13
14#include <llvm/Support/Dwarf.h>
15#include <llvm/Support/DataTypes.h>
16
17#include <algorithm>
18#include <cstring>
19
20using namespace mcld;
21using namespace llvm::dwarf;
22
23//===----------------------------------------------------------------------===//
24// Helper Function
25//===----------------------------------------------------------------------===//
26namespace bit32 {
27
28typedef std::pair<SizeTraits<32>::Address, SizeTraits<32>::Address> Entry;
29
30bool EntryCompare(const Entry& pX, const Entry& pY)
31{ return (pX.first < pY.first); }
32
33} // bit32 namespace
34
35//===----------------------------------------------------------------------===//
36// Template Specification Functions
37//===----------------------------------------------------------------------===//
38/// emitOutput<32> - write out eh_frame_hdr
39template<>
40void EhFrameHdr::emitOutput<32>(FileOutputBuffer& pOutput)
41{
42  MemoryRegion ehframehdr_region = pOutput.request(m_EhFrameHdr.offset(),
43                                                   m_EhFrameHdr.size());
44
45  MemoryRegion ehframe_region = pOutput.request(m_EhFrame.offset(),
46                                                m_EhFrame.size());
47
48  uint8_t* data = ehframehdr_region.begin();
49  // version
50  data[0] = 1;
51  // eh_frame_ptr_enc
52  data[1] = DW_EH_PE_pcrel | DW_EH_PE_sdata4;
53
54  // eh_frame_ptr
55  uint32_t* eh_frame_ptr = (uint32_t*)(data + 4);
56  *eh_frame_ptr = m_EhFrame.addr() - (m_EhFrameHdr.addr() + 4);
57
58  // fde_count
59  uint32_t* fde_count = (uint32_t*)(data + 8);
60  if (m_EhFrame.hasEhFrame())
61    *fde_count = m_EhFrame.getEhFrame()->numOfFDEs();
62  else
63    *fde_count = 0;
64
65  if (0 == *fde_count) {
66    // fde_count_enc
67    data[2] = DW_EH_PE_omit;
68    // table_enc
69    data[3] = DW_EH_PE_omit;
70  }
71  else {
72    // fde_count_enc
73    data[2] = DW_EH_PE_udata4;
74    // table_enc
75    data[3] = DW_EH_PE_datarel | DW_EH_PE_sdata4;
76
77    // prepare the binary search table
78    typedef std::vector<bit32::Entry> SearchTableType;
79    SearchTableType search_table;
80
81    for (EhFrame::const_cie_iterator i = m_EhFrame.getEhFrame()->cie_begin(),
82         e = m_EhFrame.getEhFrame()->cie_end(); i != e; ++i) {
83      EhFrame::CIE& cie = **i;
84      for (EhFrame::const_fde_iterator fi = cie.begin(), fe = cie.end();
85           fi != fe; ++fi) {
86        EhFrame::FDE& fde = **fi;
87        SizeTraits<32>::Offset offset;
88        SizeTraits<32>::Address fde_pc;
89        SizeTraits<32>::Address fde_addr;
90        offset = fde.getOffset();
91        fde_pc = computePCBegin(fde, ehframe_region);
92        fde_addr = m_EhFrame.addr() + offset;
93        search_table.push_back(std::make_pair(fde_pc, fde_addr));
94      }
95    }
96
97    std::sort(search_table.begin(), search_table.end(), bit32::EntryCompare);
98
99    // write out the binary search table
100    uint32_t* bst = (uint32_t*)(data + 12);
101    SearchTableType::const_iterator entry, entry_end = search_table.end();
102    size_t id = 0;
103    for (entry = search_table.begin(); entry != entry_end; ++entry) {
104      bst[id++] = (*entry).first - m_EhFrameHdr.addr();
105      bst[id++] = (*entry).second - m_EhFrameHdr.addr();
106    }
107  }
108}
109
110//===----------------------------------------------------------------------===//
111// EhFrameHdr
112//===----------------------------------------------------------------------===//
113
114EhFrameHdr::EhFrameHdr(LDSection& pEhFrameHdr, const LDSection& pEhFrame)
115  : m_EhFrameHdr(pEhFrameHdr), m_EhFrame(pEhFrame) {
116}
117
118EhFrameHdr::~EhFrameHdr()
119{
120}
121
122/// @ref lsb core generic 4.1
123/// .eh_frame_hdr section format
124/// uint8_t : version
125/// uint8_t : eh_frame_ptr_enc
126/// uint8_t : fde_count_enc
127/// uint8_t : table_enc
128/// uint32_t : eh_frame_ptr
129/// uint32_t : fde_count
130/// __________________________ when fde_count > 0
131/// <uint32_t, uint32_t>+ : binary search table
132/// sizeOutput - base on the fde count to size output
133void EhFrameHdr::sizeOutput()
134{
135  size_t size = 12;
136  if (m_EhFrame.hasEhFrame())
137    size += 8 * m_EhFrame.getEhFrame()->numOfFDEs();
138  m_EhFrameHdr.setSize(size);
139}
140
141/// computePCBegin - return the address of FDE's pc
142/// @ref binutils gold: ehframe.cc:222
143uint32_t EhFrameHdr::computePCBegin(const EhFrame::FDE& pFDE,
144                                    const MemoryRegion& pEhFrameRegion)
145{
146  uint8_t fde_encoding = pFDE.getCIE().getFDEEncode();
147  unsigned int eh_value = fde_encoding & 0x7;
148
149  // check the size to read in
150  if (eh_value == llvm::dwarf::DW_EH_PE_absptr) {
151    eh_value = DW_EH_PE_udata4;
152  }
153
154  size_t pc_size = 0x0;
155  switch (eh_value) {
156    case DW_EH_PE_udata2:
157      pc_size = 2;
158      break;
159    case DW_EH_PE_udata4:
160      pc_size = 4;
161      break;
162    case DW_EH_PE_udata8:
163      pc_size = 8;
164      break;
165    default:
166      // TODO
167      break;
168  }
169
170  SizeTraits<32>::Address pc = 0x0;
171  const uint8_t* offset = (const uint8_t*) pEhFrameRegion.begin() +
172                          pFDE.getOffset() +
173                          EhFrame::getDataStartOffset<32>();
174  std::memcpy(&pc, offset, pc_size);
175
176  // adjust the signed value
177  bool is_signed = (fde_encoding & llvm::dwarf::DW_EH_PE_signed) != 0x0;
178  if (DW_EH_PE_udata2 == eh_value && is_signed)
179    pc = (pc ^ 0x8000) - 0x8000;
180
181  // handle eh application
182  switch (fde_encoding & 0x70)
183  {
184    case DW_EH_PE_absptr:
185      break;
186    case DW_EH_PE_pcrel:
187      pc += m_EhFrame.addr() + pFDE.getOffset() +
188                               EhFrame::getDataStartOffset<32>();
189      break;
190    case DW_EH_PE_datarel:
191      // TODO
192      break;
193    default:
194      // TODO
195      break;
196  }
197  return pc;
198}
199