//===- 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 "mcld/Fragment/Fragment.h"
#include "mcld/Fragment/RegionFragment.h"
#include "mcld/Fragment/Stub.h"
#include "mcld/LD/EhFrame.h"
#include "mcld/LD/LDSection.h"
#include "mcld/LD/SectionData.h"
#include "mcld/Support/GCFactory.h"
#include <llvm/ADT/StringRef.h>
#include <llvm/Support/Casting.h>
#include <llvm/Support/ManagedStatic.h>
#include <cassert>
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 (frag != NULL) {
offset -= frag->size();
if (offset <= 0)
break;
frag = frag->getNextNode();
}
if ((frag != NULL) && (frag->size() != 0)) {
if (offset == 0)
frag = frag->getNextNode();
else
offset += frag->size();
}
if (frag == NULL)
return Null();
FragmentRef* result = g_FragRefFactory->allocate();
new (result) FragmentRef(*frag, offset);
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 (data == NULL || 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 (m_pFragment == NULL)
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().begin() + 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::Offset FragmentRef::getOutputOffset() const {
Offset result = 0;
if (m_pFragment != NULL)
result = m_pFragment->getOffset();
return (result + m_Offset);
}
} // namespace mcld