EhFrameHdr.tcc revision affc150dc44fab1911775a49636d0ce85333b634
1//===- EhFrameHdr.tcc -----------------------------------------------------===//
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
10#include <vector>
11
12using namespace mcld;
13using namespace llvm::dwarf;
14
15/// emitOutput - write out eh_frame_hdr
16template<size_t size>
17void EhFrameHdr::emitOutput(Output& pOutput, MCLinker& pLinker)
18{
19  MemoryRegion* ehframe_region =
20    pOutput.memArea()->request(m_EhFrameSect.offset(), m_EhFrameSect.size());
21
22  MemoryRegion* ehframehdr_region =
23    pOutput.memArea()->request(m_EhFrameHdrSect.offset(),
24                               m_EhFrameHdrSect.size());
25
26  uint8_t* data = (uint8_t*)ehframehdr_region->start();
27  // version
28  data[0] = 1;
29  // eh_frame_ptr_enc
30  data[1] = DW_EH_PE_pcrel | DW_EH_PE_sdata4;
31
32  // eh_frame_ptr
33  uint32_t* eh_frame_ptr = (uint32_t*)(data + 4);
34  *eh_frame_ptr = m_EhFrameSect.addr() - (m_EhFrameHdrSect.addr() + 4);
35
36  // fde_count
37  uint32_t* fde_count = (uint32_t*)(data + 8);
38  *fde_count = m_EhFrameData.getFDECount();
39
40  if (m_EhFrameData.getFDECount() != 0 &&
41      m_EhFrameData.canRecognizeAllEhFrame()) {
42    // fde_count_enc
43    data[2] = DW_EH_PE_udata4;
44    // table_enc
45    data[3] = DW_EH_PE_datarel | DW_EH_PE_sdata4;
46
47    // prepare the binary search table
48    typedef std::vector<typename BSTEntry<size>::EntryType> SearchTableType;
49    SearchTableType search_table;
50    EhFrame::const_fde_iterator fde = m_EhFrameData.fde_begin(),
51                                fde_end = m_EhFrameData.fde_end();
52    for(; fde != fde_end; ++fde) {
53      assert(*fde != NULL);
54      typename SizeTraits<size>::Offset offset;
55      typename SizeTraits<size>::Address fde_pc;
56      typename SizeTraits<size>::Address fde_addr;
57      offset = pLinker.getLayout().getOutputOffset(**fde);
58      fde_pc = getFDEPC<size>(**fde, offset, *ehframe_region);
59      fde_addr = m_EhFrameSect.addr() + offset;
60      search_table.push_back(std::make_pair(fde_pc, fde_addr));
61    }
62
63    std::sort(search_table.begin(), search_table.end(), BSTEntryCompare<size>());
64
65    // write out the binary search table
66    uint32_t* bst = (uint32_t*)(data + 12);
67    typename SearchTableType::const_iterator entry = search_table.begin(),
68                                             entry_end = search_table.end();
69    for (size_t id = 0; entry != entry_end; ++entry) {
70      bst[id++] = (*entry).first - m_EhFrameHdrSect.addr();
71      bst[id++] = (*entry).second - m_EhFrameHdrSect.addr();
72    }
73  } else {
74    // fde_count_enc
75    data[2] = DW_EH_PE_omit;
76    // table_enc
77    data[3] = DW_EH_PE_omit;
78  }
79
80  pOutput.memArea()->release(ehframe_region);
81  pOutput.memArea()->release(ehframehdr_region);
82}
83
84/// getFDEPC - return the address of FDE's pc
85/// @ref binutils gold: ehframe.cc:222
86template<size_t size>
87typename SizeTraits<size>::Address
88EhFrameHdr::getFDEPC(const FDE& pFDE,
89                     typename SizeTraits<size>::Offset pOffset,
90                     const MemoryRegion& pEhFrameRegion)
91{
92  uint8_t fde_encoding = pFDE.getCIE().getFDEEncode();
93  unsigned int eh_value = fde_encoding & 0x7;
94
95  // check the size to read in
96  if (eh_value == llvm::dwarf::DW_EH_PE_absptr) {
97    if (size == 32)
98      eh_value = DW_EH_PE_udata4;
99    else if (size == 64)
100      eh_value = DW_EH_PE_udata8;
101  }
102
103  size_t pc_size = 0x0;
104  switch (eh_value) {
105    case DW_EH_PE_udata2:
106      pc_size = 2;
107      break;
108    case DW_EH_PE_udata4:
109      pc_size = 4;
110      break;
111    case DW_EH_PE_udata8:
112      pc_size = 8;
113      break;
114    default:
115      // TODO
116      break;
117  }
118
119  typename SizeTraits<size>::Address pc = 0x0;
120  const uint8_t* offset = (const uint8_t*) pEhFrameRegion.start() +
121                          pOffset +
122                          pFDE.getPCBeginOffset();
123  std::memcpy(&pc, offset, pc_size);
124
125  // adjust the signed value
126  bool is_signed = (fde_encoding & llvm::dwarf::DW_EH_PE_signed) != 0x0;
127  switch (eh_value) {
128    case DW_EH_PE_udata2:
129      if (is_signed)
130        pc = (pc ^ 0x8000) - 0x8000;
131      break;
132    case DW_EH_PE_udata4:
133      if (is_signed && size > 32)
134        pc = (pc ^ 0x80000000) - 0x80000000;
135      break;
136    case DW_EH_PE_udata8:
137      break;
138    default:
139      // TODO
140      break;
141  }
142
143  // handle eh application
144  switch (fde_encoding & 0x70)
145  {
146    case DW_EH_PE_absptr:
147      break;
148    case DW_EH_PE_pcrel:
149      pc += m_EhFrameSect.addr() +
150            pOffset +
151            pFDE.getPCBeginOffset();
152      break;
153    case DW_EH_PE_datarel:
154      // TODO
155      break;
156    default:
157      // TODO
158      break;
159  }
160
161  return pc;
162}
163
164