| //===- StubFactory.cpp ----------------------------------------------------===// |
| // |
| // The MCLinker Project |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| #include <mcld/LD/StubFactory.h> |
| #include <mcld/LD/BranchIslandFactory.h> |
| #include <mcld/LD/BranchIsland.h> |
| #include <mcld/LD/LDSymbol.h> |
| #include <mcld/LD/ResolveInfo.h> |
| #include <mcld/Fragment/Stub.h> |
| #include <mcld/Fragment/Relocation.h> |
| #include <mcld/Fragment/FragmentLinker.h> |
| #include <mcld/Fragment/FragmentRef.h> |
| |
| #include <string> |
| |
| using namespace mcld; |
| |
| //===----------------------------------------------------------------------===// |
| // StubFactory |
| //===----------------------------------------------------------------------===// |
| StubFactory::~StubFactory() |
| { |
| for (StubPoolType::iterator it = m_StubPool.begin(), ie = m_StubPool.end(); |
| it != ie; ++it) |
| delete(*it); |
| } |
| |
| /// addPrototype - register a stub prototype |
| void StubFactory::addPrototype(Stub* pPrototype) |
| { |
| m_StubPool.push_back(pPrototype); |
| } |
| |
| /// create - create a stub if needed, otherwise return NULL |
| Stub* StubFactory::create(Relocation& pReloc, |
| uint64_t pTargetSymValue, |
| FragmentLinker& pLinker, |
| BranchIslandFactory& pBRIslandFactory) |
| { |
| // find if there is a prototype stub for the input relocation |
| Stub* prototype = findPrototype(pReloc, |
| pReloc.place(), |
| pTargetSymValue); |
| if (NULL != prototype) { |
| // find the island for the input relocation |
| BranchIsland* island = pBRIslandFactory.find(*(pReloc.targetRef().frag())); |
| if (NULL == island) { |
| island = pBRIslandFactory.produce(*(pReloc.targetRef().frag())); |
| } |
| |
| // find if there is such a stub in the island already |
| assert(NULL != island); |
| Stub* stub = island->findStub(prototype, pReloc); |
| if (NULL != stub) { |
| // reset the branch target to the stub instead! |
| pReloc.setSymInfo(stub->symInfo()); |
| } |
| else { |
| // create a stub from the prototype |
| stub = prototype->clone(); |
| |
| // build a name for stub symbol |
| std::string name("__"); |
| name.append(pReloc.symInfo()->name()); |
| name.append("_"); |
| name.append(stub->name()); |
| name.append("@"); |
| name.append(island->name()); |
| |
| // create LDSymbol for the stub |
| LDSymbol* symbol = |
| pLinker.defineSymbol<FragmentLinker::Force, |
| FragmentLinker::Resolve>(name, |
| false, // isDyn |
| ResolveInfo::Function, |
| ResolveInfo::Define, |
| ResolveInfo::Local, |
| stub->size(), // size |
| stub->initSymValue(), // value |
| FragmentRef::Create(*stub, stub->initSymValue()), |
| ResolveInfo::Default); |
| stub->setSymInfo(symbol->resolveInfo()); |
| |
| // add relocations of this stub (i.e., set the branch target of the stub) |
| for (Stub::fixup_iterator it = stub->fixup_begin(), |
| ie = stub->fixup_end(); it != ie; ++it) { |
| |
| Relocation* reloc = Relocation::Create((*it)->type(), |
| *(FragmentRef::Create(*stub, (*it)->offset())), |
| (*it)->addend()); |
| reloc->setSymInfo(pReloc.symInfo()); |
| island->addRelocation(*reloc); |
| } |
| |
| // add stub to the branch island |
| island->addStub(prototype, pReloc, *stub); |
| |
| // reset the branch target of the input reloc to this stub instead! |
| pReloc.setSymInfo(stub->symInfo()); |
| return stub; |
| } |
| } |
| return NULL; |
| } |
| |
| /// findPrototype - find if there is a registered stub prototype for the given |
| /// relocation |
| Stub* StubFactory::findPrototype(const Relocation& pReloc, |
| uint64_t pSource, |
| uint64_t pTargetSymValue) |
| { |
| for (StubPoolType::iterator it = m_StubPool.begin(), ie = m_StubPool.end(); |
| it != ie; ++it) { |
| if ((*it)->isMyDuty(pReloc, pSource, pTargetSymValue)) |
| return (*it); |
| } |
| return NULL; |
| } |
| |