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