[lld] 3d85424 - [ELF] Parse archives as --start-lib object files

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Tue Feb 15 09:38:05 PST 2022


Author: Fangrui Song
Date: 2022-02-15T09:38:00-08:00
New Revision: 3d85424096ff1e20ca735cbe455870cea7ed098f

URL: https://github.com/llvm/llvm-project/commit/3d85424096ff1e20ca735cbe455870cea7ed098f
DIFF: https://github.com/llvm/llvm-project/commit/3d85424096ff1e20ca735cbe455870cea7ed098f.diff

LOG: [ELF] Parse archives as --start-lib object files

https://maskray.me/blog/2022-01-16-archives-and-start-lib

For every definition in an extracted archive member, we intern the symbol twice,
once for the archive index entry, once for the .o symbol table after extraction.
This is inefficient.

Symbols in a --start-lib ObjFile/BitcodeFile are only interned once because the
result is cached in symbols[i].

Just handle an archive using the --start-lib code path. We can therefore remove
ArchiveFile and LazyArchive. For many projects, archive member extraction ratio
is high and it is a net performance win. Linking a Release build of clang is
1.01x as fast.

Note: --start-lib scans symbols in the same order that llvm-ar adds them to the
index, so in the common case the semantics should be identical. If the archive
symbol table was created in a different order, or is incomplete, this strategy
may have different semantics. Such cases are considered user error.

The `is neither ET_REL nor LLVM bitcode` error is changed to a warning.
Previously an archive may have such members without a diagnostic. Using a
warning prevents breakage.

* For some tests, the diagnostics get improved where we did not consider
  the archive member name: `b.a:` => `b.a(b.o):`.
* `no-obj.s`: the link is now allowed, matching GNU ld
* `archive-no-index.s`: the `is neither ET_REL nor LLVM bitcode` diagnostic is
  demoted to a warning.
* `incompatible.s`: even when an archive is unextracted, we may report an
  "incompatible with" error.

---

I recently decreased sizeof(SymbolUnion) by 8 and decreased memory usage quite a
bit, so retaining `symbols` for un-extracted archive members should not cause a
memory usage problem.

Reviewed By: peter.smith

Differential Revision: https://reviews.llvm.org/D119074

Added: 
    lld/test/ELF/archive-as-start-lib.s

Modified: 
    lld/ELF/Driver.cpp
    lld/ELF/Driver.h
    lld/ELF/InputFiles.cpp
    lld/ELF/InputFiles.h
    lld/ELF/MapFile.cpp
    lld/ELF/Symbols.cpp
    lld/ELF/Symbols.h
    lld/test/ELF/archive-no-index.s
    lld/test/ELF/archive-thin-missing-member.s
    lld/test/ELF/incompatible-ar-first.s
    lld/test/ELF/incompatible.s
    lld/test/ELF/lto/comdat-mixed-archive.test
    lld/test/ELF/lto/exclude-libs-libcall.ll
    lld/test/ELF/no-obj.s
    lld/test/ELF/trace-symbols.s

Removed: 
    


################################################################################
diff  --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index f586d012df727..aee700d02ff4c 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -49,6 +49,7 @@
 #include "llvm/ADT/StringSwitch.h"
 #include "llvm/Config/llvm-config.h"
 #include "llvm/LTO/LTO.h"
+#include "llvm/Object/Archive.h"
 #include "llvm/Remarks/HotnessThresholdParser.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Compression.h"
