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.
374 lines
12 KiB
374 lines
12 KiB
//===- EhFrame.cpp --------------------------------------------------------===//
|
|
//
|
|
// The MCLinker Project
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
#include "mcld/LD/EhFrame.h"
|
|
|
|
#include "mcld/Fragment/Relocation.h"
|
|
#include "mcld/LD/LDContext.h"
|
|
#include "mcld/LD/LDSection.h"
|
|
#include "mcld/LD/LDSymbol.h"
|
|
#include "mcld/LD/RelocData.h"
|
|
#include "mcld/LD/ResolveInfo.h"
|
|
#include "mcld/LD/SectionData.h"
|
|
#include "mcld/MC/Input.h"
|
|
#include "mcld/Object/ObjectBuilder.h"
|
|
#include "mcld/Support/GCFactory.h"
|
|
|
|
#include <llvm/Support/ManagedStatic.h>
|
|
|
|
namespace mcld {
|
|
|
|
typedef GCFactory<EhFrame, MCLD_SECTIONS_PER_INPUT> EhFrameFactory;
|
|
|
|
static llvm::ManagedStatic<EhFrameFactory> g_EhFrameFactory;
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// EhFrame::Record
|
|
//===----------------------------------------------------------------------===//
|
|
EhFrame::Record::Record(llvm::StringRef pRegion) : RegionFragment(pRegion) {
|
|
}
|
|
|
|
EhFrame::Record::~Record() {
|
|
// llvm::iplist will manage and delete the fragments
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// EhFrame::CIE
|
|
//===----------------------------------------------------------------------===//
|
|
EhFrame::CIE::CIE(llvm::StringRef pRegion)
|
|
: EhFrame::Record(pRegion),
|
|
m_FDEEncode(0u),
|
|
m_Mergeable(false),
|
|
m_pReloc(0),
|
|
m_PersonalityOffset(0) {
|
|
}
|
|
|
|
EhFrame::CIE::~CIE() {
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// EhFrame::FDE
|
|
//===----------------------------------------------------------------------===//
|
|
EhFrame::FDE::FDE(llvm::StringRef pRegion, EhFrame::CIE& pCIE)
|
|
: EhFrame::Record(pRegion), m_pCIE(&pCIE) {
|
|
}
|
|
|
|
EhFrame::FDE::~FDE() {
|
|
}
|
|
|
|
void EhFrame::FDE::setCIE(EhFrame::CIE& pCIE) {
|
|
m_pCIE = &pCIE;
|
|
m_pCIE->add(*this);
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// EhFrame::GeneratedCIE
|
|
//===----------------------------------------------------------------------===//
|
|
EhFrame::GeneratedCIE::GeneratedCIE(llvm::StringRef pRegion)
|
|
: EhFrame::CIE(pRegion) {
|
|
}
|
|
|
|
EhFrame::GeneratedCIE::~GeneratedCIE() {
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// EhFrame::GeneratedFDE
|
|
//===----------------------------------------------------------------------===//
|
|
EhFrame::GeneratedFDE::GeneratedFDE(llvm::StringRef pRegion, CIE& pCIE)
|
|
: EhFrame::FDE(pRegion, pCIE) {
|
|
}
|
|
|
|
EhFrame::GeneratedFDE::~GeneratedFDE() {
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// EhFrame
|
|
//===----------------------------------------------------------------------===//
|
|
EhFrame::EhFrame() : m_pSection(NULL), m_pSectionData(NULL) {
|
|
}
|
|
|
|
EhFrame::EhFrame(LDSection& pSection)
|
|
: m_pSection(&pSection), m_pSectionData(NULL) {
|
|
m_pSectionData = SectionData::Create(pSection);
|
|
}
|
|
|
|
EhFrame::~EhFrame() {
|
|
}
|
|
|
|
EhFrame* EhFrame::Create(LDSection& pSection) {
|
|
EhFrame* result = g_EhFrameFactory->allocate();
|
|
new (result) EhFrame(pSection);
|
|
return result;
|
|
}
|
|
|
|
void EhFrame::Destroy(EhFrame*& pSection) {
|
|
pSection->~EhFrame();
|
|
g_EhFrameFactory->deallocate(pSection);
|
|
pSection = NULL;
|
|
}
|
|
|
|
void EhFrame::Clear() {
|
|
g_EhFrameFactory->clear();
|
|
}
|
|
|
|
const LDSection& EhFrame::getSection() const {
|
|
assert(m_pSection != NULL);
|
|
return *m_pSection;
|
|
}
|
|
|
|
LDSection& EhFrame::getSection() {
|
|
assert(m_pSection != NULL);
|
|
return *m_pSection;
|
|
}
|
|
|
|
void EhFrame::addFragment(Fragment& pFrag) {
|
|
uint32_t offset = 0;
|
|
if (!m_pSectionData->empty())
|
|
offset = m_pSectionData->back().getOffset() + m_pSectionData->back().size();
|
|
|
|
m_pSectionData->getFragmentList().push_back(&pFrag);
|
|
pFrag.setParent(m_pSectionData);
|
|
pFrag.setOffset(offset);
|
|
}
|
|
|
|
void EhFrame::addCIE(EhFrame::CIE& pCIE, bool pAlsoAddFragment) {
|
|
m_CIEs.push_back(&pCIE);
|
|
if (pAlsoAddFragment)
|
|
addFragment(pCIE);
|
|
}
|
|
|
|
void EhFrame::addFDE(EhFrame::FDE& pFDE, bool pAlsoAddFragment) {
|
|
pFDE.getCIE().add(pFDE);
|
|
if (pAlsoAddFragment)
|
|
addFragment(pFDE);
|
|
}
|
|
|
|
size_t EhFrame::numOfFDEs() const {
|
|
// FDE number only used by .eh_frame_hdr computation, and the number of CIE
|
|
// is usually not too many. It is worthy to compromise space by time
|
|
size_t size = 0u;
|
|
for (const_cie_iterator i = cie_begin(), e = cie_end(); i != e; ++i)
|
|
size += (*i)->numOfFDEs();
|
|
return size;
|
|
}
|
|
|
|
EhFrame& EhFrame::merge(const Input& pInput, EhFrame& pFrame) {
|
|
assert(this != &pFrame);
|
|
if (pFrame.emptyCIEs()) {
|
|
// May be a partial linking, or the eh_frame has no data.
|
|
// Just append the fragments.
|
|
moveInputFragments(pFrame);
|
|
return *this;
|
|
}
|
|
|
|
const LDContext& ctx = *pInput.context();
|
|
const LDSection* rel_sec = 0;
|
|
for (LDContext::const_sect_iterator ri = ctx.relocSectBegin(),
|
|
re = ctx.relocSectEnd();
|
|
ri != re;
|
|
++ri) {
|
|
if ((*ri)->getLink() == &pFrame.getSection()) {
|
|
rel_sec = *ri;
|
|
break;
|
|
}
|
|
}
|
|
pFrame.setupAttributes(rel_sec);
|
|
|
|
// Most CIE will be merged, so we don't reserve space first.
|
|
for (cie_iterator i = pFrame.cie_begin(), e = pFrame.cie_end(); i != e; ++i) {
|
|
CIE& input_cie = **i;
|
|
// CIE number is usually very few, so we just use vector sequential search.
|
|
if (!input_cie.getMergeable()) {
|
|
moveInputFragments(pFrame, input_cie);
|
|
addCIE(input_cie, /*AlsoAddFragment=*/false);
|
|
continue;
|
|
}
|
|
|
|
cie_iterator out_i = cie_begin();
|
|
for (cie_iterator out_e = cie_end(); out_i != out_e; ++out_i) {
|
|
CIE& output_cie = **out_i;
|
|
if (output_cie == input_cie) {
|
|
// This input CIE can be merged
|
|
moveInputFragments(pFrame, input_cie, &output_cie);
|
|
removeAndUpdateCIEForFDE(pFrame, input_cie, output_cie, rel_sec);
|
|
break;
|
|
}
|
|
}
|
|
if (out_i == cie_end()) {
|
|
moveInputFragments(pFrame, input_cie);
|
|
addCIE(input_cie, /*AlsoAddFragment=*/false);
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
void EhFrame::setupAttributes(const LDSection* rel_sec) {
|
|
for (cie_iterator i = cie_begin(), e = cie_end(); i != e; ++i) {
|
|
CIE* cie = *i;
|
|
removeDiscardedFDE(*cie, rel_sec);
|
|
|
|
if (cie->getPersonalityName().size() == 0) {
|
|
// There's no personality data encoding inside augmentation string.
|
|
cie->setMergeable();
|
|
} else {
|
|
if (!rel_sec) {
|
|
// No relocation to eh_frame section
|
|
assert(cie->getPersonalityName() != "" &&
|
|
"PR name should be a symbol address or offset");
|
|
continue;
|
|
}
|
|
const RelocData* reloc_data = rel_sec->getRelocData();
|
|
for (RelocData::const_iterator ri = reloc_data->begin(),
|
|
re = reloc_data->end();
|
|
ri != re;
|
|
++ri) {
|
|
const Relocation& rel = *ri;
|
|
if (rel.targetRef().getOutputOffset() ==
|
|
cie->getOffset() + cie->getPersonalityOffset()) {
|
|
cie->setMergeable();
|
|
cie->setPersonalityName(rel.symInfo()->outSymbol()->name());
|
|
cie->setRelocation(rel);
|
|
break;
|
|
}
|
|
}
|
|
|
|
assert(cie->getPersonalityName() != "" &&
|
|
"PR name should be a symbol address or offset");
|
|
}
|
|
}
|
|
}
|
|
|
|
void EhFrame::removeDiscardedFDE(CIE& pCIE, const LDSection* pRelocSect) {
|
|
if (!pRelocSect)
|
|
return;
|
|
|
|
typedef std::vector<FDE*> FDERemoveList;
|
|
FDERemoveList to_be_removed_fdes;
|
|
const RelocData* reloc_data = pRelocSect->getRelocData();
|
|
for (fde_iterator i = pCIE.begin(), e = pCIE.end(); i != e; ++i) {
|
|
FDE& fde = **i;
|
|
for (RelocData::const_iterator ri = reloc_data->begin(),
|
|
re = reloc_data->end();
|
|
ri != re;
|
|
++ri) {
|
|
const Relocation& rel = *ri;
|
|
if (rel.targetRef().getOutputOffset() ==
|
|
fde.getOffset() + getDataStartOffset<32>()) {
|
|
bool has_section = rel.symInfo()->outSymbol()->hasFragRef();
|
|
if (!has_section)
|
|
// The section was discarded, just ignore this FDE.
|
|
// This may happen when redundant group section was read.
|
|
to_be_removed_fdes.push_back(&fde);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (FDERemoveList::iterator i = to_be_removed_fdes.begin(),
|
|
e = to_be_removed_fdes.end();
|
|
i != e;
|
|
++i) {
|
|
FDE& fde = **i;
|
|
fde.getCIE().remove(fde);
|
|
|
|
// FIXME: This traverses relocations from the beginning on each FDE, which
|
|
// may cause performance degration. Actually relocations will be sequential
|
|
// order, so we can bookkeep the previously found relocation for next use.
|
|
// Note: We must ensure FDE order is ordered.
|
|
for (RelocData::const_iterator ri = reloc_data->begin(),
|
|
re = reloc_data->end();
|
|
ri != re;) {
|
|
Relocation& rel = const_cast<Relocation&>(*ri++);
|
|
if (rel.targetRef().getOutputOffset() >= fde.getOffset() &&
|
|
rel.targetRef().getOutputOffset() < fde.getOffset() + fde.size()) {
|
|
const_cast<RelocData*>(reloc_data)->remove(rel);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void EhFrame::removeAndUpdateCIEForFDE(EhFrame& pInFrame,
|
|
CIE& pInCIE,
|
|
CIE& pOutCIE,
|
|
const LDSection* rel_sect) {
|
|
// Make this relocation to be ignored.
|
|
Relocation* rel = const_cast<Relocation*>(pInCIE.getRelocation());
|
|
if (rel && rel_sect)
|
|
const_cast<RelocData*>(rel_sect->getRelocData())->remove(*rel);
|
|
|
|
// Update the CIE-pointed FDEs
|
|
for (fde_iterator i = pInCIE.begin(), e = pInCIE.end(); i != e; ++i)
|
|
(*i)->setCIE(pOutCIE);
|
|
|
|
// We cannot know whether there are references to this fragment, so just
|
|
// keep it in input fragment list instead of memory deallocation
|
|
pInCIE.clearFDEs();
|
|
}
|
|
|
|
void EhFrame::moveInputFragments(EhFrame& pInFrame) {
|
|
SectionData& in_sd = *pInFrame.getSectionData();
|
|
SectionData::FragmentListType& in_frag_list = in_sd.getFragmentList();
|
|
SectionData& out_sd = *getSectionData();
|
|
SectionData::FragmentListType& out_frag_list = out_sd.getFragmentList();
|
|
|
|
while (!in_frag_list.empty()) {
|
|
Fragment* frag = in_frag_list.remove(in_frag_list.begin());
|
|
out_frag_list.push_back(frag);
|
|
frag->setParent(&out_sd);
|
|
}
|
|
}
|
|
|
|
void EhFrame::moveInputFragments(EhFrame& pInFrame, CIE& pInCIE, CIE* pOutCIE) {
|
|
SectionData& in_sd = *pInFrame.getSectionData();
|
|
SectionData::FragmentListType& in_frag_list = in_sd.getFragmentList();
|
|
SectionData& out_sd = *getSectionData();
|
|
SectionData::FragmentListType& out_frag_list = out_sd.getFragmentList();
|
|
|
|
if (!pOutCIE) {
|
|
// Newly inserted
|
|
Fragment* frag = in_frag_list.remove(SectionData::iterator(pInCIE));
|
|
out_frag_list.push_back(frag);
|
|
frag->setParent(&out_sd);
|
|
for (fde_iterator i = pInCIE.begin(), e = pInCIE.end(); i != e; ++i) {
|
|
frag = in_frag_list.remove(SectionData::iterator(**i));
|
|
out_frag_list.push_back(frag);
|
|
frag->setParent(&out_sd);
|
|
}
|
|
return;
|
|
}
|
|
|
|
SectionData::iterator cur_iter(*pOutCIE);
|
|
assert(cur_iter != out_frag_list.end());
|
|
for (fde_iterator i = pInCIE.begin(), e = pInCIE.end(); i != e; ++i) {
|
|
Fragment* frag = in_frag_list.remove(SectionData::iterator(**i));
|
|
cur_iter = out_frag_list.insertAfter(cur_iter, frag);
|
|
frag->setParent(&out_sd);
|
|
}
|
|
}
|
|
|
|
size_t EhFrame::computeOffsetSize() {
|
|
size_t offset = 0u;
|
|
SectionData::FragmentListType& frag_list =
|
|
getSectionData()->getFragmentList();
|
|
for (SectionData::iterator i = frag_list.begin(), e = frag_list.end(); i != e;
|
|
++i) {
|
|
Fragment& frag = *i;
|
|
frag.setOffset(offset);
|
|
offset += frag.size();
|
|
}
|
|
getSection().setSize(offset);
|
|
return offset;
|
|
}
|
|
|
|
bool operator==(const EhFrame::CIE& p1, const EhFrame::CIE& p2) {
|
|
return p1.getPersonalityName() == p2.getPersonalityName() &&
|
|
p1.getAugmentationData() == p2.getAugmentationData();
|
|
}
|
|
|
|
} // namespace mcld
|