diff --git a/lld/MachO/Driver.h b/lld/MachO/Driver.h --- a/lld/MachO/Driver.h +++ b/lld/MachO/Driver.h @@ -68,11 +68,6 @@ // rerooted. llvm::StringRef rerootPath(llvm::StringRef path); -llvm::Optional loadArchiveMember(MemoryBufferRef, uint32_t modTime, - StringRef archiveName, - bool objCOnly, - uint64_t offsetInArchive); - uint32_t getModTime(llvm::StringRef path); void printArchiveMemberLoad(StringRef reason, const InputFile *); diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp --- a/lld/MachO/Driver.cpp +++ b/lld/MachO/Driver.cpp @@ -224,49 +224,6 @@ return CHECK(parseCachePruningPolicy(ltoPolicy), "invalid LTO cache policy"); } -namespace { -struct ArchiveMember { - MemoryBufferRef mbref; - uint32_t modTime; - uint64_t offsetInArchive; -}; -} // namespace - -// Returns slices of MB by parsing MB as an archive file. -// Each slice consists of a member file in the archive. -static std::vector getArchiveMembers(MemoryBufferRef mb) { - std::unique_ptr file = - CHECK(Archive::create(mb), - mb.getBufferIdentifier() + ": failed to parse archive"); - Archive *archive = file.get(); - make>(std::move(file)); // take ownership - - std::vector v; - Error err = Error::success(); - - // Thin archives refer to .o files, so --reproduce needs the .o files too. - bool addToTar = archive->isThin() && tar; - - for (const Archive::Child &c : archive->children(err)) { - MemoryBufferRef mbref = - CHECK(c.getMemoryBufferRef(), - mb.getBufferIdentifier() + - ": could not get the buffer for a child of the archive"); - if (addToTar) - tar->append(relativeToRoot(check(c.getFullName())), mbref.getBuffer()); - uint32_t modTime = toTimeT( - CHECK(c.getLastModified(), mb.getBufferIdentifier() + - ": could not get the modification " - "time for a child of the archive")); - v.push_back({mbref, modTime, c.getChildOffset()}); - } - if (err) - fatal(mb.getBufferIdentifier() + - ": Archive::children failed: " + toString(std::move(err))); - - return v; -} - static DenseMap loadedArchives; static InputFile *addFile(StringRef path, bool forceLoadArchive, @@ -289,48 +246,52 @@ if (ArchiveFile *cachedFile = loadedArchives[path]) return cachedFile; - std::unique_ptr file = CHECK( + std::unique_ptr archive = CHECK( object::Archive::create(mbref), path + ": failed to parse archive"); - if (!file->isEmpty() && !file->hasSymbolTable()) + if (!archive->isEmpty() && !archive->hasSymbolTable()) error(path + ": archive has no index; run ranlib to add one"); + auto file = make(std::move(archive)); if (config->allLoad || forceLoadArchive) { if (Optional buffer = readFile(path)) { - for (const ArchiveMember &member : getArchiveMembers(*buffer)) { - if (Optional file = loadArchiveMember( - member.mbref, member.modTime, path, /*objCOnly=*/false, - member.offsetInArchive)) { - inputFiles.insert(*file); - printArchiveMemberLoad( - (forceLoadArchive ? "-force_load" : "-all_load"), - inputFiles.back()); - } + Error e = Error::success(); + for (const object::Archive::Child &c : file->getArchive().children(e)) { + StringRef reason = forceLoadArchive ? "-force_load" : "-all_load"; + if (Error e = file->fetch(c, reason)) + error(toString(file) + ": " + reason + + " failed to load archive member: " + toString(std::move(e))); } + if (e) + error(toString(file) + + ": Archive::children failed: " + toString(std::move(e))); } } else if (config->forceLoadObjC) { - for (const object::Archive::Symbol &sym : file->symbols()) + for (const object::Archive::Symbol &sym : file->getArchive().symbols()) if (sym.getName().startswith(objc::klass)) symtab->addUndefined(sym.getName(), /*file=*/nullptr, /*isWeakRef=*/false); // TODO: no need to look for ObjC sections for a given archive member if - // we already found that it contains an ObjC symbol. We should also - // consider creating a LazyObjFile class in order to avoid double-loading - // these files here and below (as part of the ArchiveFile). + // we already found that it contains an ObjC symbol. if (Optional buffer = readFile(path)) { - for (const ArchiveMember &member : getArchiveMembers(*buffer)) { - if (Optional file = loadArchiveMember( - member.mbref, member.modTime, path, /*objCOnly=*/true, - member.offsetInArchive)) { - inputFiles.insert(*file); - printArchiveMemberLoad("-ObjC", inputFiles.back()); - } + Error e = Error::success(); + for (const object::Archive::Child &c : file->getArchive().children(e)) { + Expected mb = c.getMemoryBufferRef(); + if (!mb || !hasObjCSection(*mb)) + continue; + if (Error e = file->fetch(c, "-ObjC")) + error(toString(file) + ": -ObjC failed to load archive member: " + + toString(std::move(e))); } + if (e) + error(toString(file) + + ": Archive::children failed: " + toString(std::move(e))); } } - newFile = loadedArchives[path] = make(std::move(file)); + file->addLazySymbols(); + newFile = loadedArchives[path] = file; break; } case file_magic::macho_object: diff --git a/lld/MachO/DriverUtils.cpp b/lld/MachO/DriverUtils.cpp --- a/lld/MachO/DriverUtils.cpp +++ b/lld/MachO/DriverUtils.cpp @@ -18,7 +18,6 @@ #include "lld/Common/Reproduce.h" #include "llvm/ADT/CachedHashString.h" #include "llvm/ADT/DenseMap.h" -#include "llvm/Bitcode/BitcodeReader.h" #include "llvm/LTO/LTO.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" @@ -277,30 +276,6 @@ return path; } -Optional macho::loadArchiveMember(MemoryBufferRef mb, - uint32_t modTime, - StringRef archiveName, - bool objCOnly, - uint64_t offsetInArchive) { - if (config->zeroModTime) - modTime = 0; - - switch (identify_magic(mb.getBuffer())) { - case file_magic::macho_object: - if (!objCOnly || hasObjCSection(mb)) - return make(mb, modTime, archiveName); - return None; - case file_magic::bitcode: - if (!objCOnly || check(isBitcodeContainingObjCCategory(mb))) - return make(mb, archiveName, offsetInArchive); - return None; - default: - error(archiveName + ": archive member " + mb.getBufferIdentifier() + - " has unhandled file type"); - return None; - } -} - uint32_t macho::getModTime(StringRef path) { if (config->zeroModTime) return 0; diff --git a/lld/MachO/InputFiles.h b/lld/MachO/InputFiles.h --- a/lld/MachO/InputFiles.h +++ b/lld/MachO/InputFiles.h @@ -184,8 +184,13 @@ class ArchiveFile final : public InputFile { public: explicit ArchiveFile(std::unique_ptr &&file); + void addLazySymbols(); static bool classof(const InputFile *f) { return f->kind() == ArchiveKind; } - void fetch(const llvm::object::Archive::Symbol &sym); + void fetch(const llvm::object::Archive::Symbol &); + // LLD normally doesn't use Error for error-handling, but the underlying + // Archive library does, so this is the cleanest way to wrap it. + Error fetch(const llvm::object::Archive::Child &, StringRef reason); + llvm::object::Archive &getArchive() const { return *file; }; private: std::unique_ptr file; diff --git a/lld/MachO/InputFiles.cpp b/lld/MachO/InputFiles.cpp --- a/lld/MachO/InputFiles.cpp +++ b/lld/MachO/InputFiles.cpp @@ -1243,47 +1243,74 @@ } ArchiveFile::ArchiveFile(std::unique_ptr &&f) - : InputFile(ArchiveKind, f->getMemoryBufferRef()), file(std::move(f)) { + : InputFile(ArchiveKind, f->getMemoryBufferRef()), file(std::move(f)) {} + +void ArchiveFile::addLazySymbols() { for (const object::Archive::Symbol &sym : file->symbols()) symtab->addLazy(sym.getName(), this, sym); } -void ArchiveFile::fetch(const object::Archive::Symbol &sym) { - object::Archive::Child c = - CHECK(sym.getMember(), toString(this) + - ": could not get the member for symbol " + - toMachOString(sym)); +static Expected loadArchiveMember(MemoryBufferRef mb, + uint32_t modTime, + StringRef archiveName, + uint64_t offsetInArchive) { + if (config->zeroModTime) + modTime = 0; + + switch (identify_magic(mb.getBuffer())) { + case file_magic::macho_object: + return make(mb, modTime, archiveName); + case file_magic::bitcode: + return make(mb, archiveName, offsetInArchive); + default: + return createStringError(inconvertibleErrorCode(), + mb.getBufferIdentifier() + + " has unhandled file type"); + } +} +Error ArchiveFile::fetch(const object::Archive::Child &c, StringRef reason) { if (!seen.insert(c.getChildOffset()).second) - return; + return Error::success(); - MemoryBufferRef mb = - CHECK(c.getMemoryBufferRef(), - toString(this) + - ": could not get the buffer for the member defining symbol " + - toMachOString(sym)); + Expected mb = c.getMemoryBufferRef(); + if (!mb) + return mb.takeError(); if (tar && c.getParent()->isThin()) - tar->append(relativeToRoot(CHECK(c.getFullName(), this)), mb.getBuffer()); + tar->append(relativeToRoot(CHECK(c.getFullName(), this)), mb->getBuffer()); + + Expected> modTime = c.getLastModified(); + if (!modTime) + return modTime.takeError(); - uint32_t modTime = toTimeT( - CHECK(c.getLastModified(), toString(this) + - ": could not get the modification time " - "for the member defining symbol " + - toMachOString(sym))); + Expected file = + loadArchiveMember(*mb, toTimeT(*modTime), getName(), c.getChildOffset()); + + if (!file) + return file.takeError(); + + inputFiles.insert(*file); + printArchiveMemberLoad(reason, *file); + return Error::success(); +} + +void ArchiveFile::fetch(const object::Archive::Symbol &sym) { + object::Archive::Child c = + CHECK(sym.getMember(), toString(this) + + ": could not get the member defining symbol " + + toMachOString(sym)); // `sym` is owned by a LazySym, which will be replace<>()d by make // and become invalid after that call. Copy it to the stack so we can refer // to it later. const object::Archive::Symbol symCopy = sym; - if (Optional file = loadArchiveMember( - mb, modTime, getName(), /*objCOnly=*/false, c.getChildOffset())) { - inputFiles.insert(*file); - // ld64 doesn't demangle sym here even with -demangle. - // Match that: intentionally don't call toMachOString(). - printArchiveMemberLoad(symCopy.getName(), *file); - } + // ld64 doesn't demangle sym here even with -demangle. + // Match that: intentionally don't call toMachOString(). + if (Error e = fetch(c, symCopy.getName())) + error(toString(this) + ": could not get the member defining symbol " + + toMachOString(symCopy) + ": " + toString(std::move(e))); } static macho::Symbol *createBitcodeSymbol(const lto::InputFile::Symbol &objSym, diff --git a/lld/MachO/ObjC.cpp b/lld/MachO/ObjC.cpp --- a/lld/MachO/ObjC.cpp +++ b/lld/MachO/ObjC.cpp @@ -13,13 +13,14 @@ #include "Target.h" #include "llvm/BinaryFormat/MachO.h" +#include "llvm/Bitcode/BitcodeReader.h" using namespace llvm; using namespace llvm::MachO; using namespace lld; using namespace lld::macho; -template static bool hasObjCSection(MemoryBufferRef mb) { +template static bool objectHasObjCSection(MemoryBufferRef mb) { using Section = typename LP::section; auto *hdr = @@ -46,9 +47,20 @@ return false; } -bool macho::hasObjCSection(MemoryBufferRef mb) { +static bool objectHasObjCSection(MemoryBufferRef mb) { if (target->wordSize == 8) - return ::hasObjCSection(mb); + return ::objectHasObjCSection(mb); else - return ::hasObjCSection(mb); + return ::objectHasObjCSection(mb); +} + +bool macho::hasObjCSection(MemoryBufferRef mb) { + switch (identify_magic(mb.getBuffer())) { + case file_magic::macho_object: + return objectHasObjCSection(mb); + case file_magic::bitcode: + return check(isBitcodeContainingObjCCategory(mb)); + default: + return false; + } } diff --git a/lld/test/MachO/invalid/bad-archive-member.s b/lld/test/MachO/invalid/bad-archive-member.s --- a/lld/test/MachO/invalid/bad-archive-member.s +++ b/lld/test/MachO/invalid/bad-archive-member.s @@ -4,10 +4,14 @@ # RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/test.s -o %t/test.o # RUN: %lld -dylib -lSystem %t/foo.o -o %t/foo.dylib # RUN: llvm-ar rcs %t/foo.a %t/foo.dylib -# RUN: not %lld %t/test.o %t/foo.a -o /dev/null 2>&1 | FileCheck %s -DFILE=%t/foo.a -# RUN: not %lld %t/test.o -force_load %t/foo.a -o /dev/null 2>&1 | FileCheck %s -DFILE=%t/foo.a -# RUN: not %lld %t/test.o -ObjC %t/foo.a -o /dev/null 2>&1 | FileCheck %s -DFILE=%t/foo.a -# CHECK: error: [[FILE]]: archive member foo.dylib has unhandled file type +# RUN: not %lld %t/test.o %t/foo.a -o /dev/null 2>&1 | FileCheck %s \ +# RUN: --check-prefix=SYM -DFILE=%t/foo.a +# RUN: not %lld %t/test.o -ObjC %t/foo.a -o /dev/null 2>&1 | FileCheck %s \ +# RUN: --check-prefix=SYM -DFILE=%t/foo.a +# RUN: not %lld %t/test.o -force_load %t/foo.a -o /dev/null 2>&1 | FileCheck %s \ +# RUN: --check-prefix=FORCE-LOAD -DFILE=%t/foo.a +# SYM: error: [[FILE]]: could not get the member defining symbol _foo: foo.dylib has unhandled file type +# FORCE-LOAD: error: [[FILE]]: -force_load failed to load archive member: foo.dylib has unhandled file type #--- foo.s .globl _foo diff --git a/lld/test/MachO/thin-archive.s b/lld/test/MachO/thin-archive.s --- a/lld/test/MachO/thin-archive.s +++ b/lld/test/MachO/thin-archive.s @@ -25,8 +25,8 @@ # CHECK: __Z1fv # CHECK: _main -# NOOBJ: error: {{.*}}lib_thin.a: could not get the buffer for the member defining symbol f(): '{{.*}}lib.o': -# NOOBJNODEMANGLE: error: {{.*}}lib_thin.a: could not get the buffer for the member defining symbol __Z1fv: '{{.*}}lib.o': +# NOOBJ: error: {{.*}}lib_thin.a: could not get the member defining symbol f(): '{{.*}}lib.o': No such file or directory +# NOOBJNODEMANGLE: error: {{.*}}lib_thin.a: could not get the member defining symbol __Z1fv: '{{.*}}lib.o': No such file or directory #--- mangled-symbol.s .globl __Z1fv