//===- EhFrameHdr.cpp -----------------------------------------------------===// // // The MCLinker Project // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "mcld/LD/EhFrameHdr.h" #include "mcld/LD/EhFrame.h" #include "mcld/LD/LDSection.h" #include <llvm/Support/Dwarf.h> #include <llvm/Support/DataTypes.h> #include <algorithm> #include <cstring> namespace mcld { //===----------------------------------------------------------------------===// // Helper Function //===----------------------------------------------------------------------===// namespace bit32 { typedef std::pair<SizeTraits<32>::Address, SizeTraits<32>::Address> Entry; bool EntryCompare(const Entry& pX, const Entry& pY) { return (pX.first < pY.first); } } // namespace bit32 //===----------------------------------------------------------------------===// // Template Specification Functions //===----------------------------------------------------------------------===// /// emitOutput<32> - write out eh_frame_hdr template <> void EhFrameHdr::emitOutput<32>(FileOutputBuffer& pOutput) { MemoryRegion ehframehdr_region = pOutput.request(m_EhFrameHdr.offset(), m_EhFrameHdr.size()); MemoryRegion ehframe_region = pOutput.request(m_EhFrame.offset(), m_EhFrame.size()); uint8_t* data = ehframehdr_region.begin(); // version data[0] = 1; // eh_frame_ptr_enc data[1] = llvm::dwarf::DW_EH_PE_pcrel | llvm::dwarf::DW_EH_PE_sdata4; // eh_frame_ptr uint32_t* eh_frame_ptr = reinterpret_cast<uint32_t*>(data + 4); *eh_frame_ptr = m_EhFrame.addr() - (m_EhFrameHdr.addr() + 4); // fde_count uint32_t* fde_count = reinterpret_cast<uint32_t*>(data + 8); if (m_EhFrame.hasEhFrame()) *fde_count = m_EhFrame.getEhFrame()->numOfFDEs(); else *fde_count = 0; if (*fde_count == 0) { // fde_count_enc data[2] = llvm::dwarf::DW_EH_PE_omit; // table_enc data[3] = llvm::dwarf::DW_EH_PE_omit; } else { // fde_count_enc data[2] = llvm::dwarf::DW_EH_PE_udata4; // table_enc data[3] = llvm::dwarf::DW_EH_PE_datarel | llvm::dwarf::DW_EH_PE_sdata4; // prepare the binary search table typedef std::vector<bit32::Entry> SearchTableType; SearchTableType search_table; for (EhFrame::const_cie_iterator i = m_EhFrame.getEhFrame()->cie_begin(), e = m_EhFrame.getEhFrame()->cie_end(); i != e; ++i) { EhFrame::CIE& cie = **i; for (EhFrame::const_fde_iterator fi = cie.begin(), fe = cie.end(); fi != fe; ++fi) { EhFrame::FDE& fde = **fi; SizeTraits<32>::Offset offset; SizeTraits<32>::Address fde_pc; SizeTraits<32>::Address fde_addr; offset = fde.getOffset(); fde_pc = computePCBegin(fde, ehframe_region); fde_addr = m_EhFrame.addr() + offset; search_table.push_back(std::make_pair(fde_pc, fde_addr)); } } std::sort(search_table.begin(), search_table.end(), bit32::EntryCompare); // write out the binary search table uint32_t* bst = reinterpret_cast<uint32_t*>(data + 12); SearchTableType::const_iterator entry, entry_end = search_table.end(); size_t id = 0; for (entry = search_table.begin(); entry != entry_end; ++entry) { bst[id++] = (*entry).first - m_EhFrameHdr.addr(); bst[id++] = (*entry).second - m_EhFrameHdr.addr(); } } } //===----------------------------------------------------------------------===// // EhFrameHdr //===----------------------------------------------------------------------===// EhFrameHdr::EhFrameHdr(LDSection& pEhFrameHdr, const LDSection& pEhFrame) : m_EhFrameHdr(pEhFrameHdr), m_EhFrame(pEhFrame) { } EhFrameHdr::~EhFrameHdr() { } /// @ref lsb core generic 4.1 /// .eh_frame_hdr section format /// uint8_t : version /// uint8_t : eh_frame_ptr_enc /// uint8_t : fde_count_enc /// uint8_t : table_enc /// uint32_t : eh_frame_ptr /// uint32_t : fde_count /// __________________________ when fde_count > 0 /// <uint32_t, uint32_t>+ : binary search table /// sizeOutput - base on the fde count to size output void EhFrameHdr::sizeOutput() { size_t size = 12; if (m_EhFrame.hasEhFrame()) size += 8 * m_EhFrame.getEhFrame()->numOfFDEs(); m_EhFrameHdr.setSize(size); } /// computePCBegin - return the address of FDE's pc uint32_t EhFrameHdr::computePCBegin(const EhFrame::FDE& pFDE, const MemoryRegion& pEhFrameRegion) { uint8_t fde_encoding = pFDE.getCIE().getFDEEncode(); unsigned int eh_value = fde_encoding & 0x7; // check the size to read in if (eh_value == llvm::dwarf::DW_EH_PE_absptr) { eh_value = llvm::dwarf::DW_EH_PE_udata4; } size_t pc_size = 0x0; switch (eh_value) { case llvm::dwarf::DW_EH_PE_udata2: pc_size = 2; break; case llvm::dwarf::DW_EH_PE_udata4: pc_size = 4; break; case llvm::dwarf::DW_EH_PE_udata8: pc_size = 8; break; default: // TODO break; } SizeTraits<32>::Address pc = 0x0; const uint8_t* offset = (const uint8_t*)pEhFrameRegion.begin() + pFDE.getOffset() + EhFrame::getDataStartOffset<32>(); std::memcpy(&pc, offset, pc_size); // adjust the signed value bool is_signed = (fde_encoding & llvm::dwarf::DW_EH_PE_signed) != 0x0; if (llvm::dwarf::DW_EH_PE_udata2 == eh_value && is_signed) pc = (pc ^ 0x8000) - 0x8000; // handle eh application switch (fde_encoding & 0x70) { case llvm::dwarf::DW_EH_PE_absptr: break; case llvm::dwarf::DW_EH_PE_pcrel: pc += m_EhFrame.addr() + pFDE.getOffset() + EhFrame::getDataStartOffset<32>(); break; case llvm::dwarf::DW_EH_PE_datarel: // TODO break; default: // TODO break; } return pc; } } // namespace mcld