2012-03-08 08:18:30 +08:00
|
|
|
//===- Passes/StubsPass.cpp - Adds stubs ----------------------------------===//
|
|
|
|
//
|
|
|
|
// The LLVM Linker
|
|
|
|
//
|
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
//
|
2012-03-16 07:36:24 +08:00
|
|
|
// This linker pass updates call sites which have references to shared library
|
|
|
|
// atoms to instead have a reference to a stub (PLT entry) for the specified
|
|
|
|
// symbol. The platform object does the work of creating the platform-specific
|
|
|
|
// StubAtom.
|
2012-03-08 08:18:30 +08:00
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
#include "llvm/ADT/DenseMap.h"
|
|
|
|
|
2012-03-16 07:36:24 +08:00
|
|
|
#include "lld/Core/DefinedAtom.h"
|
2012-03-08 08:18:30 +08:00
|
|
|
#include "lld/Core/Pass.h"
|
|
|
|
#include "lld/Core/File.h"
|
|
|
|
#include "lld/Core/Reference.h"
|
|
|
|
#include "lld/Platform/Platform.h"
|
|
|
|
|
|
|
|
|
|
|
|
namespace lld {
|
|
|
|
|
|
|
|
void StubsPass::perform() {
|
2012-03-16 07:36:24 +08:00
|
|
|
// Skip this pass if output format uses text relocations instead of stubs.
|
|
|
|
if ( !_platform.noTextRelocs() )
|
2012-03-08 08:18:30 +08:00
|
|
|
return;
|
|
|
|
|
2012-03-16 07:36:24 +08:00
|
|
|
// Use map so all call sites to same shlib symbol use same stub.
|
|
|
|
llvm::DenseMap<const Atom*, const DefinedAtom*> targetToStub;
|
2012-03-08 08:18:30 +08:00
|
|
|
|
|
|
|
// Scan all references in all atoms.
|
|
|
|
for(auto ait=_file.definedAtomsBegin(), aend=_file.definedAtomsEnd();
|
|
|
|
ait != aend; ++ait) {
|
|
|
|
const DefinedAtom* atom = *ait;
|
|
|
|
for (auto rit=atom->referencesBegin(), rend=atom->referencesEnd();
|
|
|
|
rit != rend; ++rit) {
|
|
|
|
const Reference* ref = *rit;
|
2012-03-16 07:36:24 +08:00
|
|
|
// Look at call-sites.
|
|
|
|
if ( _platform.isCallSite(ref->kind()) ) {
|
|
|
|
const Atom* target = ref->target();
|
|
|
|
assert(target != NULL);
|
|
|
|
bool replaceCalleeWithStub = false;
|
|
|
|
if ( target->definition() == Atom::definitionSharedLibrary ) {
|
|
|
|
// Calls to shared libraries go through stubs.
|
|
|
|
replaceCalleeWithStub = true;
|
|
|
|
}
|
|
|
|
else if ( const DefinedAtom* defTarget = target->definedAtom() ) {
|
|
|
|
if ( defTarget->interposable() != DefinedAtom::interposeNo ) {
|
|
|
|
// Calls to interposable functions in same linkage unit
|
|
|
|
// must also go through a stub.
|
|
|
|
assert(defTarget->scope() != DefinedAtom::scopeTranslationUnit);
|
|
|
|
replaceCalleeWithStub = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( replaceCalleeWithStub ) {
|
|
|
|
// Replace the reference's target with a stub.
|
|
|
|
const DefinedAtom* stub;
|
|
|
|
auto pos = targetToStub.find(target);
|
|
|
|
if ( pos == targetToStub.end() ) {
|
2012-03-08 08:18:30 +08:00
|
|
|
// This is no existing stub. Create a new one.
|
2012-03-16 07:36:24 +08:00
|
|
|
stub = _platform.makeStub(*target, _file);
|
|
|
|
assert(stub != NULL);
|
|
|
|
assert(stub->contentType() == DefinedAtom::typeStub);
|
|
|
|
targetToStub[target] = stub;
|
2012-03-08 08:18:30 +08:00
|
|
|
}
|
|
|
|
else {
|
2012-03-16 07:36:24 +08:00
|
|
|
// Reuse an existing stub.
|
2012-03-08 08:18:30 +08:00
|
|
|
stub = pos->second;
|
2012-03-16 07:36:24 +08:00
|
|
|
assert(stub != NULL);
|
2012-03-08 08:18:30 +08:00
|
|
|
}
|
2012-03-16 07:36:24 +08:00
|
|
|
// Switch call site to reference stub atom.
|
2012-03-08 08:18:30 +08:00
|
|
|
(const_cast<Reference*>(ref))->setTarget(stub);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// add all created stubs to file
|
2012-03-16 07:36:24 +08:00
|
|
|
for (auto it=targetToStub.begin(), end=targetToStub.end(); it != end; ++it) {
|
2012-03-08 08:18:30 +08:00
|
|
|
_file.addAtom(*it->second);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|