forked from OSchip/llvm-project
416 lines
13 KiB
C++
416 lines
13 KiB
C++
//===- Core/Resolver.cpp - Resolves Atom References -----------------------===//
|
|
//
|
|
// The LLVM Linker
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "lld/Core/Atom.h"
|
|
#include "lld/Core/File.h"
|
|
#include "lld/Core/InputFiles.h"
|
|
#include "lld/Core/LLVM.h"
|
|
#include "lld/Core/Resolver.h"
|
|
#include "lld/Core/SymbolTable.h"
|
|
#include "lld/Core/TargetInfo.h"
|
|
#include "lld/Core/UndefinedAtom.h"
|
|
|
|
#include "llvm/Support/Debug.h"
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
#include "llvm/Support/Format.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
#include <algorithm>
|
|
#include <cassert>
|
|
#include <vector>
|
|
|
|
namespace lld {
|
|
|
|
/// This is used as a filter function to std::remove_if to dead strip atoms.
|
|
class NotLive {
|
|
public:
|
|
NotLive(const llvm::DenseSet<const Atom*>& la) : _liveAtoms(la) { }
|
|
|
|
bool operator()(const Atom *atom) const {
|
|
// don't remove if live
|
|
if ( _liveAtoms.count(atom) )
|
|
return false;
|
|
// don't remove if marked never-dead-strip
|
|
if (const DefinedAtom* defAtom = dyn_cast<DefinedAtom>(atom)) {
|
|
if ( defAtom->deadStrip() == DefinedAtom::deadStripNever )
|
|
return false;
|
|
}
|
|
// do remove this atom
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
const llvm::DenseSet<const Atom*> _liveAtoms;
|
|
};
|
|
|
|
|
|
/// This is used as a filter function to std::remove_if to coalesced atoms.
|
|
class AtomCoalescedAway {
|
|
public:
|
|
AtomCoalescedAway(SymbolTable &sym) : _symbolTable(sym) {}
|
|
|
|
bool operator()(const Atom *atom) const {
|
|
const Atom *rep = _symbolTable.replacement(atom);
|
|
return rep != atom;
|
|
}
|
|
|
|
private:
|
|
SymbolTable &_symbolTable;
|
|
};
|
|
|
|
|
|
// add all atoms from all initial .o files
|
|
void Resolver::buildInitialAtomList() {
|
|
DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "Resolver initial atom list:\n");
|
|
|
|
// each input files contributes initial atoms
|
|
_atoms.reserve(1024);
|
|
_inputFiles.forEachInitialAtom(*this);
|
|
|
|
_completedInitialObjectFiles = true;
|
|
}
|
|
|
|
|
|
// called before the first atom in any file is added with doAtom()
|
|
void Resolver::doFile(const File &file) {
|
|
}
|
|
|
|
|
|
void Resolver::doUndefinedAtom(const class UndefinedAtom& atom) {
|
|
DEBUG_WITH_TYPE("resolver", llvm::dbgs()
|
|
<< " UndefinedAtom: "
|
|
<< llvm::format("0x%09lX", &atom)
|
|
<< ", name="
|
|
<< atom.name()
|
|
<< "\n");
|
|
|
|
// add to list of known atoms
|
|
_atoms.push_back(&atom);
|
|
|
|
// tell symbol table
|
|
_symbolTable.add(atom);
|
|
}
|
|
|
|
|
|
// called on each atom when a file is added
|
|
void Resolver::doDefinedAtom(const DefinedAtom &atom) {
|
|
DEBUG_WITH_TYPE("resolver", llvm::dbgs()
|
|
<< " DefinedAtom: "
|
|
<< llvm::format("0x%09lX", &atom)
|
|
<< ", file=#"
|
|
<< atom.file().ordinal()
|
|
<< ", atom=#"
|
|
<< atom.ordinal()
|
|
<< ", name="
|
|
<< atom.name()
|
|
<< "\n");
|
|
|
|
// Verify on zero-size atoms are pinned to start or end of section.
|
|
switch ( atom.sectionPosition() ) {
|
|
case DefinedAtom::sectionPositionStart:
|
|
case DefinedAtom::sectionPositionEnd:
|
|
assert(atom.size() == 0);
|
|
break;
|
|
case DefinedAtom::sectionPositionEarly:
|
|
case DefinedAtom::sectionPositionAny:
|
|
break;
|
|
}
|
|
|
|
// add to list of known atoms
|
|
_atoms.push_back(&atom);
|
|
|
|
// tell symbol table
|
|
_symbolTable.add(atom);
|
|
|
|
if (_targetInfo.deadStrip()) {
|
|
// add to set of dead-strip-roots, all symbols that
|
|
// the compiler marks as don't strip
|
|
if (atom.deadStrip() == DefinedAtom::deadStripNever)
|
|
_deadStripRoots.insert(&atom);
|
|
}
|
|
}
|
|
|
|
void Resolver::doSharedLibraryAtom(const SharedLibraryAtom& atom) {
|
|
DEBUG_WITH_TYPE("resolver", llvm::dbgs()
|
|
<< " SharedLibraryAtom: "
|
|
<< llvm::format("0x%09lX", &atom)
|
|
<< ", name="
|
|
<< atom.name()
|
|
<< "\n");
|
|
|
|
// add to list of known atoms
|
|
_atoms.push_back(&atom);
|
|
|
|
// tell symbol table
|
|
_symbolTable.add(atom);
|
|
}
|
|
|
|
void Resolver::doAbsoluteAtom(const AbsoluteAtom& atom) {
|
|
DEBUG_WITH_TYPE("resolver", llvm::dbgs()
|
|
<< " AbsoluteAtom: "
|
|
<< llvm::format("0x%09lX", &atom)
|
|
<< ", name="
|
|
<< atom.name()
|
|
<< "\n");
|
|
|
|
// add to list of known atoms
|
|
_atoms.push_back(&atom);
|
|
|
|
// tell symbol table
|
|
if (atom.scope() != Atom::scopeTranslationUnit) {
|
|
_symbolTable.add(atom);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// utility to add a vector of atoms
|
|
void Resolver::addAtoms(const std::vector<const DefinedAtom*>& newAtoms) {
|
|
for (std::vector<const DefinedAtom *>::const_iterator it = newAtoms.begin();
|
|
it != newAtoms.end(); ++it) {
|
|
this->doDefinedAtom(**it);
|
|
}
|
|
}
|
|
|
|
// ask symbol table if any definitionUndefined atoms still exist
|
|
// if so, keep searching libraries until no more atoms being added
|
|
void Resolver::resolveUndefines() {
|
|
const bool searchArchives =
|
|
_targetInfo.searchArchivesToOverrideTentativeDefinitions();
|
|
const bool searchSharedLibs =
|
|
_targetInfo.searchSharedLibrariesToOverrideTentativeDefinitions();
|
|
|
|
// keep looping until no more undefines were added in last loop
|
|
unsigned int undefineGenCount = 0xFFFFFFFF;
|
|
while (undefineGenCount != _symbolTable.size()) {
|
|
undefineGenCount = _symbolTable.size();
|
|
std::vector<const UndefinedAtom *> undefines;
|
|
_symbolTable.undefines(undefines);
|
|
for ( const Atom *undefAtom : undefines ) {
|
|
StringRef undefName = undefAtom->name();
|
|
// load for previous undefine may also have loaded this undefine
|
|
if (!_symbolTable.isDefined(undefName)) {
|
|
_inputFiles.searchLibraries(undefName, true, true, false, *this);
|
|
}
|
|
}
|
|
// search libraries for overrides of common symbols
|
|
if (searchArchives || searchSharedLibs) {
|
|
std::vector<StringRef> tentDefNames;
|
|
_symbolTable.tentativeDefinitions(tentDefNames);
|
|
for ( StringRef tentDefName : tentDefNames ) {
|
|
// Load for previous tentative may also have loaded
|
|
// something that overrode this tentative, so always check.
|
|
const Atom *curAtom = _symbolTable.findByName(tentDefName);
|
|
assert(curAtom != nullptr);
|
|
if (const DefinedAtom* curDefAtom = dyn_cast<DefinedAtom>(curAtom)) {
|
|
if (curDefAtom->merge() == DefinedAtom::mergeAsTentative ) {
|
|
// Still tentative definition, so look for override.
|
|
_inputFiles.searchLibraries(tentDefName, searchSharedLibs,
|
|
searchArchives, true, *this);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// switch all references to undefined or coalesced away atoms
|
|
// to the new defined atom
|
|
void Resolver::updateReferences() {
|
|
for(const Atom *atom : _atoms) {
|
|
if (const DefinedAtom* defAtom = dyn_cast<DefinedAtom>(atom)) {
|
|
for (const Reference *ref : *defAtom) {
|
|
const Atom* newTarget = _symbolTable.replacement(ref->target());
|
|
(const_cast<Reference*>(ref))->setTarget(newTarget);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// for dead code stripping, recursively mark atoms "live"
|
|
void Resolver::markLive(const Atom &atom) {
|
|
// if already marked live, then done (stop recursion)
|
|
if ( _liveAtoms.count(&atom) )
|
|
return;
|
|
|
|
// mark this atom is live
|
|
_liveAtoms.insert(&atom);
|
|
|
|
// mark all atoms it references as live
|
|
if ( const DefinedAtom* defAtom = dyn_cast<DefinedAtom>(&atom)) {
|
|
for (const Reference *ref : *defAtom) {
|
|
const Atom *target = ref->target();
|
|
if ( target != nullptr )
|
|
this->markLive(*target);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// remove all atoms not actually used
|
|
void Resolver::deadStripOptimize() {
|
|
// only do this optimization with -dead_strip
|
|
if (!_targetInfo.deadStrip())
|
|
return;
|
|
|
|
// clear liveness on all atoms
|
|
_liveAtoms.clear();
|
|
|
|
// By default, shared libraries are built with all globals as dead strip roots
|
|
if (_targetInfo.globalsAreDeadStripRoots()) {
|
|
for ( const Atom *atom : _atoms ) {
|
|
const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(atom);
|
|
if (defAtom == nullptr)
|
|
continue;
|
|
if ( defAtom->scope() == DefinedAtom::scopeGlobal )
|
|
_deadStripRoots.insert(defAtom);
|
|
}
|
|
}
|
|
|
|
// Or, use list of names that are dead stip roots.
|
|
for (const StringRef &name : _targetInfo.deadStripRoots()) {
|
|
const Atom *symAtom = _symbolTable.findByName(name);
|
|
assert(symAtom->definition() != Atom::definitionUndefined);
|
|
_deadStripRoots.insert(symAtom);
|
|
}
|
|
|
|
// mark all roots as live, and recursively all atoms they reference
|
|
for ( const Atom *dsrAtom : _deadStripRoots) {
|
|
this->markLive(*dsrAtom);
|
|
}
|
|
|
|
// now remove all non-live atoms from _atoms
|
|
_atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(),
|
|
NotLive(_liveAtoms)), _atoms.end());
|
|
}
|
|
|
|
|
|
// error out if some undefines remain
|
|
bool Resolver::checkUndefines(bool final) {
|
|
// when using LTO, undefines are checked after bitcode is optimized
|
|
if (_haveLLVMObjs && !final)
|
|
return false;
|
|
|
|
// build vector of remaining undefined symbols
|
|
std::vector<const UndefinedAtom *> undefinedAtoms;
|
|
_symbolTable.undefines(undefinedAtoms);
|
|
if (_targetInfo.deadStrip()) {
|
|
// When dead code stripping, we don't care if dead atoms are undefined.
|
|
undefinedAtoms.erase(std::remove_if(
|
|
undefinedAtoms.begin(), undefinedAtoms.end(),
|
|
NotLive(_liveAtoms)), undefinedAtoms.end());
|
|
}
|
|
|
|
// error message about missing symbols
|
|
if (!undefinedAtoms.empty()) {
|
|
// FIXME: need diagonstics interface for writing error messages
|
|
bool foundUndefines = false;
|
|
for (const UndefinedAtom *undefAtom : undefinedAtoms) {
|
|
if (undefAtom->canBeNull() == UndefinedAtom::canBeNullNever) {
|
|
foundUndefines = true;
|
|
if (_targetInfo.printRemainingUndefines()) {
|
|
llvm::errs() << "Undefined Symbol: " << undefAtom->file().path()
|
|
<< " : " << undefAtom->name() << "\n";
|
|
}
|
|
}
|
|
}
|
|
if (foundUndefines) {
|
|
if (_targetInfo.printRemainingUndefines())
|
|
llvm::errs() << "symbol(s) not found\n";
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
// remove from _atoms all coaleseced away atoms
|
|
void Resolver::removeCoalescedAwayAtoms() {
|
|
_atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(),
|
|
AtomCoalescedAway(_symbolTable)), _atoms.end());
|
|
}
|
|
|
|
// check for interactions between symbols defined in this linkage unit
|
|
// and same symbol name in linked dynamic shared libraries
|
|
void Resolver::checkDylibSymbolCollisions() {
|
|
for ( const Atom *atom : _atoms ) {
|
|
const DefinedAtom* defAtom = dyn_cast<DefinedAtom>(atom);
|
|
if (defAtom == nullptr)
|
|
continue;
|
|
if ( defAtom->merge() != DefinedAtom::mergeAsTentative )
|
|
continue;
|
|
assert(defAtom->scope() != DefinedAtom::scopeTranslationUnit);
|
|
// See if any shared library also has symbol which
|
|
// collides with the tentative definition.
|
|
// SymbolTable will warn if needed.
|
|
_inputFiles.searchLibraries(defAtom->name(), true, false, false, *this);
|
|
}
|
|
}
|
|
|
|
|
|
void Resolver::linkTimeOptimize() {
|
|
// FIX ME
|
|
}
|
|
|
|
bool Resolver::resolve() {
|
|
this->buildInitialAtomList();
|
|
this->resolveUndefines();
|
|
this->updateReferences();
|
|
this->deadStripOptimize();
|
|
if (this->checkUndefines(false)) {
|
|
if (!_targetInfo.allowRemainingUndefines())
|
|
return true;
|
|
}
|
|
this->removeCoalescedAwayAtoms();
|
|
this->checkDylibSymbolCollisions();
|
|
this->linkTimeOptimize();
|
|
this->_result.addAtoms(_atoms);
|
|
return false;
|
|
}
|
|
|
|
void Resolver::MergedFile::addAtom(const Atom& atom) {
|
|
if (const DefinedAtom* defAtom = dyn_cast<DefinedAtom>(&atom)) {
|
|
_definedAtoms._atoms.push_back(defAtom);
|
|
} else if (const UndefinedAtom* undefAtom = dyn_cast<UndefinedAtom>(&atom)) {
|
|
_undefinedAtoms._atoms.push_back(undefAtom);
|
|
} else if (const SharedLibraryAtom* slAtom =
|
|
dyn_cast<SharedLibraryAtom>(&atom)) {
|
|
_sharedLibraryAtoms._atoms.push_back(slAtom);
|
|
} else if (const AbsoluteAtom* abAtom = dyn_cast<AbsoluteAtom>(&atom)) {
|
|
_absoluteAtoms._atoms.push_back(abAtom);
|
|
} else {
|
|
llvm_unreachable("atom has unknown definition kind");
|
|
}
|
|
}
|
|
|
|
|
|
MutableFile::DefinedAtomRange Resolver::MergedFile::definedAtoms() {
|
|
return range<std::vector<const DefinedAtom*>::iterator>(
|
|
_definedAtoms._atoms.begin(), _definedAtoms._atoms.end());
|
|
}
|
|
|
|
|
|
|
|
void Resolver::MergedFile::addAtoms(std::vector<const Atom*>& all) {
|
|
DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "Resolver final atom list:\n");
|
|
for ( const Atom *atom : all ) {
|
|
DEBUG_WITH_TYPE("resolver", llvm::dbgs()
|
|
<< llvm::format(" 0x%09lX", atom)
|
|
<< ", name="
|
|
<< atom->name()
|
|
<< "\n");
|
|
this->addAtom(*atom);
|
|
}
|
|
}
|
|
|
|
|
|
} // namespace lld
|