//===- lib/ReaderWriter/FileArchive.cpp -----------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "lld/Common/LLVM.h" #include "lld/Core/ArchiveLibraryFile.h" #include "lld/Core/File.h" #include "lld/Core/Reader.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/Magic.h" #include "llvm/Object/Archive.h" #include "llvm/Object/Error.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Format.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_ostream.h" #include #include #include #include #include #include #include using llvm::object::Archive; using llvm::file_magic; using llvm::identify_magic; namespace lld { namespace { /// The FileArchive class represents an Archive Library file class FileArchive : public lld::ArchiveLibraryFile { public: FileArchive(std::unique_ptr mb, const Registry ®, StringRef path, bool logLoading) : ArchiveLibraryFile(path), _mb(std::shared_ptr(mb.release())), _registry(reg), _logLoading(logLoading) {} /// Check if any member of the archive contains an Atom with the /// specified name and return the File object for that member, or nullptr. File *find(StringRef name) override { auto member = _symbolMemberMap.find(name); if (member == _symbolMemberMap.end()) return nullptr; Archive::Child c = member->second; // Don't return a member already returned Expected buf = c.getBuffer(); if (!buf) { // TODO: Actually report errors helpfully. consumeError(buf.takeError()); return nullptr; } const char *memberStart = buf->data(); if (_membersInstantiated.count(memberStart)) return nullptr; _membersInstantiated.insert(memberStart); std::unique_ptr result; if (instantiateMember(c, result)) return nullptr; File *file = result.get(); _filesReturned.push_back(std::move(result)); // Give up the file pointer. It was stored and will be destroyed with destruction of FileArchive return file; } /// parse each member std::error_code parseAllMembers(std::vector> &result) override { if (std::error_code ec = parse()) return ec; llvm::Error err = llvm::Error::success(); for (auto mf = _archive->child_begin(err), me = _archive->child_end(); mf != me; ++mf) { std::unique_ptr file; if (std::error_code ec = instantiateMember(*mf, file)) { // err is Success (or we wouldn't be in the loop body) but we can't // return without testing or consuming it. consumeError(std::move(err)); return ec; } result.push_back(std::move(file)); } if (err) return errorToErrorCode(std::move(err)); return std::error_code(); } const AtomRange defined() const override { return _noDefinedAtoms; } const AtomRange undefined() const override { return _noUndefinedAtoms; } const AtomRange sharedLibrary() const override { return _noSharedLibraryAtoms; } const AtomRange absolute() const override { return _noAbsoluteAtoms; } void clearAtoms() override { _noDefinedAtoms.clear(); _noUndefinedAtoms.clear(); _noSharedLibraryAtoms.clear(); _noAbsoluteAtoms.clear(); } protected: std::error_code doParse() override { // Make Archive object which will be owned by FileArchive object. llvm::Error Err = llvm::Error::success(); _archive.reset(new Archive(_mb->getMemBufferRef(), Err)); if (Err) return errorToErrorCode(std::move(Err)); std::error_code ec; if ((ec = buildTableOfContents())) return ec; return std::error_code(); } private: std::error_code instantiateMember(Archive::Child member, std::unique_ptr &result) const { Expected mbOrErr = member.getMemoryBufferRef(); if (!mbOrErr) return errorToErrorCode(mbOrErr.takeError()); llvm::MemoryBufferRef mb = mbOrErr.get(); std::string memberPath = (_archive->getFileName() + "(" + mb.getBufferIdentifier() + ")").str(); if (_logLoading) llvm::errs() << memberPath << "\n"; std::unique_ptr memberMB(MemoryBuffer::getMemBuffer( mb.getBuffer(), mb.getBufferIdentifier(), false)); ErrorOr> fileOrErr = _registry.loadFile(std::move(memberMB)); if (std::error_code ec = fileOrErr.getError()) return ec; result = std::move(fileOrErr.get()); if (std::error_code ec = result->parse()) return ec; result->setArchivePath(_archive->getFileName()); // The memory buffer is co-owned by the archive file and the children, // so that the bufffer is deallocated when all the members are destructed. result->setSharedMemoryBuffer(_mb); return std::error_code(); } std::error_code buildTableOfContents() { DEBUG_WITH_TYPE("FileArchive", llvm::dbgs() << "Table of contents for archive '" << _archive->getFileName() << "':\n"); for (const Archive::Symbol &sym : _archive->symbols()) { StringRef name = sym.getName(); Expected memberOrErr = sym.getMember(); if (!memberOrErr) return errorToErrorCode(memberOrErr.takeError()); Archive::Child member = memberOrErr.get(); DEBUG_WITH_TYPE("FileArchive", llvm::dbgs() << llvm::format("0x%08llX ", member.getBuffer()->data()) << "'" << name << "'\n"); _symbolMemberMap.insert(std::make_pair(name, member)); } return std::error_code(); } typedef std::unordered_map MemberMap; typedef std::set InstantiatedSet; std::shared_ptr _mb; const Registry &_registry; std::unique_ptr _archive; MemberMap _symbolMemberMap; InstantiatedSet _membersInstantiated; bool _logLoading; std::vector> _memberBuffers; std::vector> _filesReturned; }; class ArchiveReader : public Reader { public: ArchiveReader(bool logLoading) : _logLoading(logLoading) {} bool canParse(file_magic magic, MemoryBufferRef) const override { return magic == file_magic::archive; } ErrorOr> loadFile(std::unique_ptr mb, const Registry ®) const override { StringRef path = mb->getBufferIdentifier(); std::unique_ptr ret = std::make_unique(std::move(mb), reg, path, _logLoading); return std::move(ret); } private: bool _logLoading; }; } // anonymous namespace void Registry::addSupportArchives(bool logLoading) { add(std::unique_ptr(new ArchiveReader(logLoading))); } } // namespace lld