1//===-- BinaryHolder.h - Utility class for accessing binaries -------------===//
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// This program is a utility that aims to be a dropin replacement for
10// Darwin's dsymutil.
11//
12//===----------------------------------------------------------------------===//
13#ifndef LLVM_TOOLS_DSYMUTIL_BINARYHOLDER_H
14#define LLVM_TOOLS_DSYMUTIL_BINARYHOLDER_H
15
16#include "llvm/ADT/DenseMap.h"
17#include "llvm/ADT/StringMap.h"
18#include "llvm/Object/Archive.h"
19#include "llvm/Object/Error.h"
20#include "llvm/Object/MachOUniversal.h"
21#include "llvm/Object/ObjectFile.h"
22#include "llvm/Support/Chrono.h"
23#include "llvm/Support/Errc.h"
24#include "llvm/Support/ErrorOr.h"
25#include "llvm/Support/VirtualFileSystem.h"
26#include "llvm/TargetParser/Triple.h"
27
28#include <mutex>
29
30namespace llvm {
31namespace dsymutil {
32
33/// The BinaryHolder class is responsible for creating and owning
34/// ObjectFiles and their underlying MemoryBuffers. It differs from a simple
35/// OwningBinary in that it handles accessing and caching of archives and its
36/// members.
37class BinaryHolder {
38public:
39 using TimestampTy = sys::TimePoint<std::chrono::seconds>;
40
41 struct Options {
42 Options(bool Verbose = false, bool Warn = true)
43 : Verbose(Verbose), Warn(Warn) {}
44 bool Verbose;
45 bool Warn;
46 };
47
48 BinaryHolder(IntrusiveRefCntPtr<vfs::FileSystem> VFS,
49 BinaryHolder::Options Opts = {});
50
51 // Forward declarations for friend declaration.
52 class ObjectEntry;
53 class ArchiveEntry;
54
55 /// Base class shared by cached entries, representing objects and archives.
56 class EntryBase {
57 protected:
58 std::unique_ptr<MemoryBuffer> MemBuffer;
59 std::unique_ptr<object::MachOUniversalBinary> FatBinary;
60 std::string FatBinaryName;
61 };
62
63 /// Cached entry holding one or more (in case of a fat binary) object files.
64 class ObjectEntry : public EntryBase {
65 public:
66 /// Load the given object binary in memory.
67 Error load(IntrusiveRefCntPtr<vfs::FileSystem> VFS, StringRef Filename,
68 TimestampTy Timestamp, BinaryHolder::Options = {});
69
70 /// Access all owned ObjectFiles.
71 std::vector<const object::ObjectFile *> getObjects() const;
72
73 /// Access to a derived version of all the currently owned ObjectFiles. The
74 /// conversion might be invalid, in which case an Error is returned.
75 template <typename ObjectFileType>
76 Expected<std::vector<const ObjectFileType *>> getObjectsAs() const {
77 std::vector<const ObjectFileType *> Result;
78 Result.reserve(Objects.size());
79 for (auto &Object : Objects) {
80 const auto *Derived = dyn_cast<ObjectFileType>(Object.get());
81 if (!Derived)
82 return errorCodeToError(EC: object::object_error::invalid_file_type);
83 Result.push_back(Derived);
84 }
85 return Result;
86 }
87
88 /// Access the owned ObjectFile with architecture \p T.
89 Expected<const object::ObjectFile &> getObject(const Triple &T) const;
90
91 /// Access to a derived version of the currently owned ObjectFile with
92 /// architecture \p T. The conversion must be known to be valid.
93 template <typename ObjectFileType>
94 Expected<const ObjectFileType &> getObjectAs(const Triple &T) const {
95 auto Object = getObject(T);
96 if (!Object)
97 return Object.takeError();
98 return cast<ObjectFileType>(*Object);
99 }
100
101 private:
102 std::vector<std::unique_ptr<object::ObjectFile>> Objects;
103 friend ArchiveEntry;
104 };
105
106 /// Cached entry holding one or more (in the of a fat binary) archive files.
107 class ArchiveEntry : public EntryBase {
108 public:
109 struct KeyTy {
110 std::string Filename;
111 TimestampTy Timestamp;
112
113 KeyTy() {}
114 KeyTy(StringRef Filename, TimestampTy Timestamp)
115 : Filename(Filename.str()), Timestamp(Timestamp) {}
116 };
117
118 /// Load the given object binary in memory.
119 Error load(IntrusiveRefCntPtr<vfs::FileSystem> VFS, StringRef Filename,
120 TimestampTy Timestamp, BinaryHolder::Options = {});
121
122 Expected<const ObjectEntry &> getObjectEntry(StringRef Filename,
123 TimestampTy Timestamp,
124 BinaryHolder::Options = {});
125
126 private:
127 std::vector<std::unique_ptr<object::Archive>> Archives;
128 DenseMap<KeyTy, std::unique_ptr<ObjectEntry>> MemberCache;
129 std::mutex MemberCacheMutex;
130 };
131
132 Expected<const ObjectEntry &>
133 getObjectEntry(StringRef Filename, TimestampTy Timestamp = TimestampTy());
134
135 void clear();
136 void eraseObjectEntry(StringRef Filename);
137
138private:
139 /// Cache of static archives. Objects that are part of a static archive are
140 /// stored under this object, rather than in the map below.
141 StringMap<std::unique_ptr<ArchiveEntry>> ArchiveCache;
142 StringMap<uint32_t> ArchiveRefCounter;
143 std::mutex ArchiveCacheMutex;
144
145 /// Object entries for objects that are not in a static archive.
146 StringMap<std::unique_ptr<ObjectEntry>> ObjectCache;
147 StringMap<uint32_t> ObjectRefCounter;
148 std::mutex ObjectCacheMutex;
149
150 /// Virtual File System instance.
151 IntrusiveRefCntPtr<vfs::FileSystem> VFS;
152
153 Options Opts;
154};
155
156} // namespace dsymutil
157
158template <> struct DenseMapInfo<dsymutil::BinaryHolder::ArchiveEntry::KeyTy> {
159
160 static inline dsymutil::BinaryHolder::ArchiveEntry::KeyTy getEmptyKey() {
161 return dsymutil::BinaryHolder::ArchiveEntry::KeyTy();
162 }
163
164 static inline dsymutil::BinaryHolder::ArchiveEntry::KeyTy getTombstoneKey() {
165 return dsymutil::BinaryHolder::ArchiveEntry::KeyTy("/", {});
166 }
167
168 static unsigned
169 getHashValue(const dsymutil::BinaryHolder::ArchiveEntry::KeyTy &K) {
170 return hash_combine(args: DenseMapInfo<StringRef>::getHashValue(Val: K.Filename),
171 args: DenseMapInfo<unsigned>::getHashValue(
172 Val: K.Timestamp.time_since_epoch().count()));
173 }
174
175 static bool isEqual(const dsymutil::BinaryHolder::ArchiveEntry::KeyTy &LHS,
176 const dsymutil::BinaryHolder::ArchiveEntry::KeyTy &RHS) {
177 return LHS.Filename == RHS.Filename && LHS.Timestamp == RHS.Timestamp;
178 }
179};
180
181} // namespace llvm
182#endif
183