| //===- EhFrameHdr.tcc -----------------------------------------------------===// |
| // |
| // The MCLinker Project |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include <vector> |
| |
| using namespace mcld; |
| using namespace llvm::dwarf; |
| |
| /// emitOutput - write out eh_frame_hdr |
| template<size_t size> |
| void EhFrameHdr::emitOutput(Output& pOutput, MCLinker& pLinker) |
| { |
| MemoryRegion* ehframe_region = |
| pOutput.memArea()->request(m_EhFrameSect.offset(), m_EhFrameSect.size()); |
| |
| MemoryRegion* ehframehdr_region = |
| pOutput.memArea()->request(m_EhFrameHdrSect.offset(), |
| m_EhFrameHdrSect.size()); |
| |
| uint8_t* data = (uint8_t*)ehframehdr_region->start(); |
| // version |
| data[0] = 1; |
| // eh_frame_ptr_enc |
| data[1] = DW_EH_PE_pcrel | DW_EH_PE_sdata4; |
| |
| // eh_frame_ptr |
| uint32_t* eh_frame_ptr = (uint32_t*)(data + 4); |
| *eh_frame_ptr = m_EhFrameSect.addr() - (m_EhFrameHdrSect.addr() + 4); |
| |
| // fde_count |
| uint32_t* fde_count = (uint32_t*)(data + 8); |
| *fde_count = m_EhFrameData.getFDECount(); |
| |
| if (m_EhFrameData.getFDECount() != 0 && |
| m_EhFrameData.canRecognizeAllEhFrame()) { |
| // fde_count_enc |
| data[2] = DW_EH_PE_udata4; |
| // table_enc |
| data[3] = DW_EH_PE_datarel | DW_EH_PE_sdata4; |
| |
| // prepare the binary search table |
| typedef std::vector<typename BSTEntry<size>::EntryType> SearchTableType; |
| SearchTableType search_table; |
| EhFrame::const_fde_iterator fde = m_EhFrameData.fde_begin(), |
| fde_end = m_EhFrameData.fde_end(); |
| for(; fde != fde_end; ++fde) { |
| assert(*fde != NULL); |
| typename SizeTraits<size>::Offset offset; |
| typename SizeTraits<size>::Address fde_pc; |
| typename SizeTraits<size>::Address fde_addr; |
| offset = pLinker.getLayout().getOutputOffset(**fde); |
| fde_pc = getFDEPC<size>(**fde, offset, *ehframe_region); |
| fde_addr = m_EhFrameSect.addr() + offset; |
| search_table.push_back(std::make_pair(fde_pc, fde_addr)); |
| } |
| |
| std::sort(search_table.begin(), search_table.end(), BSTEntryCompare<size>()); |
| |
| // write out the binary search table |
| uint32_t* bst = (uint32_t*)(data + 12); |
| typename SearchTableType::const_iterator entry = search_table.begin(), |
| entry_end = search_table.end(); |
| for (size_t id = 0; entry != entry_end; ++entry) { |
| bst[id++] = (*entry).first - m_EhFrameHdrSect.addr(); |
| bst[id++] = (*entry).second - m_EhFrameHdrSect.addr(); |
| } |
| } else { |
| // fde_count_enc |
| data[2] = DW_EH_PE_omit; |
| // table_enc |
| data[3] = DW_EH_PE_omit; |
| } |
| |
| pOutput.memArea()->release(ehframe_region); |
| pOutput.memArea()->release(ehframehdr_region); |
| } |
| |
| /// getFDEPC - return the address of FDE's pc |
| /// @ref binutils gold: ehframe.cc:222 |
| template<size_t size> |
| typename SizeTraits<size>::Address |
| EhFrameHdr::getFDEPC(const FDE& pFDE, |
| typename SizeTraits<size>::Offset pOffset, |
| 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) { |
| if (size == 32) |
| eh_value = DW_EH_PE_udata4; |
| else if (size == 64) |
| eh_value = DW_EH_PE_udata8; |
| } |
| |
| size_t pc_size = 0x0; |
| switch (eh_value) { |
| case DW_EH_PE_udata2: |
| pc_size = 2; |
| break; |
| case DW_EH_PE_udata4: |
| pc_size = 4; |
| break; |
| case DW_EH_PE_udata8: |
| pc_size = 8; |
| break; |
| default: |
| // TODO |
| break; |
| } |
| |
| typename SizeTraits<size>::Address pc = 0x0; |
| const uint8_t* offset = (const uint8_t*) pEhFrameRegion.start() + |
| pOffset + |
| pFDE.getPCBeginOffset(); |
| std::memcpy(&pc, offset, pc_size); |
| |
| // adjust the signed value |
| bool is_signed = (fde_encoding & llvm::dwarf::DW_EH_PE_signed) != 0x0; |
| switch (eh_value) { |
| case DW_EH_PE_udata2: |
| if (is_signed) |
| pc = (pc ^ 0x8000) - 0x8000; |
| break; |
| case DW_EH_PE_udata4: |
| if (is_signed && size > 32) |
| pc = (pc ^ 0x80000000) - 0x80000000; |
| break; |
| case DW_EH_PE_udata8: |
| break; |
| default: |
| // TODO |
| break; |
| } |
| |
| // handle eh application |
| switch (fde_encoding & 0x70) |
| { |
| case DW_EH_PE_absptr: |
| break; |
| case DW_EH_PE_pcrel: |
| pc += m_EhFrameSect.addr() + |
| pOffset + |
| pFDE.getPCBeginOffset(); |
| break; |
| case DW_EH_PE_datarel: |
| // TODO |
| break; |
| default: |
| // TODO |
| break; |
| } |
| |
| return pc; |
| } |
| |