Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -79,6 +79,7 @@ Driver->main(Args, CanExitEarly); freeArena(); + emitFinalNotes(); return !ErrorCount; } Index: ELF/Error.h =================================================================== --- ELF/Error.h +++ ELF/Error.h @@ -45,6 +45,11 @@ void error(const Twine &Msg); LLVM_ATTRIBUTE_NORETURN void fatal(const Twine &Msg); +/// Prints notes that have been scheduled to be printed just before the linker +/// exits (see schedule*). +void emitFinalNotes(); +void scheduleEmptyArchiveNote(); + LLVM_ATTRIBUTE_NORETURN void exitLld(int Val); // check() functions are convenient functions to strip errors Index: ELF/Error.cpp =================================================================== --- ELF/Error.cpp +++ ELF/Error.cpp @@ -25,6 +25,7 @@ namespace lld { +static bool EmptyArchiveNoteScheduled = false; uint64_t elf::ErrorCount; raw_ostream *elf::ErrorOS; StringRef elf::Argv0; @@ -68,23 +69,37 @@ } void elf::error(const Twine &Msg) { - std::lock_guard Lock(Mu); + bool shouldExit = false; + { + std::lock_guard Lock(Mu); - if (Config->ErrorLimit == 0 || ErrorCount < Config->ErrorLimit) { - print("error: ", raw_ostream::RED); - *ErrorOS << Msg << "\n"; - } else if (ErrorCount == Config->ErrorLimit) { - print("error: ", raw_ostream::RED); - *ErrorOS << "too many errors emitted, stopping now" - << " (use -error-limit=0 to see all errors)\n"; - if (Config->ExitEarly) - exitLld(1); + if (Config->ErrorLimit == 0 || ErrorCount < Config->ErrorLimit) { + print("error: ", raw_ostream::RED); + *ErrorOS << Msg << "\n"; + } else if (ErrorCount == Config->ErrorLimit) { + shouldExit = true; + print("error: ", raw_ostream::RED); + *ErrorOS << "too many errors emitted, stopping now" + << " (use -error-limit=0 to see all errors)\n"; + } + ++ErrorCount; } + if (shouldExit) exitLld(1); +} - ++ErrorCount; +void elf::emitFinalNotes() { + std::lock_guard Lock(Mu); + if (EmptyArchiveNoteScheduled) + *ErrorOS << "\nNOTE: At least one archive listed no symbols in its index." + << " This can happen when creating archives with a version" + << " of ar that does not understand the object files in" + << " the archive. For example, if you are using LLVM" + << " bitcode objects (such as created by -flto), you may " + << " need to use llvm-ar or GNU ar with a plugin.\n"; } void elf::exitLld(int Val) { + emitFinalNotes(); // Dealloc/destroy ManagedStatic variables before calling // _exit(). In a non-LTO build, this is a nop. In an LTO // build allows us to get the output of -time-passes. @@ -96,10 +111,14 @@ } void elf::fatal(const Twine &Msg) { - std::lock_guard Lock(Mu); - print("error: ", raw_ostream::RED); - *ErrorOS << Msg << "\n"; + { + std::lock_guard Lock(Mu); + print("error: ", raw_ostream::RED); + *ErrorOS << Msg << "\n"; + } exitLld(1); } +void elf::scheduleEmptyArchiveNote() { EmptyArchiveNoteScheduled = true; } + } // namespace lld Index: ELF/InputFiles.h =================================================================== --- ELF/InputFiles.h +++ ELF/InputFiles.h @@ -240,6 +240,7 @@ public: explicit ArchiveFile(MemoryBufferRef M) : InputFile(ArchiveKind, M) {} static bool classof(const InputFile *F) { return F->kind() == ArchiveKind; } + static bool archiveWithoutSymbolsSeen() { return ArchiveWithoutSymbolsSeen; } template void parse(); // Returns a memory buffer for a given symbol and the offset in the archive @@ -249,6 +250,7 @@ std::pair getMember(const Archive::Symbol *Sym); private: + static bool ArchiveWithoutSymbolsSeen; std::unique_ptr File; llvm::DenseSet Seen; }; Index: ELF/InputFiles.cpp =================================================================== --- ELF/InputFiles.cpp +++ ELF/InputFiles.cpp @@ -55,6 +55,8 @@ }; } +bool ArchiveFile::ArchiveWithoutSymbolsSeen = false; + Optional elf::readFile(StringRef Path) { log(Path); auto MBOrErr = MemoryBuffer::getFile(Path); @@ -556,8 +558,13 @@ MB.getBufferIdentifier() + ": failed to parse archive"); // Read the symbol table to construct Lazy objects. - for (const Archive::Symbol &Sym : File->symbols()) + bool NoSymbols = true; + for (const Archive::Symbol &Sym : File->symbols()) { Symtab::X->addLazyArchive(this, Sym); + NoSymbols = false; + } + + if (NoSymbols) ArchiveWithoutSymbolsSeen = true; } // Returns a buffer pointing to a member file containing a given symbol. Index: ELF/Relocations.cpp =================================================================== --- ELF/Relocations.cpp +++ ELF/Relocations.cpp @@ -611,10 +611,12 @@ toString(Sym) + "'"; if (Config->UnresolvedSymbols == UnresolvedPolicy::WarnAll || - (Config->UnresolvedSymbols == UnresolvedPolicy::Warn && CanBeExternal)) + (Config->UnresolvedSymbols == UnresolvedPolicy::Warn && CanBeExternal)) { warn(Msg); - else + } else { error(Msg); + if (ArchiveFile::archiveWithoutSymbolsSeen()) scheduleEmptyArchiveNote(); + } } template Index: test/ELF/lto/archive-no-index.ll =================================================================== --- /dev/null +++ test/ELF/lto/archive-no-index.ll @@ -0,0 +1,39 @@ +; REQUIRES: x86 +; Tests that we suggest that LTO symbols missing from an archive index +; may be the cause of undefined references, but only if we both +; encountered an empty archive index and undefined references (to prevent +; noisy false alarms). + +; RUN: rm -fr %T/archive-no-index +; RUN: mkdir %T/archive-no-index +; RUN: llvm-as %S/Inputs/archive.ll -o %T/archive-no-index/f.o +; RUN: llvm-ar cr %T/archive-no-index/libf.a +; RUN: llvm-ar qS %T/archive-no-index/libf.a %T/archive-no-index/f.o +; RUN: llvm-as %s -o %t.o +; RUN: not ld.lld -emain -m elf_x86_64 %t.o -o %t %T/archive-no-index/libf.a \ +; RUN: 2>&1 | FileCheck --check-prefix=NOTE %s + +; RUN: llvm-ar crs %T/archive-no-index/libfs.a %T/archive-no-index/f.o +; RUN: ld.lld -emain -m elf_x86_64 %t.o -o %t %T/archive-no-index/libf.a \ +; RUN: %T/archive-no-index/libfs.a + +; RUN: llvm-as %S/Inputs/archive-3.ll -o %T/archive-no-index/foo.o +; RUN: llvm-ar crs %T/archive-no-index/libfoo.a %T/archive-no-index/foo.o +; RUN: not ld.lld -emain -m elf_x86_64 %t.o -o %t %T/archive-no-index/libfoo.a \ +; RUN: 2>&1 | FileCheck --check-prefix=NO-NOTE %s + +; NOTE: undefined symbol 'f' +; NOTE: archive listed no symbols + +; NO-NOTE: undefined symbol 'f' +; NO-NOTE-NOT: archive listed no symbols + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +declare void @f() + +define i32 @main() { + call void @f() + ret i32 0 +}