//===- FragmentRef.cpp --------------------------------------------------===// // // The MCLinker Project // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include <mcld/Fragment/FragmentRef.h> #include <cstring> #include <cassert> #include <llvm/Support/Casting.h> #include <llvm/Support/ManagedStatic.h> #include <mcld/Fragment/Fragment.h> #include <mcld/LD/LDSection.h> #include <mcld/LD/SectionData.h> #include <mcld/LD/EhFrame.h> #include <mcld/Support/GCFactory.h> #include <mcld/Support/MemoryRegion.h> #include <mcld/Fragment/RegionFragment.h> #include <mcld/Fragment/Stub.h> using namespace mcld; typedef GCFactory<FragmentRef, MCLD_SECTIONS_PER_INPUT> FragRefFactory; static llvm::ManagedStatic<FragRefFactory> g_FragRefFactory; FragmentRef FragmentRef::g_NullFragmentRef; //===----------------------------------------------------------------------===// // FragmentRef //===----------------------------------------------------------------------===// FragmentRef::FragmentRef() : m_pFragment(NULL), m_Offset(0) { } FragmentRef::FragmentRef(Fragment& pFrag, FragmentRef::Offset pOffset) : m_pFragment(&pFrag), m_Offset(pOffset) { } /// Create - create a fragment reference for a given fragment. /// /// @param pFrag - the given fragment /// @param pOffset - the offset, can be larger than the fragment, but can not /// be larger than the section size. /// @return if the offset is legal, return the fragment reference. Otherwise, /// return NULL. FragmentRef* FragmentRef::Create(Fragment& pFrag, uint64_t pOffset) { int64_t offset = pOffset; Fragment* frag = &pFrag; while (NULL != frag) { offset -= frag->size(); if (offset <= 0) break; frag = frag->getNextNode(); } if (NULL == frag) return Null(); FragmentRef* result = g_FragRefFactory->allocate(); new (result) FragmentRef(*frag, offset + frag->size()); return result; } FragmentRef* FragmentRef::Create(LDSection& pSection, uint64_t pOffset) { SectionData* data = NULL; switch (pSection.kind()) { case LDFileFormat::Relocation: // No fragment reference refers to a relocation section break; case LDFileFormat::EhFrame: if (pSection.hasEhFrame()) data = &pSection.getEhFrame()->getSectionData(); break; default: data = pSection.getSectionData(); break; } if (NULL == data || data->empty()) { return Null(); } return Create(data->front(), pOffset); } void FragmentRef::Clear() { g_FragRefFactory->clear(); } FragmentRef* FragmentRef::Null() { return &g_NullFragmentRef; } FragmentRef& FragmentRef::assign(const FragmentRef& pCopy) { m_pFragment = const_cast<Fragment*>(pCopy.m_pFragment); m_Offset = pCopy.m_Offset; return *this; } FragmentRef& FragmentRef::assign(Fragment& pFrag, FragmentRef::Offset pOffset) { m_pFragment = &pFrag; m_Offset = pOffset; return *this; } void FragmentRef::memcpy(void* pDest, size_t pNBytes, Offset pOffset) const { // check if the offset is still in a legal range. if (NULL == m_pFragment) return; unsigned int total_offset = m_Offset + pOffset; switch(m_pFragment->getKind()) { case Fragment::Region: { RegionFragment* region_frag = static_cast<RegionFragment*>(m_pFragment); unsigned int total_length = region_frag->getRegion().size(); if (total_length < (total_offset+pNBytes)) pNBytes = total_length - total_offset; std::memcpy(pDest, region_frag->getRegion().getBuffer(total_offset), pNBytes); return; } case Fragment::Stub: { Stub* stub_frag = static_cast<Stub*>(m_pFragment); unsigned int total_length = stub_frag->size(); if (total_length < (total_offset+pNBytes)) pNBytes = total_length - total_offset; std::memcpy(pDest, stub_frag->getContent() + total_offset, pNBytes); return; } case Fragment::Alignment: case Fragment::Fillment: default: return; } } FragmentRef::Address FragmentRef::deref() { if (NULL == m_pFragment) return NULL; Address base = NULL; switch(m_pFragment->getKind()) { case Fragment::Region: base = static_cast<RegionFragment*>(m_pFragment)->getRegion().getBuffer(); break; case Fragment::Alignment: case Fragment::Fillment: default: return NULL; } return base + m_Offset; } FragmentRef::ConstAddress FragmentRef::deref() const { if (NULL == m_pFragment) return NULL; ConstAddress base = NULL; switch(m_pFragment->getKind()) { case Fragment::Region: base = static_cast<const RegionFragment*>(m_pFragment)->getRegion().getBuffer(); break; case Fragment::Alignment: case Fragment::Fillment: default: return NULL; } return base + m_Offset; } FragmentRef::Offset FragmentRef::getOutputOffset() const { Offset result = 0; if (NULL != m_pFragment) result = m_pFragment->getOffset(); return (result + m_Offset); }