1//===- FragmentRef.cpp --------------------------------------------------===//
2//
3//                     The MCLinker Project
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9#include "mcld/Fragment/FragmentRef.h"
10
11#include "mcld/Fragment/Fragment.h"
12#include "mcld/Fragment/RegionFragment.h"
13#include "mcld/Fragment/Stub.h"
14#include "mcld/LD/EhFrame.h"
15#include "mcld/LD/LDSection.h"
16#include "mcld/LD/SectionData.h"
17#include "mcld/Support/GCFactory.h"
18
19#include <llvm/ADT/StringRef.h>
20#include <llvm/Support/Casting.h>
21#include <llvm/Support/ManagedStatic.h>
22
23#include <cassert>
24
25namespace mcld {
26
27typedef GCFactory<FragmentRef, MCLD_SECTIONS_PER_INPUT> FragRefFactory;
28
29static llvm::ManagedStatic<FragRefFactory> g_FragRefFactory;
30
31FragmentRef FragmentRef::g_NullFragmentRef;
32
33//===----------------------------------------------------------------------===//
34// FragmentRef
35//===----------------------------------------------------------------------===//
36FragmentRef::FragmentRef() : m_pFragment(NULL), m_Offset(0) {
37}
38
39FragmentRef::FragmentRef(Fragment& pFrag, FragmentRef::Offset pOffset)
40    : m_pFragment(&pFrag), m_Offset(pOffset) {
41}
42
43/// Create - create a fragment reference for a given fragment.
44///
45/// @param pFrag - the given fragment
46/// @param pOffset - the offset, can be larger than the fragment, but can not
47///                  be larger than the section size.
48/// @return if the offset is legal, return the fragment reference. Otherwise,
49/// return NULL.
50FragmentRef* FragmentRef::Create(Fragment& pFrag, uint64_t pOffset) {
51  int64_t offset = pOffset;
52  Fragment* frag = &pFrag;
53
54  while (frag != NULL) {
55    offset -= frag->size();
56    if (offset <= 0)
57      break;
58    frag = frag->getNextNode();
59  }
60  if ((frag != NULL) && (frag->size() != 0)) {
61    if (offset == 0)
62      frag = frag->getNextNode();
63    else
64      offset += frag->size();
65  }
66
67  if (frag == NULL)
68    return Null();
69
70  FragmentRef* result = g_FragRefFactory->allocate();
71  new (result) FragmentRef(*frag, offset);
72
73  return result;
74}
75
76FragmentRef* FragmentRef::Create(LDSection& pSection, uint64_t pOffset) {
77  SectionData* data = NULL;
78  switch (pSection.kind()) {
79    case LDFileFormat::Relocation:
80      // No fragment reference refers to a relocation section
81      break;
82    case LDFileFormat::EhFrame:
83      if (pSection.hasEhFrame())
84        data = pSection.getEhFrame()->getSectionData();
85      break;
86    default:
87      data = pSection.getSectionData();
88      break;
89  }
90
91  if (data == NULL || data->empty()) {
92    return Null();
93  }
94
95  return Create(data->front(), pOffset);
96}
97
98void FragmentRef::Clear() {
99  g_FragRefFactory->clear();
100}
101
102FragmentRef* FragmentRef::Null() {
103  return &g_NullFragmentRef;
104}
105
106FragmentRef& FragmentRef::assign(const FragmentRef& pCopy) {
107  m_pFragment = const_cast<Fragment*>(pCopy.m_pFragment);
108  m_Offset = pCopy.m_Offset;
109  return *this;
110}
111
112FragmentRef& FragmentRef::assign(Fragment& pFrag, FragmentRef::Offset pOffset) {
113  m_pFragment = &pFrag;
114  m_Offset = pOffset;
115  return *this;
116}
117
118void FragmentRef::memcpy(void* pDest, size_t pNBytes, Offset pOffset) const {
119  // check if the offset is still in a legal range.
120  if (m_pFragment == NULL)
121    return;
122
123  unsigned int total_offset = m_Offset + pOffset;
124  switch (m_pFragment->getKind()) {
125    case Fragment::Region: {
126      RegionFragment* region_frag = static_cast<RegionFragment*>(m_pFragment);
127      unsigned int total_length = region_frag->getRegion().size();
128      if (total_length < (total_offset + pNBytes))
129        pNBytes = total_length - total_offset;
130
131      std::memcpy(
132          pDest, region_frag->getRegion().begin() + total_offset, pNBytes);
133      return;
134    }
135    case Fragment::Stub: {
136      Stub* stub_frag = static_cast<Stub*>(m_pFragment);
137      unsigned int total_length = stub_frag->size();
138      if (total_length < (total_offset + pNBytes))
139        pNBytes = total_length - total_offset;
140      std::memcpy(pDest, stub_frag->getContent() + total_offset, pNBytes);
141      return;
142    }
143    case Fragment::Alignment:
144    case Fragment::Fillment:
145    default:
146      return;
147  }
148}
149
150FragmentRef::Offset FragmentRef::getOutputOffset() const {
151  Offset result = 0;
152  if (m_pFragment != NULL)
153    result = m_pFragment->getOffset();
154  return (result + m_Offset);
155}
156
157}  // namespace mcld
158