C++程序  |  158行  |  4.34 KB

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