You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
196 lines
5.9 KiB
196 lines
5.9 KiB
//===- 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
|