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/ADT/Triple.h"
19 #include "llvm/Object/Archive.h"
20 #include "llvm/Object/Error.h"
21 #include "llvm/Object/MachOUniversal.h"
22 #include "llvm/Object/ObjectFile.h"
23 #include "llvm/Support/Chrono.h"
24 #include "llvm/Support/Errc.h"
25 #include "llvm/Support/ErrorOr.h"
26 #include "llvm/Support/VirtualFileSystem.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   BinaryHolder(IntrusiveRefCntPtr<vfs::FileSystem> VFS, bool Verbose = false)
VFS(VFS)42       : VFS(VFS), Verbose(Verbose) {}
43 
44   // Forward declarations for friend declaration.
45   class ObjectEntry;
46   class ArchiveEntry;
47 
48   /// Base class shared by cached entries, representing objects and archives.
49   class EntryBase {
50   protected:
51     std::unique_ptr<MemoryBuffer> MemBuffer;
52     std::unique_ptr<object::MachOUniversalBinary> FatBinary;
53     std::string FatBinaryName;
54   };
55 
56   /// Cached entry holding one or more (in case of a fat binary) object files.
57   class ObjectEntry : public EntryBase {
58   public:
59     /// Load the given object binary in memory.
60     Error load(IntrusiveRefCntPtr<vfs::FileSystem> VFS, StringRef Filename,
61                bool Verbose = false);
62 
63     /// Access all owned ObjectFiles.
64     std::vector<const object::ObjectFile *> getObjects() const;
65 
66     /// Access to a derived version of all the currently owned ObjectFiles. The
67     /// conversion might be invalid, in which case an Error is returned.
68     template <typename ObjectFileType>
getObjectsAs()69     Expected<std::vector<const ObjectFileType *>> getObjectsAs() const {
70       std::vector<const ObjectFileType *> Result;
71       Result.reserve(Objects.size());
72       for (auto &Object : Objects) {
73         const auto *Derived = dyn_cast<ObjectFileType>(Object.get());
74         if (!Derived)
75           return errorCodeToError(object::object_error::invalid_file_type);
76         Result.push_back(Derived);
77       }
78       return Result;
79     }
80 
81     /// Access the owned ObjectFile with architecture \p T.
82     Expected<const object::ObjectFile &> getObject(const Triple &T) const;
83 
84     /// Access to a derived version of the currently owned ObjectFile with
85     /// architecture \p T. The conversion must be known to be valid.
86     template <typename ObjectFileType>
getObjectAs(const Triple & T)87     Expected<const ObjectFileType &> getObjectAs(const Triple &T) const {
88       auto Object = getObject(T);
89       if (!Object)
90         return Object.takeError();
91       return cast<ObjectFileType>(*Object);
92     }
93 
94   private:
95     std::vector<std::unique_ptr<object::ObjectFile>> Objects;
96     friend ArchiveEntry;
97   };
98 
99   /// Cached entry holding one or more (in the of a fat binary) archive files.
100   class ArchiveEntry : public EntryBase {
101   public:
102     struct KeyTy {
103       std::string Filename;
104       TimestampTy Timestamp;
105 
KeyTyKeyTy106       KeyTy() : Filename(), Timestamp() {}
KeyTyKeyTy107       KeyTy(StringRef Filename, TimestampTy Timestamp)
108           : Filename(Filename.str()), Timestamp(Timestamp) {}
109     };
110 
111     /// Load the given object binary in memory.
112     Error load(IntrusiveRefCntPtr<vfs::FileSystem> VFS, StringRef Filename,
113                TimestampTy Timestamp, bool Verbose = false);
114 
115     Expected<const ObjectEntry &> getObjectEntry(StringRef Filename,
116                                                  TimestampTy Timestamp,
117                                                  bool Verbose = false);
118 
119   private:
120     std::vector<std::unique_ptr<object::Archive>> Archives;
121     DenseMap<KeyTy, ObjectEntry> MemberCache;
122     std::mutex MemberCacheMutex;
123   };
124 
125   Expected<const ObjectEntry &>
126   getObjectEntry(StringRef Filename, TimestampTy Timestamp = TimestampTy());
127 
128   void clear();
129 
130 private:
131   /// Cache of static archives. Objects that are part of a static archive are
132   /// stored under this object, rather than in the map below.
133   StringMap<ArchiveEntry> ArchiveCache;
134   std::mutex ArchiveCacheMutex;
135 
136   /// Object entries for objects that are not in a static archive.
137   StringMap<ObjectEntry> ObjectCache;
138   std::mutex ObjectCacheMutex;
139 
140   /// Virtual File System instance.
141   IntrusiveRefCntPtr<vfs::FileSystem> VFS;
142 
143   bool Verbose;
144 };
145 
146 } // namespace dsymutil
147 
148 template <> struct DenseMapInfo<dsymutil::BinaryHolder::ArchiveEntry::KeyTy> {
149 
150   static inline dsymutil::BinaryHolder::ArchiveEntry::KeyTy getEmptyKey() {
151     return dsymutil::BinaryHolder::ArchiveEntry::KeyTy();
152   }
153 
154   static inline dsymutil::BinaryHolder::ArchiveEntry::KeyTy getTombstoneKey() {
155     return dsymutil::BinaryHolder::ArchiveEntry::KeyTy("/", {});
156   }
157 
158   static unsigned
159   getHashValue(const dsymutil::BinaryHolder::ArchiveEntry::KeyTy &K) {
160     return hash_combine(DenseMapInfo<StringRef>::getHashValue(K.Filename),
161                         DenseMapInfo<unsigned>::getHashValue(
162                             K.Timestamp.time_since_epoch().count()));
163   }
164 
165   static bool isEqual(const dsymutil::BinaryHolder::ArchiveEntry::KeyTy &LHS,
166                       const dsymutil::BinaryHolder::ArchiveEntry::KeyTy &RHS) {
167     return LHS.Filename == RHS.Filename && LHS.Timestamp == RHS.Timestamp;
168   }
169 };
170 
171 } // namespace llvm
172 #endif
173