xref: /llvm-project/llvm/tools/dsymutil/BinaryHolder.h (revision 5886454669c3c9026f7f27eab13509dd0241f2d6)
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 
30 namespace llvm {
31 namespace 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.
37 class BinaryHolder {
38 public:
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(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 
138 private:
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 
158 template <> 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(DenseMapInfo<StringRef>::getHashValue(K.Filename),
171                         DenseMapInfo<unsigned>::getHashValue(
172                             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