diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2015-03-24 21:31:36 +0000 | 
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2015-03-24 21:31:36 +0000 | 
| commit | fb911942f1434f3d1750f83f25f5e42c80e60638 (patch) | |
| tree | 1678c4a4f0182e4029a86d135aa4a1b7d09e3c41 /lib/Core/Resolver.cpp | |
Notes
Diffstat (limited to 'lib/Core/Resolver.cpp')
| -rw-r--r-- | lib/Core/Resolver.cpp | 516 | 
1 files changed, 516 insertions, 0 deletions
diff --git a/lib/Core/Resolver.cpp b/lib/Core/Resolver.cpp new file mode 100644 index 000000000000..393a7ef2bfc8 --- /dev/null +++ b/lib/Core/Resolver.cpp @@ -0,0 +1,516 @@ +//===- 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"); +    return; +  } + +  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() +                    << "\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() { +  ScopedTask task(getDefaultDomain(), "resolveUndefines"); +  int index = 0; +  std::set<File *> seen; +  for (;;) { +    bool undefAdded = false; +    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; +    } +    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() { +  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() { +  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() { +  // 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() { +  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() { +  updatePreloadArchiveMap(); +  if (!resolveUndefines()) +    return false; +  updateReferences(); +  deadStripOptimize(); +  if (checkUndefines()) +    if (!_ctx.allowRemainingUndefines()) +      return false; +  removeCoalescedAwayAtoms(); +  _result->addAtoms(_atoms); +  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) { +    DEBUG_WITH_TYPE("resolver", llvm::dbgs() +                    << llvm::format("    0x%09lX", atom) +                    << ", name=" +                    << atom->name() +                    << "\n"); +    addAtom(*atom); +  } +} + +} // namespace lld  | 
