1//===- lib/ReaderWriter/FileArchive.cpp -----------------------------------===// 2// 3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4// See https://llvm.org/LICENSE.txt for license information. 5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6// 7//===----------------------------------------------------------------------===// 8 9#include "lld/Common/LLVM.h" 10#include "lld/Core/ArchiveLibraryFile.h" 11#include "lld/Core/File.h" 12#include "lld/Core/Reader.h" 13#include "llvm/ADT/STLExtras.h" 14#include "llvm/ADT/StringRef.h" 15#include "llvm/BinaryFormat/Magic.h" 16#include "llvm/Object/Archive.h" 17#include "llvm/Object/Error.h" 18#include "llvm/Support/Debug.h" 19#include "llvm/Support/ErrorOr.h" 20#include "llvm/Support/FileSystem.h" 21#include "llvm/Support/Format.h" 22#include "llvm/Support/MemoryBuffer.h" 23#include "llvm/Support/raw_ostream.h" 24#include <memory> 25#include <set> 26#include <string> 27#include <system_error> 28#include <unordered_map> 29#include <utility> 30#include <vector> 31 32using llvm::object::Archive; 33using llvm::file_magic; 34using llvm::identify_magic; 35 36namespace lld { 37 38namespace { 39 40/// The FileArchive class represents an Archive Library file 41class FileArchive : public lld::ArchiveLibraryFile { 42public: 43 FileArchive(std::unique_ptr<MemoryBuffer> mb, const Registry ®, 44 StringRef path, bool logLoading) 45 : ArchiveLibraryFile(path), _mb(std::shared_ptr<MemoryBuffer>(mb.release())), 46 _registry(reg), _logLoading(logLoading) {} 47 48 /// Check if any member of the archive contains an Atom with the 49 /// specified name and return the File object for that member, or nullptr. 50 File *find(StringRef name) override { 51 auto member = _symbolMemberMap.find(name); 52 if (member == _symbolMemberMap.end()) 53 return nullptr; 54 Archive::Child c = member->second; 55 56 // Don't return a member already returned 57 Expected<StringRef> buf = c.getBuffer(); 58 if (!buf) { 59 // TODO: Actually report errors helpfully. 60 consumeError(buf.takeError()); 61 return nullptr; 62 } 63 const char *memberStart = buf->data(); 64 if (_membersInstantiated.count(memberStart)) 65 return nullptr; 66 _membersInstantiated.insert(memberStart); 67 68 std::unique_ptr<File> result; 69 if (instantiateMember(c, result)) 70 return nullptr; 71 72 File *file = result.get(); 73 _filesReturned.push_back(std::move(result)); 74 75 // Give up the file pointer. It was stored and will be destroyed with destruction of FileArchive 76 return file; 77 } 78 79 /// parse each member 80 std::error_code 81 parseAllMembers(std::vector<std::unique_ptr<File>> &result) override { 82 if (std::error_code ec = parse()) 83 return ec; 84 llvm::Error err = llvm::Error::success(); 85 for (auto mf = _archive->child_begin(err), me = _archive->child_end(); 86 mf != me; ++mf) { 87 std::unique_ptr<File> file; 88 if (std::error_code ec = instantiateMember(*mf, file)) { 89 // err is Success (or we wouldn't be in the loop body) but we can't 90 // return without testing or consuming it. 91 consumeError(std::move(err)); 92 return ec; 93 } 94 result.push_back(std::move(file)); 95 } 96 if (err) 97 return errorToErrorCode(std::move(err)); 98 return std::error_code(); 99 } 100 101 const AtomRange<DefinedAtom> defined() const override { 102 return _noDefinedAtoms; 103 } 104 105 const AtomRange<UndefinedAtom> undefined() const override { 106 return _noUndefinedAtoms; 107 } 108 109 const AtomRange<SharedLibraryAtom> sharedLibrary() const override { 110 return _noSharedLibraryAtoms; 111 } 112 113 const AtomRange<AbsoluteAtom> absolute() const override { 114 return _noAbsoluteAtoms; 115 } 116 117 void clearAtoms() override { 118 _noDefinedAtoms.clear(); 119 _noUndefinedAtoms.clear(); 120 _noSharedLibraryAtoms.clear(); 121 _noAbsoluteAtoms.clear(); 122 } 123 124protected: 125 std::error_code doParse() override { 126 // Make Archive object which will be owned by FileArchive object. 127 llvm::Error Err = llvm::Error::success(); 128 _archive.reset(new Archive(_mb->getMemBufferRef(), Err)); 129 if (Err) 130 return errorToErrorCode(std::move(Err)); 131 std::error_code ec; 132 if ((ec = buildTableOfContents())) 133 return ec; 134 return std::error_code(); 135 } 136 137private: 138 std::error_code instantiateMember(Archive::Child member, 139 std::unique_ptr<File> &result) const { 140 Expected<llvm::MemoryBufferRef> mbOrErr = member.getMemoryBufferRef(); 141 if (!mbOrErr) 142 return errorToErrorCode(mbOrErr.takeError()); 143 llvm::MemoryBufferRef mb = mbOrErr.get(); 144 std::string memberPath = (_archive->getFileName() + "(" 145 + mb.getBufferIdentifier() + ")").str(); 146 147 if (_logLoading) 148 llvm::errs() << memberPath << "\n"; 149 150 std::unique_ptr<MemoryBuffer> memberMB(MemoryBuffer::getMemBuffer( 151 mb.getBuffer(), mb.getBufferIdentifier(), false)); 152 153 ErrorOr<std::unique_ptr<File>> fileOrErr = 154 _registry.loadFile(std::move(memberMB)); 155 if (std::error_code ec = fileOrErr.getError()) 156 return ec; 157 result = std::move(fileOrErr.get()); 158 if (std::error_code ec = result->parse()) 159 return ec; 160 result->setArchivePath(_archive->getFileName()); 161 162 // The memory buffer is co-owned by the archive file and the children, 163 // so that the bufffer is deallocated when all the members are destructed. 164 result->setSharedMemoryBuffer(_mb); 165 return std::error_code(); 166 } 167 168 std::error_code buildTableOfContents() { 169 DEBUG_WITH_TYPE("FileArchive", llvm::dbgs() 170 << "Table of contents for archive '" 171 << _archive->getFileName() << "':\n"); 172 for (const Archive::Symbol &sym : _archive->symbols()) { 173 StringRef name = sym.getName(); 174 Expected<Archive::Child> memberOrErr = sym.getMember(); 175 if (!memberOrErr) 176 return errorToErrorCode(memberOrErr.takeError()); 177 Archive::Child member = memberOrErr.get(); 178 DEBUG_WITH_TYPE("FileArchive", 179 llvm::dbgs() 180 << llvm::format("0x%08llX ", 181 member.getBuffer()->data()) 182 << "'" << name << "'\n"); 183 _symbolMemberMap.insert(std::make_pair(name, member)); 184 } 185 return std::error_code(); 186 } 187 188 typedef std::unordered_map<StringRef, Archive::Child> MemberMap; 189 typedef std::set<const char *> InstantiatedSet; 190 191 std::shared_ptr<MemoryBuffer> _mb; 192 const Registry &_registry; 193 std::unique_ptr<Archive> _archive; 194 MemberMap _symbolMemberMap; 195 InstantiatedSet _membersInstantiated; 196 bool _logLoading; 197 std::vector<std::unique_ptr<MemoryBuffer>> _memberBuffers; 198 std::vector<std::unique_ptr<File>> _filesReturned; 199}; 200 201class ArchiveReader : public Reader { 202public: 203 ArchiveReader(bool logLoading) : _logLoading(logLoading) {} 204 205 bool canParse(file_magic magic, MemoryBufferRef) const override { 206 return magic == file_magic::archive; 207 } 208 209 ErrorOr<std::unique_ptr<File>> loadFile(std::unique_ptr<MemoryBuffer> mb, 210 const Registry ®) const override { 211 StringRef path = mb->getBufferIdentifier(); 212 std::unique_ptr<File> ret = 213 std::make_unique<FileArchive>(std::move(mb), reg, path, _logLoading); 214 return std::move(ret); 215 } 216 217private: 218 bool _logLoading; 219}; 220 221} // anonymous namespace 222 223void Registry::addSupportArchives(bool logLoading) { 224 add(std::unique_ptr<Reader>(new ArchiveReader(logLoading))); 225} 226 227} // namespace lld 228