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 &reg,
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 &reg) 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