//===- 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/IRBuilder.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/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,
IRBuilder& pBuilder,
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 =
pBuilder.AddSymbol<IRBuilder::Force, IRBuilder::Resolve>(
name,
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;
}