| //===- 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/ArchiveLibraryFile.h" |
| #include "lld/Core/File.h" |
| #include "lld/Core/Instrumentation.h" |
| #include "lld/Core/LLVM.h" |
| #include "lld/Core/LinkingContext.h" |
| #include "lld/Core/Resolver.h" |
| #include "lld/Core/SharedLibraryFile.h" |
| #include "lld/Core/SymbolTable.h" |
| #include "lld/Core/UndefinedAtom.h" |
| #include "llvm/ADT/iterator_range.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 <utility> |
| #include <vector> |
| |
| namespace lld { |
| |
| bool Resolver::handleFile(File &file) { |
| bool undefAdded = false; |
| for (const DefinedAtom *atom : file.defined()) |
| doDefinedAtom(*atom); |
| for (const UndefinedAtom *atom : file.undefined()) { |
| if (doUndefinedAtom(*atom)) { |
| undefAdded = true; |
| maybePreloadArchiveMember(atom->name()); |
| } |
| } |
| for (const SharedLibraryAtom *atom : file.sharedLibrary()) |
| doSharedLibraryAtom(*atom); |
| for (const AbsoluteAtom *atom : file.absolute()) |
| doAbsoluteAtom(*atom); |
| return undefAdded; |
| } |
| |
| void Resolver::forEachUndefines(File &file, bool searchForOverrides, |
| UndefCallback callback) { |
| size_t i = _undefineIndex[&file]; |
| do { |
| for (; i < _undefines.size(); ++i) { |
| StringRef undefName = _undefines[i]; |
| if (undefName.empty()) |
| continue; |
| const Atom *atom = _symbolTable.findByName(undefName); |
| if (!isa<UndefinedAtom>(atom) || _symbolTable.isCoalescedAway(atom)) { |
| // The symbol was resolved by some other file. Cache the result. |
| _undefines[i] = ""; |
| continue; |
| } |
| callback(undefName, false); |
| } |
| if (!searchForOverrides) |
| continue; |
| for (StringRef tentDefName : _symbolTable.tentativeDefinitions()) { |
| // 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) |
| callback(tentDefName, true); |
| } |
| } while (i < _undefines.size()); |
| _undefineIndex[&file] = i; |
| } |
| |
| bool Resolver::handleArchiveFile(File &file) { |
| ArchiveLibraryFile *archiveFile = cast<ArchiveLibraryFile>(&file); |
| bool searchForOverrides = |
| _ctx.searchArchivesToOverrideTentativeDefinitions(); |
| bool undefAdded = false; |
| forEachUndefines(file, searchForOverrides, |
| [&](StringRef undefName, bool dataSymbolOnly) { |
| if (File *member = archiveFile->find(undefName, dataSymbolOnly)) { |
| member->setOrdinal(_ctx.getNextOrdinalAndIncrement()); |
| member->beforeLink(); |
| updatePreloadArchiveMap(); |
| undefAdded = handleFile(*member) || undefAdded; |
| } |
| }); |
| return undefAdded; |
| } |
| |
| void Resolver::handleSharedLibrary(File &file) { |
| // Add all the atoms from the shared library |
| SharedLibraryFile *sharedLibrary = cast<SharedLibraryFile>(&file); |
| handleFile(*sharedLibrary); |
| bool searchForOverrides = |
| _ctx.searchSharedLibrariesToOverrideTentativeDefinitions(); |
| forEachUndefines(file, searchForOverrides, |
| [&](StringRef undefName, bool dataSymbolOnly) { |
| if (const SharedLibraryAtom *atom = |
| sharedLibrary->exports(undefName, dataSymbolOnly)) |
| doSharedLibraryAtom(*atom); |
| }); |
| } |
| |
| bool Resolver::doUndefinedAtom(const 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 |
| bool newUndefAdded = _symbolTable.add(atom); |
| if (newUndefAdded) |
| _undefines.push_back(atom.name()); |
| |
| // If the undefined symbol has an alternative name, try to resolve the |
| // symbol with the name to give it a second chance. This feature is used |
| // for COFF "weak external" symbol. |
| if (newUndefAdded || !_symbolTable.isDefined(atom.name())) { |
| if (const UndefinedAtom *fallbackAtom = atom.fallback()) { |
| doUndefinedAtom(*fallbackAtom); |
| _symbolTable.addReplacement(&atom, fallbackAtom); |
| } |
| } |
| return newUndefAdded; |
| } |
| |
| /// \brief Add the section group and the group-child reference members. |
| void Resolver::maybeAddSectionGroupOrGnuLinkOnce(const DefinedAtom &atom) { |
| // First time adding a group? |
| bool isFirstTime = _symbolTable.addGroup(atom); |
| |
| if (!isFirstTime) { |
| // If duplicate symbols are allowed, select the first group. |
| if (_ctx.getAllowDuplicates()) |
| return; |
| auto *prevGroup = dyn_cast<DefinedAtom>(_symbolTable.findGroup(atom.name())); |
| assert(prevGroup && |
| "Internal Error: The group atom could only be a defined atom"); |
| // The atoms should be of the same content type, reject invalid group |
| // resolution behaviors. |
| if (atom.contentType() == prevGroup->contentType()) |
| return; |
| llvm::errs() << "SymbolTable: error while merging " << atom.name() |
| << "\n"; |
| llvm::report_fatal_error("duplicate symbol error"); |
| } |
| |
| for (const Reference *r : atom) { |
| if (r->kindNamespace() == lld::Reference::KindNamespace::all && |
| r->kindValue() == lld::Reference::kindGroupChild) { |
| const DefinedAtom *target = dyn_cast<DefinedAtom>(r->target()); |
| assert(target && "Internal Error: kindGroupChild references need to " |
| "be associated with Defined Atoms only"); |
| _atoms.push_back(target); |
| _symbolTable.add(*target); |
| } |
| } |
| } |
| |
| // Called on each atom when a file is added. Returns true if a given |
| // atom is added to the symbol table. |
| 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() |
| << ", type=" |
| << atom.contentType() |
| << "\n"); |
| |
| // add to list of known atoms |
| _atoms.push_back(&atom); |
| |
| if (atom.isGroupParent()) { |
| maybeAddSectionGroupOrGnuLinkOnce(atom); |
| } else { |
| _symbolTable.add(atom); |
| } |
| |
| // An atom that should never be dead-stripped is a dead-strip root. |
| if (_ctx.deadStrip() && 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 (const DefinedAtom *newAtom : newAtoms) |
| doDefinedAtom(*newAtom); |
| } |
| |
| // Instantiate an archive file member if there's a file containing a |
| // defined symbol for a given symbol name. Instantiation is done in a |
| // different worker thread and has no visible side effect. |
| void Resolver::maybePreloadArchiveMember(StringRef sym) { |
| auto it = _archiveMap.find(sym); |
| if (it == _archiveMap.end()) |
| return; |
| ArchiveLibraryFile *archive = it->second; |
| archive->preload(_ctx.getTaskGroup(), sym); |
| } |
| |
| // Returns true if at least one of N previous files has created an |
| // undefined symbol. |
| bool Resolver::undefinesAdded(int begin, int end) { |
| std::vector<std::unique_ptr<Node>> &inputs = _ctx.getNodes(); |
| for (int i = begin; i < end; ++i) |
| if (FileNode *node = dyn_cast<FileNode>(inputs[i].get())) |
| if (_newUndefinesAdded[node->getFile()]) |
| return true; |
| return false; |
| } |
| |
| File *Resolver::getFile(int &index) { |
| std::vector<std::unique_ptr<Node>> &inputs = _ctx.getNodes(); |
| if ((size_t)index >= inputs.size()) |
| return nullptr; |
| if (GroupEnd *group = dyn_cast<GroupEnd>(inputs[index].get())) { |
| // We are at the end of the current group. If one or more new |
| // undefined atom has been added in the last groupSize files, we |
| // reiterate over the files. |
| int size = group->getSize(); |
| if (undefinesAdded(index - size, index)) { |
| index -= size; |
| return getFile(index); |
| } |
| ++index; |
| return getFile(index); |
| } |
| return cast<FileNode>(inputs[index++].get())->getFile(); |
| } |
| |
| // Update a map of Symbol -> ArchiveFile. The map is used for speculative |
| // file loading. |
| void Resolver::updatePreloadArchiveMap() { |
| std::vector<std::unique_ptr<Node>> &nodes = _ctx.getNodes(); |
| for (int i = nodes.size() - 1; i >= 0; --i) { |
| auto *fnode = dyn_cast<FileNode>(nodes[i].get()); |
| if (!fnode) |
| continue; |
| auto *archive = dyn_cast<ArchiveLibraryFile>(fnode->getFile()); |
| if (!archive || _archiveSeen.count(archive)) |
| continue; |
| _archiveSeen.insert(archive); |
| for (StringRef sym : archive->getDefinedSymbols()) |
| _archiveMap[sym] = archive; |
| } |
| } |
| |
| // Keep adding atoms until _ctx.getNextFile() returns an error. This |
| // function is where undefined atoms are resolved. |
| bool Resolver::resolveUndefines() { |
| DEBUG_WITH_TYPE("resolver", |
| llvm::dbgs() << "******** Resolving undefines:\n"); |
| ScopedTask task(getDefaultDomain(), "resolveUndefines"); |
| int index = 0; |
| std::set<File *> seen; |
| for (;;) { |
| bool undefAdded = false; |
| DEBUG_WITH_TYPE("resolver", |
| llvm::dbgs() << "Loading file #" << index << "\n"); |
| File *file = getFile(index); |
| if (!file) |
| return true; |
| if (std::error_code ec = file->parse()) { |
| llvm::errs() << "Cannot open " + file->path() |
| << ": " << ec.message() << "\n"; |
| return false; |
| } |
| DEBUG_WITH_TYPE("resolver", |
| llvm::dbgs() << "Loaded file: " << file->path() << "\n"); |
| file->beforeLink(); |
| updatePreloadArchiveMap(); |
| switch (file->kind()) { |
| case File::kindObject: |
| // The same file may be visited more than once if the file is |
| // in --start-group and --end-group. Only library files should |
| // be processed more than once. |
| if (seen.count(file)) |
| break; |
| seen.insert(file); |
| assert(!file->hasOrdinal()); |
| file->setOrdinal(_ctx.getNextOrdinalAndIncrement()); |
| undefAdded = handleFile(*file); |
| break; |
| case File::kindArchiveLibrary: |
| if (!file->hasOrdinal()) |
| file->setOrdinal(_ctx.getNextOrdinalAndIncrement()); |
| undefAdded = handleArchiveFile(*file); |
| break; |
| case File::kindSharedLibrary: |
| if (!file->hasOrdinal()) |
| file->setOrdinal(_ctx.getNextOrdinalAndIncrement()); |
| handleSharedLibrary(*file); |
| break; |
| } |
| _newUndefinesAdded[file] = undefAdded; |
| } |
| } |
| |
| // switch all references to undefined or coalesced away atoms |
| // to the new defined atom |
| void Resolver::updateReferences() { |
| DEBUG_WITH_TYPE("resolver", |
| llvm::dbgs() << "******** Updating references:\n"); |
| ScopedTask task(getDefaultDomain(), "updateReferences"); |
| for (const Atom *atom : _atoms) { |
| if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(atom)) { |
| for (const Reference *ref : *defAtom) { |
| // A reference of type kindAssociate should't be updated. |
| // Instead, an atom having such reference will be removed |
| // if the target atom is coalesced away, so that they will |
| // go away as a group. |
| if (ref->kindNamespace() == lld::Reference::KindNamespace::all && |
| ref->kindValue() == lld::Reference::kindAssociate) { |
| if (_symbolTable.isCoalescedAway(atom)) |
| _deadAtoms.insert(ref->target()); |
| continue; |
| } |
| 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) { |
| // Mark the atom is live. If it's already marked live, then stop recursion. |
| auto exists = _liveAtoms.insert(atom); |
| if (!exists.second) |
| return; |
| |
| // Mark all atoms it references as live |
| if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(atom)) { |
| for (const Reference *ref : *defAtom) |
| markLive(ref->target()); |
| for (auto &p : llvm::make_range(_reverseRef.equal_range(defAtom))) { |
| const Atom *target = p.second; |
| markLive(target); |
| } |
| } |
| } |
| |
| static bool isBackref(const Reference *ref) { |
| if (ref->kindNamespace() != lld::Reference::KindNamespace::all) |
| return false; |
| return (ref->kindValue() == lld::Reference::kindLayoutAfter || |
| ref->kindValue() == lld::Reference::kindGroupChild); |
| } |
| |
| // remove all atoms not actually used |
| void Resolver::deadStripOptimize() { |
| DEBUG_WITH_TYPE("resolver", |
| llvm::dbgs() << "******** Dead stripping unused atoms:\n"); |
| ScopedTask task(getDefaultDomain(), "deadStripOptimize"); |
| // only do this optimization with -dead_strip |
| if (!_ctx.deadStrip()) |
| return; |
| |
| // Some type of references prevent referring atoms to be dead-striped. |
| // Make a reverse map of such references before traversing the graph. |
| // While traversing the list of atoms, mark AbsoluteAtoms as live |
| // in order to avoid reclaim. |
| for (const Atom *atom : _atoms) { |
| if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(atom)) |
| for (const Reference *ref : *defAtom) |
| if (isBackref(ref)) |
| _reverseRef.insert(std::make_pair(ref->target(), atom)); |
| if (const AbsoluteAtom *absAtom = dyn_cast<AbsoluteAtom>(atom)) |
| markLive(absAtom); |
| } |
| |
| // By default, shared libraries are built with all globals as dead strip roots |
| if (_ctx.globalsAreDeadStripRoots()) |
| for (const Atom *atom : _atoms) |
| if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(atom)) |
| if (defAtom->scope() == DefinedAtom::scopeGlobal) |
| _deadStripRoots.insert(defAtom); |
| |
| // Or, use list of names that are dead strip roots. |
| for (const StringRef &name : _ctx.deadStripRoots()) { |
| const Atom *symAtom = _symbolTable.findByName(name); |
| assert(symAtom); |
| _deadStripRoots.insert(symAtom); |
| } |
| |
| // mark all roots as live, and recursively all atoms they reference |
| for (const Atom *dsrAtom : _deadStripRoots) |
| markLive(dsrAtom); |
| |
| // now remove all non-live atoms from _atoms |
| _atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(), [&](const Atom *a) { |
| return _liveAtoms.count(a) == 0; |
| }), |
| _atoms.end()); |
| } |
| |
| // error out if some undefines remain |
| bool Resolver::checkUndefines() { |
| DEBUG_WITH_TYPE("resolver", |
| llvm::dbgs() << "******** Checking for undefines:\n"); |
| |
| // build vector of remaining undefined symbols |
| std::vector<const UndefinedAtom *> undefinedAtoms = _symbolTable.undefines(); |
| if (_ctx.deadStrip()) { |
| // When dead code stripping, we don't care if dead atoms are undefined. |
| undefinedAtoms.erase( |
| std::remove_if(undefinedAtoms.begin(), undefinedAtoms.end(), |
| [&](const Atom *a) { return _liveAtoms.count(a) == 0; }), |
| undefinedAtoms.end()); |
| } |
| |
| if (undefinedAtoms.empty()) |
| return false; |
| |
| // Warn about unresolved symbols. |
| bool foundUndefines = false; |
| for (const UndefinedAtom *undef : undefinedAtoms) { |
| // Skip over a weak symbol. |
| if (undef->canBeNull() != UndefinedAtom::canBeNullNever) |
| continue; |
| |
| // If this is a library and undefined symbols are allowed on the |
| // target platform, skip over it. |
| if (isa<SharedLibraryFile>(undef->file()) && _ctx.allowShlibUndefines()) |
| continue; |
| |
| // If the undefine is coalesced away, skip over it. |
| if (_symbolTable.isCoalescedAway(undef)) |
| continue; |
| |
| // Seems like this symbol is undefined. Warn that. |
| foundUndefines = true; |
| if (_ctx.printRemainingUndefines()) { |
| llvm::errs() << "Undefined symbol: " << undef->file().path() |
| << ": " << _ctx.demangle(undef->name()) |
| << "\n"; |
| } |
| } |
| if (!foundUndefines) |
| return false; |
| if (_ctx.printRemainingUndefines()) |
| llvm::errs() << "symbol(s) not found\n"; |
| return true; |
| } |
| |
| // remove from _atoms all coaleseced away atoms |
| void Resolver::removeCoalescedAwayAtoms() { |
| DEBUG_WITH_TYPE("resolver", |
| llvm::dbgs() << "******** Removing coalesced away atoms:\n"); |
| ScopedTask task(getDefaultDomain(), "removeCoalescedAwayAtoms"); |
| _atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(), [&](const Atom *a) { |
| return _symbolTable.isCoalescedAway(a) || _deadAtoms.count(a); |
| }), |
| _atoms.end()); |
| } |
| |
| bool Resolver::resolve() { |
| DEBUG_WITH_TYPE("resolver", |
| llvm::dbgs() << "******** Resolving atom references:\n"); |
| updatePreloadArchiveMap(); |
| if (!resolveUndefines()) |
| return false; |
| updateReferences(); |
| deadStripOptimize(); |
| if (checkUndefines()) { |
| DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "Found undefines... "); |
| if (!_ctx.allowRemainingUndefines()) { |
| DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "which we don't allow\n"); |
| return false; |
| } |
| DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "which we are ok with\n"); |
| } |
| removeCoalescedAwayAtoms(); |
| _result->addAtoms(_atoms); |
| DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "******** Finished resolver\n"); |
| return true; |
| } |
| |
| void Resolver::MergedFile::addAtoms(std::vector<const Atom *> &all) { |
| ScopedTask task(getDefaultDomain(), "addAtoms"); |
| DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "Resolver final atom list:\n"); |
| |
| for (const Atom *atom : all) { |
| #ifndef NDEBUG |
| if (auto *definedAtom = dyn_cast<DefinedAtom>(atom)) { |
| DEBUG_WITH_TYPE("resolver", llvm::dbgs() |
| << llvm::format(" 0x%09lX", atom) |
| << ", file=#" |
| << definedAtom->file().ordinal() |
| << ", atom=#" |
| << definedAtom->ordinal() |
| << ", name=" |
| << definedAtom->name() |
| << ", type=" |
| << definedAtom->contentType() |
| << "\n"); |
| } else { |
| DEBUG_WITH_TYPE("resolver", llvm::dbgs() |
| << llvm::format(" 0x%09lX", atom) |
| << ", name=" |
| << atom->name() |
| << "\n"); |
| } |
| #endif |
| addAtom(*atom); |
| } |
| } |
| |
| } // namespace lld |