• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===- Core/Resolver.cpp - Resolves Atom References -----------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "lld/Core/Resolver.h"
10 #include "lld/Common/LLVM.h"
11 #include "lld/Core/ArchiveLibraryFile.h"
12 #include "lld/Core/Atom.h"
13 #include "lld/Core/File.h"
14 #include "lld/Core/Instrumentation.h"
15 #include "lld/Core/LinkingContext.h"
16 #include "lld/Core/SharedLibraryFile.h"
17 #include "lld/Core/SymbolTable.h"
18 #include "lld/Core/UndefinedAtom.h"
19 #include "llvm/ADT/iterator_range.h"
20 #include "llvm/Support/Debug.h"
21 #include "llvm/Support/Error.h"
22 #include "llvm/Support/ErrorHandling.h"
23 #include "llvm/Support/Format.h"
24 #include "llvm/Support/raw_ostream.h"
25 #include <algorithm>
26 #include <cassert>
27 #include <utility>
28 #include <vector>
29 
30 namespace lld {
31 
handleFile(File & file)32 llvm::Expected<bool> Resolver::handleFile(File &file) {
33   if (auto ec = _ctx.handleLoadedFile(file))
34     return std::move(ec);
35   bool undefAdded = false;
36   for (auto &atom : file.defined().owning_ptrs())
37     doDefinedAtom(std::move(atom));
38   for (auto &atom : file.undefined().owning_ptrs()) {
39     if (doUndefinedAtom(std::move(atom)))
40       undefAdded = true;
41   }
42   for (auto &atom : file.sharedLibrary().owning_ptrs())
43     doSharedLibraryAtom(std::move(atom));
44   for (auto &atom : file.absolute().owning_ptrs())
45     doAbsoluteAtom(std::move(atom));
46   return undefAdded;
47 }
48 
forEachUndefines(File & file,UndefCallback callback)49 llvm::Expected<bool> Resolver::forEachUndefines(File &file,
50                                                 UndefCallback callback) {
51   size_t i = _undefineIndex[&file];
52   bool undefAdded = false;
53   do {
54     for (; i < _undefines.size(); ++i) {
55       StringRef undefName = _undefines[i];
56       if (undefName.empty())
57         continue;
58       const Atom *atom = _symbolTable.findByName(undefName);
59       if (!isa<UndefinedAtom>(atom) || _symbolTable.isCoalescedAway(atom)) {
60         // The symbol was resolved by some other file. Cache the result.
61         _undefines[i] = "";
62         continue;
63       }
64       auto undefAddedOrError = callback(undefName);
65       if (auto ec = undefAddedOrError.takeError())
66         return std::move(ec);
67       undefAdded |= undefAddedOrError.get();
68     }
69   } while (i < _undefines.size());
70   _undefineIndex[&file] = i;
71   return undefAdded;
72 }
73 
handleArchiveFile(File & file)74 llvm::Expected<bool> Resolver::handleArchiveFile(File &file) {
75   ArchiveLibraryFile *archiveFile = cast<ArchiveLibraryFile>(&file);
76   return forEachUndefines(file,
77                           [&](StringRef undefName) -> llvm::Expected<bool> {
78     if (File *member = archiveFile->find(undefName)) {
79       member->setOrdinal(_ctx.getNextOrdinalAndIncrement());
80       return handleFile(*member);
81     }
82     return false;
83   });
84 }
85 
handleSharedLibrary(File & file)86 llvm::Error Resolver::handleSharedLibrary(File &file) {
87   // Add all the atoms from the shared library
88   SharedLibraryFile *sharedLibrary = cast<SharedLibraryFile>(&file);
89   auto undefAddedOrError = handleFile(*sharedLibrary);
90   if (auto ec = undefAddedOrError.takeError())
91     return ec;
92   undefAddedOrError =
93       forEachUndefines(file, [&](StringRef undefName) -> llvm::Expected<bool> {
94         auto atom = sharedLibrary->exports(undefName);
95         if (atom.get())
96           doSharedLibraryAtom(std::move(atom));
97         return false;
98       });
99 
100   if (auto ec = undefAddedOrError.takeError())
101     return ec;
102   return llvm::Error::success();
103 }
104 
doUndefinedAtom(OwningAtomPtr<UndefinedAtom> atom)105 bool Resolver::doUndefinedAtom(OwningAtomPtr<UndefinedAtom> atom) {
106   DEBUG_WITH_TYPE("resolver", llvm::dbgs()
107                     << "       UndefinedAtom: "
108                     << llvm::format("0x%09lX", atom.get())
109                     << ", name=" << atom.get()->name() << "\n");
110 
111   // tell symbol table
112   bool newUndefAdded = _symbolTable.add(*atom.get());
113   if (newUndefAdded)
114     _undefines.push_back(atom.get()->name());
115 
116   // add to list of known atoms
117   _atoms.push_back(OwningAtomPtr<Atom>(atom.release()));
118 
119   return newUndefAdded;
120 }
121 
122 // Called on each atom when a file is added. Returns true if a given
123 // atom is added to the symbol table.
doDefinedAtom(OwningAtomPtr<DefinedAtom> atom)124 void Resolver::doDefinedAtom(OwningAtomPtr<DefinedAtom> atom) {
125   DEBUG_WITH_TYPE("resolver", llvm::dbgs()
126                     << "         DefinedAtom: "
127                     << llvm::format("0x%09lX", atom.get())
128                     << ", file=#"
129                     << atom.get()->file().ordinal()
130                     << ", atom=#"
131                     << atom.get()->ordinal()
132                     << ", name="
133                     << atom.get()->name()
134                     << ", type="
135                     << atom.get()->contentType()
136                     << "\n");
137 
138   // An atom that should never be dead-stripped is a dead-strip root.
139   if (_ctx.deadStrip() &&
140       atom.get()->deadStrip() == DefinedAtom::deadStripNever) {
141     _deadStripRoots.insert(atom.get());
142   }
143 
144   // add to list of known atoms
145   _symbolTable.add(*atom.get());
146   _atoms.push_back(OwningAtomPtr<Atom>(atom.release()));
147 }
148 
doSharedLibraryAtom(OwningAtomPtr<SharedLibraryAtom> atom)149 void Resolver::doSharedLibraryAtom(OwningAtomPtr<SharedLibraryAtom> atom) {
150   DEBUG_WITH_TYPE("resolver", llvm::dbgs()
151                     << "   SharedLibraryAtom: "
152                     << llvm::format("0x%09lX", atom.get())
153                     << ", name="
154                     << atom.get()->name()
155                     << "\n");
156 
157   // tell symbol table
158   _symbolTable.add(*atom.get());
159 
160   // add to list of known atoms
161   _atoms.push_back(OwningAtomPtr<Atom>(atom.release()));
162 }
163 
doAbsoluteAtom(OwningAtomPtr<AbsoluteAtom> atom)164 void Resolver::doAbsoluteAtom(OwningAtomPtr<AbsoluteAtom> atom) {
165   DEBUG_WITH_TYPE("resolver", llvm::dbgs()
166                     << "       AbsoluteAtom: "
167                     << llvm::format("0x%09lX", atom.get())
168                     << ", name="
169                     << atom.get()->name()
170                     << "\n");
171 
172   // tell symbol table
173   if (atom.get()->scope() != Atom::scopeTranslationUnit)
174     _symbolTable.add(*atom.get());
175 
176   // add to list of known atoms
177   _atoms.push_back(OwningAtomPtr<Atom>(atom.release()));
178 }
179 
180 // Returns true if at least one of N previous files has created an
181 // undefined symbol.
undefinesAdded(int begin,int end)182 bool Resolver::undefinesAdded(int begin, int end) {
183   std::vector<std::unique_ptr<Node>> &inputs = _ctx.getNodes();
184   for (int i = begin; i < end; ++i)
185     if (FileNode *node = dyn_cast<FileNode>(inputs[i].get()))
186       if (_newUndefinesAdded[node->getFile()])
187         return true;
188   return false;
189 }
190 
getFile(int & index)191 File *Resolver::getFile(int &index) {
192   std::vector<std::unique_ptr<Node>> &inputs = _ctx.getNodes();
193   if ((size_t)index >= inputs.size())
194     return nullptr;
195   if (GroupEnd *group = dyn_cast<GroupEnd>(inputs[index].get())) {
196     // We are at the end of the current group. If one or more new
197     // undefined atom has been added in the last groupSize files, we
198     // reiterate over the files.
199     int size = group->getSize();
200     if (undefinesAdded(index - size, index)) {
201       index -= size;
202       return getFile(index);
203     }
204     ++index;
205     return getFile(index);
206   }
207   return cast<FileNode>(inputs[index++].get())->getFile();
208 }
209 
210 // Keep adding atoms until _ctx.getNextFile() returns an error. This
211 // function is where undefined atoms are resolved.
resolveUndefines()212 bool Resolver::resolveUndefines() {
213   DEBUG_WITH_TYPE("resolver",
214                   llvm::dbgs() << "******** Resolving undefines:\n");
215   ScopedTask task(getDefaultDomain(), "resolveUndefines");
216   int index = 0;
217   std::set<File *> seen;
218   for (;;) {
219     bool undefAdded = false;
220     DEBUG_WITH_TYPE("resolver",
221                     llvm::dbgs() << "Loading file #" << index << "\n");
222     File *file = getFile(index);
223     if (!file)
224       return true;
225     if (std::error_code ec = file->parse()) {
226       llvm::errs() << "Cannot open " + file->path() << ": " << ec.message()
227                    << "\n";
228       return false;
229     }
230     DEBUG_WITH_TYPE("resolver",
231                     llvm::dbgs() << "Loaded file: " << file->path() << "\n");
232     switch (file->kind()) {
233     case File::kindErrorObject:
234     case File::kindNormalizedObject:
235     case File::kindMachObject:
236     case File::kindCEntryObject:
237     case File::kindHeaderObject:
238     case File::kindEntryObject:
239     case File::kindUndefinedSymsObject:
240     case File::kindStubHelperObject:
241     case File::kindResolverMergedObject:
242     case File::kindSectCreateObject: {
243       // The same file may be visited more than once if the file is
244       // in --start-group and --end-group. Only library files should
245       // be processed more than once.
246       if (seen.count(file))
247         break;
248       seen.insert(file);
249       assert(!file->hasOrdinal());
250       file->setOrdinal(_ctx.getNextOrdinalAndIncrement());
251       auto undefAddedOrError = handleFile(*file);
252       if (auto EC = undefAddedOrError.takeError()) {
253         // FIXME: This should be passed to logAllUnhandledErrors but it needs
254         // to be passed a Twine instead of a string.
255         llvm::errs() << "Error in " + file->path() << ": ";
256         logAllUnhandledErrors(std::move(EC), llvm::errs(), std::string());
257         return false;
258       }
259       undefAdded = undefAddedOrError.get();
260       break;
261     }
262     case File::kindArchiveLibrary: {
263       if (!file->hasOrdinal())
264         file->setOrdinal(_ctx.getNextOrdinalAndIncrement());
265       auto undefAddedOrError = handleArchiveFile(*file);
266       if (auto EC = undefAddedOrError.takeError()) {
267         // FIXME: This should be passed to logAllUnhandledErrors but it needs
268         // to be passed a Twine instead of a string.
269         llvm::errs() << "Error in " + file->path() << ": ";
270         logAllUnhandledErrors(std::move(EC), llvm::errs(), std::string());
271         return false;
272       }
273       undefAdded = undefAddedOrError.get();
274       break;
275     }
276     case File::kindSharedLibrary:
277       if (!file->hasOrdinal())
278         file->setOrdinal(_ctx.getNextOrdinalAndIncrement());
279       if (auto EC = handleSharedLibrary(*file)) {
280         // FIXME: This should be passed to logAllUnhandledErrors but it needs
281         // to be passed a Twine instead of a string.
282         llvm::errs() << "Error in " + file->path() << ": ";
283         logAllUnhandledErrors(std::move(EC), llvm::errs(), std::string());
284         return false;
285       }
286       break;
287     }
288     _newUndefinesAdded[file] = undefAdded;
289   }
290 }
291 
292 // switch all references to undefined or coalesced away atoms
293 // to the new defined atom
updateReferences()294 void Resolver::updateReferences() {
295   DEBUG_WITH_TYPE("resolver",
296                   llvm::dbgs() << "******** Updating references:\n");
297   ScopedTask task(getDefaultDomain(), "updateReferences");
298   for (const OwningAtomPtr<Atom> &atom : _atoms) {
299     if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(atom.get())) {
300       for (const Reference *ref : *defAtom) {
301         // A reference of type kindAssociate shouldn't be updated.
302         // Instead, an atom having such reference will be removed
303         // if the target atom is coalesced away, so that they will
304         // go away as a group.
305         if (ref->kindNamespace() == lld::Reference::KindNamespace::all &&
306             ref->kindValue() == lld::Reference::kindAssociate) {
307           if (_symbolTable.isCoalescedAway(atom.get()))
308             _deadAtoms.insert(ref->target());
309           continue;
310         }
311         const Atom *newTarget = _symbolTable.replacement(ref->target());
312         const_cast<Reference *>(ref)->setTarget(newTarget);
313       }
314     }
315   }
316 }
317 
318 // For dead code stripping, recursively mark atoms "live"
markLive(const Atom * atom)319 void Resolver::markLive(const Atom *atom) {
320   // Mark the atom is live. If it's already marked live, then stop recursion.
321   auto exists = _liveAtoms.insert(atom);
322   if (!exists.second)
323     return;
324 
325   // Mark all atoms it references as live
326   if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(atom)) {
327     for (const Reference *ref : *defAtom)
328       markLive(ref->target());
329     for (auto &p : llvm::make_range(_reverseRef.equal_range(defAtom))) {
330       const Atom *target = p.second;
331       markLive(target);
332     }
333   }
334 }
335 
isBackref(const Reference * ref)336 static bool isBackref(const Reference *ref) {
337   if (ref->kindNamespace() != lld::Reference::KindNamespace::all)
338     return false;
339   return (ref->kindValue() == lld::Reference::kindLayoutAfter);
340 }
341 
342 // remove all atoms not actually used
deadStripOptimize()343 void Resolver::deadStripOptimize() {
344   DEBUG_WITH_TYPE("resolver",
345                   llvm::dbgs() << "******** Dead stripping unused atoms:\n");
346   ScopedTask task(getDefaultDomain(), "deadStripOptimize");
347   // only do this optimization with -dead_strip
348   if (!_ctx.deadStrip())
349     return;
350 
351   // Some type of references prevent referring atoms to be dead-striped.
352   // Make a reverse map of such references before traversing the graph.
353   // While traversing the list of atoms, mark AbsoluteAtoms as live
354   // in order to avoid reclaim.
355   for (const OwningAtomPtr<Atom> &atom : _atoms) {
356     if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(atom.get()))
357       for (const Reference *ref : *defAtom)
358         if (isBackref(ref))
359           _reverseRef.insert(std::make_pair(ref->target(), atom.get()));
360     if (const AbsoluteAtom *absAtom = dyn_cast<AbsoluteAtom>(atom.get()))
361       markLive(absAtom);
362   }
363 
364   // By default, shared libraries are built with all globals as dead strip roots
365   if (_ctx.globalsAreDeadStripRoots())
366     for (const OwningAtomPtr<Atom> &atom : _atoms)
367       if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(atom.get()))
368         if (defAtom->scope() == DefinedAtom::scopeGlobal)
369           _deadStripRoots.insert(defAtom);
370 
371   // Or, use list of names that are dead strip roots.
372   for (const StringRef &name : _ctx.deadStripRoots()) {
373     const Atom *symAtom = _symbolTable.findByName(name);
374     assert(symAtom);
375     _deadStripRoots.insert(symAtom);
376   }
377 
378   // mark all roots as live, and recursively all atoms they reference
379   for (const Atom *dsrAtom : _deadStripRoots)
380     markLive(dsrAtom);
381 
382   // now remove all non-live atoms from _atoms
383   _atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(),
384                               [&](OwningAtomPtr<Atom> &a) {
385                  return _liveAtoms.count(a.get()) == 0;
386                }),
387                _atoms.end());
388 }
389 
390 // error out if some undefines remain
checkUndefines()391 bool Resolver::checkUndefines() {
392   DEBUG_WITH_TYPE("resolver",
393                   llvm::dbgs() << "******** Checking for undefines:\n");
394 
395   // build vector of remaining undefined symbols
396   std::vector<const UndefinedAtom *> undefinedAtoms = _symbolTable.undefines();
397   if (_ctx.deadStrip()) {
398     // When dead code stripping, we don't care if dead atoms are undefined.
399     undefinedAtoms.erase(
400         std::remove_if(undefinedAtoms.begin(), undefinedAtoms.end(),
401                        [&](const Atom *a) { return _liveAtoms.count(a) == 0; }),
402         undefinedAtoms.end());
403   }
404 
405   if (undefinedAtoms.empty())
406     return false;
407 
408   // Warn about unresolved symbols.
409   bool foundUndefines = false;
410   for (const UndefinedAtom *undef : undefinedAtoms) {
411     // Skip over a weak symbol.
412     if (undef->canBeNull() != UndefinedAtom::canBeNullNever)
413       continue;
414 
415     // If this is a library and undefined symbols are allowed on the
416     // target platform, skip over it.
417     if (isa<SharedLibraryFile>(undef->file()) && _ctx.allowShlibUndefines())
418       continue;
419 
420     // If the undefine is coalesced away, skip over it.
421     if (_symbolTable.isCoalescedAway(undef))
422       continue;
423 
424     // Seems like this symbol is undefined. Warn that.
425     foundUndefines = true;
426     if (_ctx.printRemainingUndefines()) {
427       llvm::errs() << "Undefined symbol: " << undef->file().path() << ": "
428                    << _ctx.demangle(undef->name()) << "\n";
429     }
430   }
431   if (!foundUndefines)
432     return false;
433   if (_ctx.printRemainingUndefines())
434     llvm::errs() << "symbol(s) not found\n";
435   return true;
436 }
437 
438 // Remove from _atoms all coalesced away atoms.
removeCoalescedAwayAtoms()439 void Resolver::removeCoalescedAwayAtoms() {
440   DEBUG_WITH_TYPE("resolver",
441                   llvm::dbgs() << "******** Removing coalesced away atoms:\n");
442   ScopedTask task(getDefaultDomain(), "removeCoalescedAwayAtoms");
443   _atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(),
444                               [&](OwningAtomPtr<Atom> &a) {
445                  return _symbolTable.isCoalescedAway(a.get()) ||
446                         _deadAtoms.count(a.get());
447                }),
448                _atoms.end());
449 }
450 
resolve()451 bool Resolver::resolve() {
452   DEBUG_WITH_TYPE("resolver",
453                   llvm::dbgs() << "******** Resolving atom references:\n");
454   if (!resolveUndefines())
455     return false;
456   updateReferences();
457   deadStripOptimize();
458   if (checkUndefines()) {
459     DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "Found undefines... ");
460     if (!_ctx.allowRemainingUndefines()) {
461       DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "which we don't allow\n");
462       return false;
463     }
464     DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "which we are ok with\n");
465   }
466   removeCoalescedAwayAtoms();
467   _result->addAtoms(_atoms);
468   DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "******** Finished resolver\n");
469   return true;
470 }
471 
addAtoms(llvm::MutableArrayRef<OwningAtomPtr<Atom>> all)472 void Resolver::MergedFile::addAtoms(
473                               llvm::MutableArrayRef<OwningAtomPtr<Atom>> all) {
474   ScopedTask task(getDefaultDomain(), "addAtoms");
475   DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "Resolver final atom list:\n");
476 
477   for (OwningAtomPtr<Atom> &atom : all) {
478 #ifndef NDEBUG
479     if (auto *definedAtom = dyn_cast<DefinedAtom>(atom.get())) {
480       DEBUG_WITH_TYPE("resolver", llvm::dbgs()
481                       << llvm::format("    0x%09lX", definedAtom)
482                       << ", file=#"
483                       << definedAtom->file().ordinal()
484                       << ", atom=#"
485                       << definedAtom->ordinal()
486                       << ", name="
487                       << definedAtom->name()
488                       << ", type="
489                       << definedAtom->contentType()
490                       << "\n");
491     } else {
492       DEBUG_WITH_TYPE("resolver", llvm::dbgs()
493                       << llvm::format("    0x%09lX", atom.get())
494                       << ", name="
495                       << atom.get()->name()
496                       << "\n");
497     }
498 #endif
499     addAtom(*atom.release());
500   }
501 }
502 
503 } // namespace lld
504