1 //===- lib/ReaderWriter/FileArchive.cpp -----------------------------------===//
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 #include "lld/Common/LLVM.h"
11 #include "lld/Core/ArchiveLibraryFile.h"
12 #include "lld/Core/File.h"
13 #include "lld/Core/Reader.h"
14 #include "llvm/ADT/STLExtras.h"
15 #include "llvm/ADT/StringRef.h"
16 #include "llvm/BinaryFormat/Magic.h"
17 #include "llvm/Object/Archive.h"
18 #include "llvm/Object/Error.h"
19 #include "llvm/Support/Debug.h"
20 #include "llvm/Support/ErrorOr.h"
21 #include "llvm/Support/FileSystem.h"
22 #include "llvm/Support/Format.h"
23 #include "llvm/Support/MemoryBuffer.h"
24 #include "llvm/Support/raw_ostream.h"
28 #include <system_error>
29 #include <unordered_map>
33 using llvm::object::Archive;
34 using llvm::file_magic;
35 using llvm::identify_magic;
41 /// \brief The FileArchive class represents an Archive Library file
42 class FileArchive : public lld::ArchiveLibraryFile {
44 FileArchive(std::unique_ptr<MemoryBuffer> mb, const Registry ®,
45 StringRef path, bool logLoading)
46 : ArchiveLibraryFile(path), _mb(std::shared_ptr<MemoryBuffer>(mb.release())),
47 _registry(reg), _logLoading(logLoading) {}
49 /// \brief Check if any member of the archive contains an Atom with the
50 /// specified name and return the File object for that member, or nullptr.
51 File *find(StringRef name) override {
52 auto member = _symbolMemberMap.find(name);
53 if (member == _symbolMemberMap.end())
55 Archive::Child c = member->second;
57 // Don't return a member already returned
58 Expected<StringRef> buf = c.getBuffer();
60 // TODO: Actually report errors helpfully.
61 consumeError(buf.takeError());
64 const char *memberStart = buf->data();
65 if (_membersInstantiated.count(memberStart))
67 _membersInstantiated.insert(memberStart);
69 std::unique_ptr<File> result;
70 if (instantiateMember(c, result))
73 File *file = result.get();
74 _filesReturned.push_back(std::move(result));
76 // Give up the file pointer. It was stored and will be destroyed with destruction of FileArchive
80 /// \brief parse each member
82 parseAllMembers(std::vector<std::unique_ptr<File>> &result) override {
83 if (std::error_code ec = parse())
85 llvm::Error err = llvm::Error::success();
86 for (auto mf = _archive->child_begin(err), me = _archive->child_end();
88 std::unique_ptr<File> file;
89 if (std::error_code ec = instantiateMember(*mf, file)) {
90 // err is Success (or we wouldn't be in the loop body) but we can't
91 // return without testing or consuming it.
92 consumeError(std::move(err));
95 result.push_back(std::move(file));
98 return errorToErrorCode(std::move(err));
99 return std::error_code();
102 const AtomRange<DefinedAtom> defined() const override {
103 return _noDefinedAtoms;
106 const AtomRange<UndefinedAtom> undefined() const override {
107 return _noUndefinedAtoms;
110 const AtomRange<SharedLibraryAtom> sharedLibrary() const override {
111 return _noSharedLibraryAtoms;
114 const AtomRange<AbsoluteAtom> absolute() const override {
115 return _noAbsoluteAtoms;
118 void clearAtoms() override {
119 _noDefinedAtoms.clear();
120 _noUndefinedAtoms.clear();
121 _noSharedLibraryAtoms.clear();
122 _noAbsoluteAtoms.clear();
126 std::error_code doParse() override {
127 // Make Archive object which will be owned by FileArchive object.
128 llvm::Error Err = llvm::Error::success();
129 _archive.reset(new Archive(_mb->getMemBufferRef(), Err));
131 return errorToErrorCode(std::move(Err));
133 if ((ec = buildTableOfContents()))
135 return std::error_code();
139 std::error_code instantiateMember(Archive::Child member,
140 std::unique_ptr<File> &result) const {
141 Expected<llvm::MemoryBufferRef> mbOrErr = member.getMemoryBufferRef();
143 return errorToErrorCode(mbOrErr.takeError());
144 llvm::MemoryBufferRef mb = mbOrErr.get();
145 std::string memberPath = (_archive->getFileName() + "("
146 + mb.getBufferIdentifier() + ")").str();
149 llvm::errs() << memberPath << "\n";
151 std::unique_ptr<MemoryBuffer> memberMB(MemoryBuffer::getMemBuffer(
152 mb.getBuffer(), mb.getBufferIdentifier(), false));
154 ErrorOr<std::unique_ptr<File>> fileOrErr =
155 _registry.loadFile(std::move(memberMB));
156 if (std::error_code ec = fileOrErr.getError())
158 result = std::move(fileOrErr.get());
159 if (std::error_code ec = result->parse())
161 result->setArchivePath(_archive->getFileName());
163 // The memory buffer is co-owned by the archive file and the children,
164 // so that the bufffer is deallocated when all the members are destructed.
165 result->setSharedMemoryBuffer(_mb);
166 return std::error_code();
169 std::error_code buildTableOfContents() {
170 DEBUG_WITH_TYPE("FileArchive", llvm::dbgs()
171 << "Table of contents for archive '"
172 << _archive->getFileName() << "':\n");
173 for (const Archive::Symbol &sym : _archive->symbols()) {
174 StringRef name = sym.getName();
175 Expected<Archive::Child> memberOrErr = sym.getMember();
177 return errorToErrorCode(memberOrErr.takeError());
178 Archive::Child member = memberOrErr.get();
179 DEBUG_WITH_TYPE("FileArchive",
181 << llvm::format("0x%08llX ",
182 member.getBuffer()->data())
183 << "'" << name << "'\n");
184 _symbolMemberMap.insert(std::make_pair(name, member));
186 return std::error_code();
189 typedef std::unordered_map<StringRef, Archive::Child> MemberMap;
190 typedef std::set<const char *> InstantiatedSet;
192 std::shared_ptr<MemoryBuffer> _mb;
193 const Registry &_registry;
194 std::unique_ptr<Archive> _archive;
195 MemberMap _symbolMemberMap;
196 InstantiatedSet _membersInstantiated;
198 std::vector<std::unique_ptr<MemoryBuffer>> _memberBuffers;
199 std::vector<std::unique_ptr<File>> _filesReturned;
202 class ArchiveReader : public Reader {
204 ArchiveReader(bool logLoading) : _logLoading(logLoading) {}
206 bool canParse(file_magic magic, MemoryBufferRef) const override {
207 return magic == file_magic::archive;
210 ErrorOr<std::unique_ptr<File>> loadFile(std::unique_ptr<MemoryBuffer> mb,
211 const Registry ®) const override {
212 StringRef path = mb->getBufferIdentifier();
213 std::unique_ptr<File> ret =
214 llvm::make_unique<FileArchive>(std::move(mb), reg, path, _logLoading);
215 return std::move(ret);
222 } // anonymous namespace
224 void Registry::addSupportArchives(bool logLoading) {
225 add(std::unique_ptr<Reader>(new ArchiveReader(logLoading)));