//===- 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);
}