//===- 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