1//===- StubFactory.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/LD/StubFactory.h>
10#include <mcld/IRBuilder.h>
11#include <mcld/LD/BranchIslandFactory.h>
12#include <mcld/LD/BranchIsland.h>
13#include <mcld/LD/LDSymbol.h>
14#include <mcld/LD/ResolveInfo.h>
15#include <mcld/Fragment/Stub.h>
16#include <mcld/Fragment/Relocation.h>
17
18#include <string>
19
20using namespace mcld;
21
22//===----------------------------------------------------------------------===//
23// StubFactory
24//===----------------------------------------------------------------------===//
25StubFactory::~StubFactory()
26{
27  for (StubPoolType::iterator it = m_StubPool.begin(), ie = m_StubPool.end();
28       it != ie; ++it)
29    delete(*it);
30}
31
32/// addPrototype - register a stub prototype
33void StubFactory::addPrototype(Stub* pPrototype)
34{
35  m_StubPool.push_back(pPrototype);
36}
37
38/// create - create a stub if needed, otherwise return NULL
39Stub* StubFactory::create(Relocation& pReloc,
40                          uint64_t pTargetSymValue,
41                          IRBuilder& pBuilder,
42                          BranchIslandFactory& pBRIslandFactory)
43{
44  // find if there is a prototype stub for the input relocation
45  Stub* stub = NULL;
46  Stub* prototype = findPrototype(pReloc, pReloc.place(), pTargetSymValue);
47  if (prototype != NULL) {
48    const Fragment* frag = pReloc.targetRef().frag();
49    // find the islands for the input relocation
50    std::pair<BranchIsland*, BranchIsland*> islands =
51        pBRIslandFactory.getIslands(*frag);
52    if (islands.first == NULL) {
53      // early exit if we can not find the forward island.
54      return NULL;
55    }
56
57    // find if there is such a stub in the backward island first.
58    if (islands.second != NULL) {
59      stub = islands.second->findStub(prototype, pReloc);
60    }
61
62    if (stub != NULL) {
63      // reset the branch target to the stub instead!
64      pReloc.setSymInfo(stub->symInfo());
65    } else {
66      // find if there is such a stub in the forward island.
67      stub = islands.first->findStub(prototype, pReloc);
68      if (stub != NULL) {
69        // reset the branch target to the stub instead!
70        pReloc.setSymInfo(stub->symInfo());
71      } else {
72        // create a stub from the prototype
73        stub = prototype->clone();
74
75        // build a name for stub symbol
76        std::string name("__");
77        name.append(pReloc.symInfo()->name())
78            .append("_")
79            .append(stub->name())
80            .append("@")
81            .append(islands.first->name());
82
83        // create LDSymbol for the stub
84        LDSymbol* symbol =
85            pBuilder.AddSymbol<IRBuilder::Force, IRBuilder::Unresolve>(
86                name,
87                ResolveInfo::Function,
88                ResolveInfo::Define,
89                ResolveInfo::Local,
90                stub->size(), // size
91                stub->initSymValue(), // value
92                FragmentRef::Create(*stub, stub->initSymValue()),
93                ResolveInfo::Default);
94        stub->setSymInfo(symbol->resolveInfo());
95
96        // add relocations of this stub (i.e., set the branch target of the stub)
97        for (Stub::fixup_iterator it = stub->fixup_begin(),
98             ie = stub->fixup_end(); it != ie; ++it) {
99
100          Relocation* reloc =
101              Relocation::Create((*it)->type(),
102                                 *(FragmentRef::Create(*stub, (*it)->offset())),
103                                 (*it)->addend());
104          reloc->setSymInfo(pReloc.symInfo());
105          islands.first->addRelocation(*reloc);
106        }
107
108        // add stub to the forward branch island
109        islands.first->addStub(prototype, pReloc, *stub);
110
111        // reset the branch target of the input reloc to this stub instead!
112        pReloc.setSymInfo(stub->symInfo());
113      }
114    }
115  }
116  return stub;
117}
118
119/// findPrototype - find if there is a registered stub prototype for the given
120/// relocation
121Stub* StubFactory::findPrototype(const Relocation& pReloc,
122                                 uint64_t pSource,
123                                 uint64_t pTargetSymValue)
124{
125  for (StubPoolType::iterator it = m_StubPool.begin(), ie = m_StubPool.end();
126       it != ie; ++it) {
127    if ((*it)->isMyDuty(pReloc, pSource, pTargetSymValue))
128      return (*it);
129  }
130  return NULL;
131}
132