[lld] [llvm] WIP: [LLD][COFF] Make unresolved symbol search behavior compliant with MSVC link.exe (PR #85290)

via llvm-commits llvm-commits at lists.llvm.org
Mon Mar 25 11:18:19 PDT 2024


================
@@ -562,25 +565,120 @@ std::pair<Symbol *, bool> SymbolTable::insert(StringRef name, InputFile *file) {
   return result;
 }
 
+static LazyIntrusiveNode *lazyNode(Symbol *s) {
+  if (auto *sym = dyn_cast<LazyArchive>(s))
+    return &sym->node;
+  if (auto *sym = dyn_cast<LazyObject>(s))
+    return &sym->node;
+  return nullptr;
+}
+
+static ArchiveFile *lazyParent(InputFile *f) {
+  if (!f)
+    return nullptr;
+  if (auto *obj = dyn_cast<ObjFile>(f))
+    return obj->parent;
+  if (auto *obj = dyn_cast<BitcodeFile>(f))
+    return obj->parent;
+  return nullptr;
+}
+
+static ArchiveFile *lazyArchive(Symbol *s) {
+  if (auto *sym = dyn_cast<LazyArchive>(s))
+    return sym->file;
+  if (auto *sym = dyn_cast<LazyObject>(s))
+    return lazyParent(sym->file);
+  return nullptr;
+}
+
+// The search behavior for undefined symbols is different when the OBJ
+// was pulled from an archive (LIB). This is documented here:
+// https://learn.microsoft.com/en-us/cpp/build/reference/link-input-files?view=msvc-170
+// "Object files on the command line are processed in the order they
+// appear on the command line. Libraries are searched in command line
+// order as well, with the following caveat: Symbols that are unresolved
+// when bringing in an object file from a library are searched for in
+// that library first, and then the following libraries from the command
+// line and /DEFAULTLIB (Specify default library) directives, and then
+// to any libraries at the beginning of the command line."
+static Symbol *searchArchiveSymbol(Symbol *s, ArchiveFile *pivot) {
+  auto &Alloc = getSpecificAllocSingleton<SymbolUnion>().Allocator;
+  Symbol *curr = s;
+  for (;;) {
+    if (lazyArchive(curr)->CmdLineIndex >= pivot->CmdLineIndex)
+      return curr;
+    uint32_t next = lazyNode(curr)->next;
+    if (!next)
+      break;
+    curr = reinterpret_cast<LazyArchive *>(
+        Alloc.fromAlignedIndex<SymbolUnion>(next));
+  }
+  return s;
+}
+
 Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f,
                                   bool isWeakAlias) {
   auto [s, wasInserted] = insert(name, f);
   if (wasInserted || (s->isLazy() && isWeakAlias)) {
     replaceSymbol<Undefined>(s, name);
     return s;
   }
-  if (s->isLazy())
+  if (s->isLazy()) {
+    if (s->pendingArchiveLoad)
+      return s;
+    if (ArchiveFile *parent = lazyParent(f)) {
+      // We're placing a undefined symbol from an archive OBJ. The rules are
+      // different than regular OBJs on the command-line.
+      Symbol *selected = searchArchiveSymbol(s, parent);
+      forceLazy(selected);
+      // Now that we have selected a symbol, we don't need the linked list of
+      // `LazyArchive`s anymore. Collapse to the selected symbol.
+      if (s != selected)
+        memcpy(s, selected, sizeof(SymbolUnion));
+      *lazyNode(s) = LazyIntrusiveNode();
+      return s;
+    }
+    // We're placing a undefined symbol from a command-line OBJ.
     forceLazy(s);
+  }
   return s;
 }
 
+// This creates a linked list of archives where a specific symbol was seen.
+// We later walk that list if a undefined symbol needs to be resolved from an
----------------
sylvain-audi wrote:

typo "if a undefined" -> "if an undefined"

https://github.com/llvm/llvm-project/pull/85290


More information about the llvm-commits mailing list