@@ -96,7 +97,6 @@ bool elf::link(ArrayRef<const char *> args, llvm::raw_ostream &stdoutOS,
     inputSections.clear();
     outputSections.clear();
     memoryBuffers.clear();
-    archiveFiles.clear();
     binaryFiles.clear();
     bitcodeFiles.clear();
     lazyBitcodeFiles.clear();
@@ -228,32 +228,30 @@ void LinkerDriver::addFile(StringRef path, bool withLOption) {
       return;
     }
 
-    std::unique_ptr<Archive> file =
-        CHECK(Archive::create(mbref), path + ": failed to parse archive");
-
-    // If an archive file has no symbol table, it may be intentional (used as a
-    // group of lazy object files where the symbol table is not useful), or the
-    // user is attempting LTO and using a default ar command that doesn't
-    // understand the LLVM bitcode file. Treat the archive as a group of lazy
-    // object files.
-    if (file->isEmpty() || file->hasSymbolTable()) {
-      // Handle the regular case.
-      files.push_back(make<ArchiveFile>(std::move(file)));
-      return;
-    }
-
+    auto members = getArchiveMembers(mbref);
+    archiveFiles.emplace_back(path, members.size());
+
+    // Handle archives and --start-lib/--end-lib using the same code path. This
+    // scans all the ELF relocatable object files and bitcode files in the
+    // archive rather than just the index file, with the benefit that the
+    // symbols are only loaded once. For many projects archives see high
+    // utilization rates and it is a net performance win. --start-lib scans
+    // symbols in the same order that llvm-ar adds them to the index, so in the
+    // common case the semantics are identical. If the archive symbol table was
+    // created in a 
diff erent order, or is incomplete, this strategy has
+    // 
diff erent semantics. Such output 
diff erences are considered user error.
+    //
     // All files within the archive get the same group ID to allow mutual
     // references for --warn-backrefs.
     bool saved = InputFile::isInGroup;
     InputFile::isInGroup = true;
-    for (const std::pair<MemoryBufferRef, uint64_t> &p :
-         getArchiveMembers(mbref)) {
+    for (const std::pair<MemoryBufferRef, uint64_t> &p : members) {
       auto magic = identify_magic(p.first.getBuffer());
       if (magic == file_magic::bitcode || magic == file_magic::elf_relocatable)
         files.push_back(createLazyFile(p.first, path, p.second));
       else
-        error(path + ": archive member '" + p.first.getBufferIdentifier() +
-              "' is neither ET_REL nor LLVM bitcode");
+        warn(path + ": archive member '" + p.first.getBufferIdentifier() +
+             "' is neither ET_REL nor LLVM bitcode");
     }
     InputFile::isInGroup = saved;
     if (!saved)
@@ -1470,13 +1468,16 @@ void LinkerDriver::createFiles(opt::InputArgList &args) {
 
   // Iterate over argv to process input files and positional arguments.
   InputFile::isInGroup = false;
+  bool hasInput = false;
   for (auto *arg : args) {
     switch (arg->getOption().getID()) {
     case OPT_library:
       addLibrary(arg->getValue());
+      hasInput = true;
       break;
     case OPT_INPUT:
       addFile(arg->getValue(), /*withLOption=*/false);
+      hasInput = true;
       break;
     case OPT_defsym: {
       StringRef from;
@@ -1565,7 +1566,7 @@ void LinkerDriver::createFiles(opt::InputArgList &args) {
     }
   }
 
-  if (files.empty() && errorCount() == 0)
+  if (files.empty() && !hasInput && errorCount() == 0)
     error("no input files");
 }
 
@@ -1721,10 +1722,7 @@ static void handleLibcall(StringRef name) {
     return;
 
   MemoryBufferRef mb;
-  if (auto *lo = dyn_cast<LazyObject>(sym))
-    mb = lo->file->mb;
-  else
-    mb = cast<LazyArchive>(sym)->getMemberBuffer();
+  mb = cast<LazyObject>(sym)->file->mb;
 
   if (isBitcode(mb))
     sym->extract();

diff  --git a/lld/ELF/Driver.h b/lld/ELF/Driver.h
index 878b5d30a2b60..8a7e5e4e2df47 100644
--- a/lld/ELF/Driver.h
+++ b/lld/ELF/Driver.h
@@ -42,6 +42,9 @@ class LinkerDriver {
   std::unique_ptr<BitcodeCompiler> lto;
 
   std::vector<InputFile *> files;
+
+public:
+  SmallVector<std::pair<StringRef, unsigned>, 0> archiveFiles;
 };
 
 // Parses command line options.

diff  --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp
index 9f759e6817905..4f5c69f4e08ac 100644
--- a/lld/ELF/InputFiles.cpp
+++ b/lld/ELF/InputFiles.cpp
@@ -43,7 +43,6 @@ bool InputFile::isInGroup;
 uint32_t InputFile::nextGroupId;
 
 SmallVector<std::unique_ptr<MemoryBuffer>> elf::memoryBuffers;
-SmallVector<ArchiveFile *, 0> elf::archiveFiles;
 SmallVector<BinaryFile *, 0> elf::binaryFiles;
 SmallVector<BitcodeFile *, 0> elf::bitcodeFiles;
 SmallVector<BitcodeFile *, 0> elf::lazyBitcodeFiles;
@@ -176,13 +175,6 @@ template <class ELFT> static void doParseFile(InputFile *file) {
     return;
   }
 
-  // .a file
-  if (auto *f = dyn_cast<ArchiveFile>(file)) {
-    archiveFiles.push_back(f);
-    f->parse();
-    return;
-  }
-
   // Lazy object file
   if (file->lazy) {
     if (auto *f = dyn_cast<BitcodeFile>(file)) {
@@ -1116,14 +1108,12 @@ void ObjFile<ELFT>::initializeSymbols(const object::ELFFile<ELFT> &obj) {
     // defined symbol in a .eh_frame becomes dangling symbols.
     if (sec == &InputSection::discarded) {
       Undefined und{this, StringRef(), binding, stOther, type, secIdx};
-      // !ArchiveFile::parsed or !LazyObjFile::lazy means that the file
-      // containing this object has not finished processing, i.e. this symbol is
-      // a result of a lazy symbol extract. We should demote the lazy symbol to
-      // an Undefined so that any relocations outside of the group to it will
-      // trigger a discarded section error.
-      if ((sym->symbolKind == Symbol::LazyArchiveKind &&
-           !cast<ArchiveFile>(sym->file)->parsed) ||
-          (sym->symbolKind == Symbol::LazyObjectKind && !sym->file->lazy)) {
+      // !LazyObjFile::lazy indicates that the file containing this object has
+      // not finished processing, i.e. this symbol is a result of a lazy symbol
+      // extract. We should demote the lazy symbol to an Undefined so that any
+      // relocations outside of the group to it will trigger a discarded section
+      // error.
+      if (sym->symbolKind == Symbol::LazyObjectKind && !sym->file->lazy) {
         sym->replace(und);
         // Prevent LTO from internalizing the symbol in case there is a
         // reference to this symbol from this file.
@@ -1159,44 +1149,6 @@ void ObjFile<ELFT>::initializeSymbols(const object::ELFFile<ELFT> &obj) {
   }
 }
 
-ArchiveFile::ArchiveFile(std::unique_ptr<Archive> &&file)
-    : InputFile(ArchiveKind, file->getMemoryBufferRef()),
-      file(std::move(file)) {}
-
-void ArchiveFile::parse() {
-  SymbolTable &symtab = *elf::symtab;
-  for (const Archive::Symbol &sym : file->symbols())
-    symtab.addSymbol(LazyArchive{*this, sym});
-
-  // Inform a future invocation of ObjFile<ELFT>::initializeSymbols() that this
-  // archive has been processed.
-  parsed = true;
-}
-
-// Returns a buffer pointing to a member file containing a given symbol.
-void ArchiveFile::extract(const Archive::Symbol &sym) {
-  Archive::Child c =
-      CHECK(sym.getMember(), toString(this) +
-                                 ": could not get the member for symbol " +
-                                 toELFString(sym));
-
-  if (!seen.insert(c.getChildOffset()).second)
-    return;
-
-  MemoryBufferRef mb =
-      CHECK(c.getMemoryBufferRef(),
-            toString(this) +
-                ": could not get the buffer for the member defining symbol " +
-                toELFString(sym));
-
-  if (tar && c.getParent()->isThin())
-    tar->append(relativeToRoot(CHECK(c.getFullName(), this)), mb.getBuffer());
-
-  InputFile *file = createObjectFile(mb, getName(), c.getChildOffset());
-  file->groupId = groupId;
-  parseFile(file);
-}
-
 // The handling of tentative definitions (COMMON symbols) in archives is murky.
 // A tentative definition will be promoted to a global definition if there are
 // no non-tentative definitions to dominate it. When we hold a tentative
@@ -1263,36 +1215,6 @@ static bool isNonCommonDef(MemoryBufferRef mb, StringRef symName,
   }
 }
 
-bool ArchiveFile::shouldExtractForCommon(const Archive::Symbol &sym) {
-  Archive::Child c =
-      CHECK(sym.getMember(), toString(this) +
-                                 ": could not get the member for symbol " +
-                                 toELFString(sym));
-  MemoryBufferRef mb =
-      CHECK(c.getMemoryBufferRef(),
-            toString(this) +
-                ": could not get the buffer for the member defining symbol " +
-                toELFString(sym));
-
-  if (isBitcode(mb))
-    return isBitcodeNonCommonDef(mb, sym.getName(), getName());
-
-  return isNonCommonDef(mb, sym.getName(), getName());
-}
-
-size_t ArchiveFile::getMemberCount() const {
-  size_t count = 0;
-  Error err = Error::success();
-  for (const Archive::Child &c : file->children(err)) {
-    (void)c;
-    ++count;
-  }
-  // This function is used by --print-archive-stats=, where an error does not
-  // really matter.
-  consumeError(std::move(err));
-  return count;
-}
-
 unsigned SharedFile::vernauxNum;
 
 // Parse the version definitions in the object file if present, and return a

diff  --git a/lld/ELF/InputFiles.h b/lld/ELF/InputFiles.h
index 7634586205d7e..bbd3072c54d5f 100644
--- a/lld/ELF/InputFiles.h
+++ b/lld/ELF/InputFiles.h
@@ -15,8 +15,8 @@
 #include "lld/Common/Reproduce.h"
 #include "llvm/ADT/DenseSet.h"
 #include "llvm/BinaryFormat/Magic.h"
-#include "llvm/Object/Archive.h"
 #include "llvm/Object/ELF.h"
+#include "llvm/Support/MemoryBufferRef.h"
 #include "llvm/Support/Threading.h"
 
 namespace llvm {
@@ -35,8 +35,6 @@ std::string toString(const elf::InputFile *f);
 
 namespace elf {
 
-using llvm::object::Archive;
-
 class InputSection;
 class Symbol;
 
@@ -310,33 +308,6 @@ template <class ELFT> class ObjFile : public ELFFileBase {
   llvm::once_flag initDwarf;
 };
 
-// An ArchiveFile object represents a .a file.
-class ArchiveFile : public InputFile {
-public:
-  explicit ArchiveFile(std::unique_ptr<Archive> &&file);
-  static bool classof(const InputFile *f) { return f->kind() == ArchiveKind; }
-  void parse();
-
-  // Pulls out an object file that contains a definition for Sym and
-  // returns it. If the same file was instantiated before, this
-  // function does nothing (so we don't instantiate the same file
-  // more than once.)
-  void extract(const Archive::Symbol &sym);
-
-  // Check if a non-common symbol should be extracted to override a common
-  // definition.
-  bool shouldExtractForCommon(const Archive::Symbol &sym);
-
-  size_t getMemberCount() const;
-  size_t getExtractedMemberCount() const { return seen.size(); }
-
-  bool parsed = false;
-
-private:
-  std::unique_ptr<Archive> file;
-  llvm::DenseSet<uint64_t> seen;
-};
-
 class BitcodeFile : public InputFile {
 public:
   BitcodeFile(MemoryBufferRef m, StringRef archiveName,
@@ -403,7 +374,6 @@ inline bool isBitcode(MemoryBufferRef mb) {
 std::string replaceThinLTOSuffix(StringRef path);
 
 extern SmallVector<std::unique_ptr<MemoryBuffer>> memoryBuffers;
-extern SmallVector<ArchiveFile *, 0> archiveFiles;
 extern SmallVector<BinaryFile *, 0> binaryFiles;
 extern SmallVector<BitcodeFile *, 0> bitcodeFiles;
 extern SmallVector<BitcodeFile *, 0> lazyBitcodeFiles;

diff  --git a/lld/ELF/MapFile.cpp b/lld/ELF/MapFile.cpp
index a201ec5995a77..54cacb8780dc8 100644
--- a/lld/ELF/MapFile.cpp
+++ b/lld/ELF/MapFile.cpp
@@ -19,6 +19,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "MapFile.h"
+#include "Driver.h"
 #include "InputFiles.h"
 #include "LinkerScript.h"
 #include "OutputSections.h"
@@ -307,7 +308,19 @@ void elf::writeArchiveStats() {
   }
 
   os << "members\textracted\tarchive\n";
-  for (const ArchiveFile *f : archiveFiles)
-    os << f->getMemberCount() << '\t' << f->getExtractedMemberCount() << '\t'
-       << f->getName() << '\n';
+
+  SmallVector<StringRef, 0> archives;
+  DenseMap<CachedHashStringRef, unsigned> all, extracted;
+  for (ELFFileBase *file : objectFiles)
+    if (file->archiveName.size())
+      ++extracted[CachedHashStringRef(file->archiveName)];
+  for (BitcodeFile *file : bitcodeFiles)
+    if (file->archiveName.size())
+      ++extracted[CachedHashStringRef(file->archiveName)];
+  for (std::pair<StringRef, unsigned> f : driver->archiveFiles) {
+    unsigned &v = extracted[CachedHashString(f.first)];
+    os << f.second << '\t' << v << '\t' << f.first << '\n';
+    // If the archive occurs multiple times, other instances have a count of 0.
+    v = 0;
+  }
 }

diff  --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp
index 86a281980ba59..e604d350574bc 100644
--- a/lld/ELF/Symbols.cpp
+++ b/lld/ELF/Symbols.cpp
@@ -33,10 +33,6 @@ std::string lld::toString(const elf::Symbol &sym) {
   return ret;
 }
 
-std::string lld::toELFString(const Archive::Symbol &b) {
-  return demangle(b.getName(), config->demangle);
-}
-
 Defined *ElfSym::bss;
 Defined *ElfSym::etext1;
 Defined *ElfSym::etext2;
@@ -129,7 +125,6 @@ static uint64_t getSymVA(const Symbol &sym, int64_t addend) {
   case Symbol::SharedKind:
   case Symbol::UndefinedKind:
     return 0;
-  case Symbol::LazyArchiveKind:
   case Symbol::LazyObjectKind:
     llvm_unreachable("lazy symbol reached writer");
   case Symbol::CommonKind:
@@ -246,24 +241,12 @@ void Symbol::parseSymbolVersion() {
 }
 
 void Symbol::extract() const {
-  if (auto *sym = dyn_cast<LazyArchive>(this)) {
-    cast<ArchiveFile>(sym->file)->extract(sym->sym);
-  } else if (file->lazy) {
+  if (file->lazy) {
     file->lazy = false;
     parseFile(file);
   }
 }
 
-MemoryBufferRef LazyArchive::getMemberBuffer() {
-  Archive::Child c =
-      CHECK(sym.getMember(),
-            "could not get the member for symbol " + toELFString(sym));
-
-  return CHECK(c.getMemoryBufferRef(),
-               "could not get the buffer for the member defining symbol " +
-                   toELFString(sym));
-}
-
 uint8_t Symbol::computeBinding() const {
   if ((visibility != STV_DEFAULT && visibility != STV_PROTECTED) ||
       versionId == VER_NDX_LOCAL)
@@ -428,9 +411,6 @@ void Symbol::resolve(const Symbol &other) {
   case Symbol::DefinedKind:
     resolveDefined(cast<Defined>(other));
     break;
-  case Symbol::LazyArchiveKind:
-    resolveLazy(cast<LazyArchive>(other));
-    break;
   case Symbol::LazyObjectKind:
     resolveLazy(cast<LazyObject>(other));
     break;
@@ -691,14 +671,7 @@ template <class LazyT> void Symbol::resolveLazy(const LazyT &other) {
   // For common objects, we want to look for global or weak definitions that
   // should be extracted as the canonical definition instead.
   if (isCommon() && elf::config->fortranCommon) {
-    if (auto *laSym = dyn_cast<LazyArchive>(&other)) {
-      ArchiveFile *archive = cast<ArchiveFile>(laSym->file);
-      const Archive::Symbol &archiveSym = laSym->sym;
-      if (archive->shouldExtractForCommon(archiveSym)) {
-        replaceCommon(*this, other);
-        return;
-      }
-    } else if (auto *loSym = dyn_cast<LazyObject>(&other)) {
+    if (auto *loSym = dyn_cast<LazyObject>(&other)) {
       if (loSym->file->shouldExtractForCommon(getName())) {
         replaceCommon(*this, other);
         return;

diff  --git a/lld/ELF/Symbols.h b/lld/ELF/Symbols.h
index c499964ec35dd..6cd8370317124 100644
--- a/lld/ELF/Symbols.h
+++ b/lld/ELF/Symbols.h
@@ -17,7 +17,6 @@
 #include "lld/Common/LLVM.h"
 #include "lld/Common/Memory.h"
 #include "llvm/ADT/DenseMap.h"
-#include "llvm/Object/Archive.h"
 #include "llvm/Object/ELF.h"
 #include <tuple>
 
@@ -25,11 +24,6 @@ namespace lld {
 // Returns a string representation for a symbol for diagnostics.
 std::string toString(const elf::Symbol &);
 
-// There are two 
diff erent ways to convert an Archive::Symbol to a string:
-// One for Microsoft name mangling and one for Itanium name mangling.
-// Call the functions toCOFFString and toELFString, not just toString.
-std::string toELFString(const llvm::object::Archive::Symbol &);
-
 namespace elf {
 class CommonSymbol;
 class Defined;
@@ -59,7 +53,6 @@ class Symbol {
     CommonKind,
     SharedKind,
     UndefinedKind,
-    LazyArchiveKind,
     LazyObjectKind,
   };
 
@@ -152,9 +145,7 @@ class Symbol {
 
   bool isLocal() const { return binding == llvm::ELF::STB_LOCAL; }
 
-  bool isLazy() const {
-    return symbolKind == LazyArchiveKind || symbolKind == LazyObjectKind;
-  }
+  bool isLazy() const { return symbolKind == LazyObjectKind; }
 
   // True if this is an undefined weak symbol. This only works once
   // all input files have been added.
@@ -417,36 +408,15 @@ class SharedSymbol : public Symbol {
   uint32_t alignment;
 };
 
-// LazyArchive and LazyObject represent a symbols that is not yet in the link,
-// but we know where to find it if needed. If the resolver finds both Undefined
-// and Lazy for the same name, it will ask the Lazy to load a file.
+// LazyObject symbols represent symbols in object files between --start-lib and
+// --end-lib options. LLD also handles traditional archives as if all the files
+// in the archive are surrounded by --start-lib and --end-lib.
 //
 // A special complication is the handling of weak undefined symbols. They should
 // not load a file, but we have to remember we have seen both the weak undefined
 // and the lazy. We represent that with a lazy symbol with a weak binding. This
 // means that code looking for undefined symbols normally also has to take lazy
 // symbols into consideration.
-
-// This class represents a symbol defined in an archive file. It is
-// created from an archive file header, and it knows how to load an
-// object file from an archive to replace itself with a defined
-// symbol.
-class LazyArchive : public Symbol {
-public:
-  LazyArchive(InputFile &file, const llvm::object::Archive::Symbol s)
-      : Symbol(LazyArchiveKind, &file, s.getName(), llvm::ELF::STB_GLOBAL,
-               llvm::ELF::STV_DEFAULT, llvm::ELF::STT_NOTYPE),
-        sym(s) {}
-
-  static bool classof(const Symbol *s) { return s->kind() == LazyArchiveKind; }
-
-  MemoryBufferRef getMemberBuffer();
-
-  const llvm::object::Archive::Symbol sym;
-};
-
-// LazyObject symbols represents symbols in object files between
-// --start-lib and --end-lib options.
 class LazyObject : public Symbol {
 public:
   LazyObject(InputFile &file)
@@ -505,8 +475,7 @@ union SymbolUnion {
   alignas(CommonSymbol) char b[sizeof(CommonSymbol)];
   alignas(Undefined) char c[sizeof(Undefined)];
   alignas(SharedSymbol) char d[sizeof(SharedSymbol)];
-  alignas(LazyArchive) char e[sizeof(LazyArchive)];
-  alignas(LazyObject) char f[sizeof(LazyObject)];
+  alignas(LazyObject) char e[sizeof(LazyObject)];
 };
 
 // It is important to keep the size of SymbolUnion small for performance and
@@ -527,7 +496,6 @@ static inline void assertSymbols() {
   AssertSymbol<CommonSymbol>();
   AssertSymbol<Undefined>();
   AssertSymbol<SharedSymbol>();
-  AssertSymbol<LazyArchive>();
   AssertSymbol<LazyObject>();
 }
 
@@ -539,8 +507,6 @@ size_t Symbol::getSymbolSize() const {
     return sizeof(CommonSymbol);
   case DefinedKind:
     return sizeof(Defined);
-  case LazyArchiveKind:
-    return sizeof(LazyArchive);
   case LazyObjectKind:
     return sizeof(LazyObject);
   case SharedKind:

diff  --git a/lld/test/ELF/archive-as-start-lib.s b/lld/test/ELF/archive-as-start-lib.s
new file mode 100644
index 0000000000000..aa86ce8b7c6be
--- /dev/null
+++ b/lld/test/ELF/archive-as-start-lib.s
@@ -0,0 +1,31 @@
+# REQUIRES: x86
+
+# RUN: rm -rf %t && split-file %s %t && cd %t
+# RUN: llvm-mc -filetype=obj -triple=x86_64 a.s -o a.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64 b.s -o b.o
+
+## Create an archive with incomplete index: foo is missing.
+# RUN: llvm-ar --format=gnu rc a.a a.o
+# RUN: llvm-ar --format=gnu rcS b.a b.o && tail -c +9 b.a > b-tail
+# RUN: cat a.a b-tail > weird.a
+# RUN: llvm-nm --print-armap weird.a | FileCheck %s --check-prefix=ARMAP
+
+# ARMAP:      Archive map
+# ARMAP-NEXT: _start in a.o
+# ARMAP-EMPTY:
+
+## The incomplete archive index is ignored. -u foo extracts weird.a(b.o).
+## In GNU ld, foo is undefined.
+# RUN: ld.lld -m elf_x86_64 -u foo weird.a -o lazy
+# RUN: llvm-nm lazy | FileCheck %s --implicit-check-not={{.}}
+
+# CHECK: [[#%x,]] T _start
+# CHECK: [[#%x,]] T foo
+
+#--- a.s
+.globl _start
+_start:
+
+#--- b.s
+.globl foo
+foo:

diff  --git a/lld/test/ELF/archive-no-index.s b/lld/test/ELF/archive-no-index.s
index 259051773b1b9..23b019e24b648 100644
--- a/lld/test/ELF/archive-no-index.s
+++ b/lld/test/ELF/archive-no-index.s
@@ -9,9 +9,9 @@
 
 # RUN: ld.lld -shared %t.archive.o -o %t.so
 # RUN: llvm-ar crS %t.a %t.so
-# RUN: not ld.lld %t.o %t.a --noinhibit-exec -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERR
+# RUN: ld.lld %t.o %t.a -o /dev/null 2>&1 | FileCheck %s --check-prefix=WARN
 
-# ERR: error: {{.*}}.a: archive member '{{.*}}.so' is neither ET_REL nor LLVM bitcode
+# WARN: warning: {{.*}}.a: archive member '{{.*}}.so' is neither ET_REL nor LLVM bitcode
 
 .globl _start
 _start:

diff  --git a/lld/test/ELF/archive-thin-missing-member.s b/lld/test/ELF/archive-thin-missing-member.s
index 473ebe9c9b6e8..1d770451fcbe8 100644
--- a/lld/test/ELF/archive-thin-missing-member.s
+++ b/lld/test/ELF/archive-thin-missing-member.s
@@ -13,13 +13,11 @@
 
 # Test error when thin archive has symbol table but member is missing.
 # RUN: not ld.lld --entry=_Z1fi -m elf_amd64_fbsd %t-syms.a -o /dev/null 2>&1 | FileCheck -DMSG=%errc_ENOENT %s --check-prefix=ERR2
-# ERR2: {{.*}}-syms.a: could not get the buffer for the member defining symbol f(int): '{{.*}}.o': [[MSG]]
-# RUN: not ld.lld --entry=_Z1fi --no-demangle -m elf_amd64_fbsd %t-syms.a -o /dev/null 2>&1 | FileCheck -DMSG=%errc_ENOENT %s --check-prefix=ERR2MANGLE
-# ERR2MANGLE: {{.*}}-syms.a: could not get the buffer for the member defining symbol _Z1fi: '{{.*}}.o': [[MSG]]
+# RUN: not ld.lld --entry=_Z1fi --no-demangle -m elf_amd64_fbsd %t-syms.a -o /dev/null 2>&1 | FileCheck -DMSG=%errc_ENOENT %s --check-prefix=ERR2
 
 # Test error when thin archive is linked using --whole-archive but member is missing.
-# RUN: not ld.lld --entry=_Z1fi --whole-archive %t-syms.a -o /dev/null 2>&1 | FileCheck -DMSG=%errc_ENOENT %s --check-prefix=ERR3
-# ERR3: {{.*}}-syms.a: could not get the buffer for a child of the archive: '{{.*}}.o': [[MSG]]
+# RUN: not ld.lld --entry=_Z1fi --whole-archive %t-syms.a -o /dev/null 2>&1 | FileCheck -DMSG=%errc_ENOENT %s --check-prefix=ERR2
+# ERR2: {{.*}}-syms.a: could not get the buffer for a child of the archive: '{{.*}}.o': [[MSG]]
 
 .global _Z1fi
 _Z1fi:

diff  --git a/lld/test/ELF/incompatible-ar-first.s b/lld/test/ELF/incompatible-ar-first.s
index a82a55a39ed80..f9688fd3a6caf 100644
--- a/lld/test/ELF/incompatible-ar-first.s
+++ b/lld/test/ELF/incompatible-ar-first.s
@@ -8,8 +8,7 @@
 // We used to crash when
 // * The first object seen by the symbol table is from an archive.
 // * -m was not used.
-// CHECK: .a({{.*}}a.o) is incompatible with {{.*}}b.o
 
-// RUN: not ld.lld --start-lib %ta.o --end-lib %tb.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=CHECK2
+// RUN: not ld.lld --start-lib %ta.o --end-lib %tb.o -o /dev/null 2>&1 | FileCheck %s
 
-// CHECK2: {{.*}}b.o is incompatible{{$}}
+// CHECK: {{.*}}b.o is incompatible{{$}}

diff  --git a/lld/test/ELF/incompatible.s b/lld/test/ELF/incompatible.s
index c274dfd9b786b..39c25106f4d72 100644
--- a/lld/test/ELF/incompatible.s
+++ b/lld/test/ELF/incompatible.s
@@ -58,14 +58,19 @@
 // RUN:   FileCheck --check-prefix=A-AND-FREEBSD-SCRIPT %s
 // A-AND-FREEBSD-SCRIPT: a.o is incompatible with elf32-i386-freebsd
 
+/// %tb.a is not extracted, but we report an error anyway.
+// RUN: rm -f %tb.a && llvm-ar rc %tb.a %tb.o
+// RUN: not ld.lld %ta.o %tb.a -o /dev/null 2>&1 | FileCheck --check-prefix=UNEXTRACTED-ARCHIVE %s
+// UNEXTRACTED-ARCHIVE: {{.*}}.a({{.*}}b.o) is incompatible with {{.*}}a.o
+
 // We used to fail to identify this incompatibility and crash trying to
 // read a 64 bit file as a 32 bit one.
-// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/archive2.s -o %ta.o
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/archive2.s -o %tc.o
 // RUN: rm -f %t.a
-// RUN: llvm-ar rc %t.a %ta.o
-// RUN: llvm-mc -filetype=obj -triple=i686-linux %s -o %tb.o
-// RUN: not ld.lld %t.a %tb.o 2>&1 -o /dev/null | FileCheck --check-prefix=ARCHIVE %s
-// ARCHIVE: .a({{.*}}a.o) is incompatible with {{.*}}b.o
+// RUN: llvm-ar rc %t.a %tc.o
+// RUN: llvm-mc -filetype=obj -triple=i686-linux %s -o %td.o
+// RUN: not ld.lld %t.a %td.o 2>&1 -o /dev/null | FileCheck --check-prefix=ARCHIVE %s
+// ARCHIVE: {{.*}}d.o is incompatible
 .global _start
 _start:
 .data

diff  --git a/lld/test/ELF/lto/comdat-mixed-archive.test b/lld/test/ELF/lto/comdat-mixed-archive.test
index 98cba1ff93c9e..52cd4cf74f3d8 100644
--- a/lld/test/ELF/lto/comdat-mixed-archive.test
+++ b/lld/test/ELF/lto/comdat-mixed-archive.test
@@ -27,8 +27,8 @@ BCSYM:      U bar
 BCSYM-NEXT: W foo
 
 ;; Check that the symbols are handled in the expected order.
-TRACE:      lib.a: lazy definition of foo
-TRACE-NEXT: lib.a: lazy definition of bar
+TRACE:      lib.a(obj.o): lazy definition of foo
+TRACE-NEXT: lib.a(obj.o): lazy definition of bar
 TRACE-NEXT: lib.a(bc.bc): reference to bar
 TRACE-NEXT: lib.a(obj.o): reference to foo
 TRACE-NEXT: lib.a(obj.o): definition of bar

diff  --git a/lld/test/ELF/lto/exclude-libs-libcall.ll b/lld/test/ELF/lto/exclude-libs-libcall.ll
index ece587f1c4dac..3fdffb4276d9e 100644
--- a/lld/test/ELF/lto/exclude-libs-libcall.ll
+++ b/lld/test/ELF/lto/exclude-libs-libcall.ll
@@ -6,7 +6,7 @@
 ; RUN: ld.lld -shared --exclude-libs=b.a %t/a.bc %t/b.a -o %t.so -y __divti3 2>&1 | FileCheck %s --check-prefix=TRACE
 ; RUN: llvm-readelf --dyn-syms %t.so | FileCheck %s
 
-; TRACE:      {{.*}}/b.a: lazy definition of __divti3
+; TRACE:      {{.*}}/b.a(b.o): lazy definition of __divti3
 ; TRACE-NEXT: lto.tmp: reference to __divti3
 ; TRACE-NEXT: {{.*}}/b.a(b.o): definition of __divti3
 

diff  --git a/lld/test/ELF/no-obj.s b/lld/test/ELF/no-obj.s
index 4e8bcee2a1a04..ce89ca96cce6d 100644
--- a/lld/test/ELF/no-obj.s
+++ b/lld/test/ELF/no-obj.s
@@ -2,9 +2,7 @@
 // RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
 // RUN: rm -f %t.a
 // RUN: llvm-ar rcs %t.a %t.o
-// RUN: not ld.lld -o /dev/null -u _start %t.a 2>&1 | FileCheck %s
-
-// CHECK: target emulation unknown: -m or at least one .o file required
+// RUN: ld.lld -o /dev/null -u _start %t.a 2>&1 | count 0
 
 .global _start
 _start:

diff  --git a/lld/test/ELF/trace-symbols.s b/lld/test/ELF/trace-symbols.s
index f5211b367eb47..785414e2bd5b5 100644
--- a/lld/test/ELF/trace-symbols.s
+++ b/lld/test/ELF/trace-symbols.s
@@ -50,7 +50,7 @@
 # RUN:   FileCheck -check-prefix=FOO_AND_COMMON %s
 # FOO_AND_COMMON: trace-symbols.s.tmp: reference to foo
 # FOO_AND_COMMON: trace-symbols.s.tmp2: definition of foo
-# FOO_AND_COMMON: trace-symbols.s.tmp1.a: lazy definition of common
+# FOO_AND_COMMON: trace-symbols.s.tmp1.a({{.*}}.tmp1): lazy definition of common
 
 # RUN: ld.lld -y foo -y common %t %t1.so %t2 -o %t3 | \
 # RUN:   FileCheck -check-prefix=SHLIBDCOMMON %s


        


More information about the llvm-commits mailing list