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