• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===-- BinaryHolder.cpp --------------------------------------------------===//
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 
15 #include "BinaryHolder.h"
16 #include "llvm/Object/MachO.h"
17 #include "llvm/Support/WithColor.h"
18 #include "llvm/Support/raw_ostream.h"
19 
20 namespace llvm {
21 namespace dsymutil {
22 
23 static std::pair<StringRef, StringRef>
getArchiveAndObjectName(StringRef Filename)24 getArchiveAndObjectName(StringRef Filename) {
25   StringRef Archive = Filename.substr(0, Filename.find('('));
26   StringRef Object = Filename.substr(Archive.size() + 1).drop_back();
27   return {Archive, Object};
28 }
29 
isArchive(StringRef Filename)30 static bool isArchive(StringRef Filename) { return Filename.endswith(")"); }
31 
32 static std::vector<MemoryBufferRef>
getMachOFatMemoryBuffers(StringRef Filename,MemoryBuffer & Mem,object::MachOUniversalBinary & Fat)33 getMachOFatMemoryBuffers(StringRef Filename, MemoryBuffer &Mem,
34                          object::MachOUniversalBinary &Fat) {
35   std::vector<MemoryBufferRef> Buffers;
36   StringRef FatData = Fat.getData();
37   for (auto It = Fat.begin_objects(), End = Fat.end_objects(); It != End;
38        ++It) {
39     StringRef ObjData = FatData.substr(It->getOffset(), It->getSize());
40     Buffers.emplace_back(ObjData, Filename);
41   }
42   return Buffers;
43 }
44 
load(StringRef Filename,TimestampTy Timestamp,bool Verbose)45 Error BinaryHolder::ArchiveEntry::load(StringRef Filename,
46                                        TimestampTy Timestamp, bool Verbose) {
47   StringRef ArchiveFilename = getArchiveAndObjectName(Filename).first;
48 
49   // Try to load archive and force it to be memory mapped.
50   auto ErrOrBuff = MemoryBuffer::getFileOrSTDIN(ArchiveFilename, -1, false);
51   if (auto Err = ErrOrBuff.getError())
52     return errorCodeToError(Err);
53 
54   MemBuffer = std::move(*ErrOrBuff);
55 
56   if (Verbose)
57     WithColor::note() << "loaded archive '" << ArchiveFilename << "'\n";
58 
59   // Load one or more archive buffers, depending on whether we're dealing with
60   // a fat binary.
61   std::vector<MemoryBufferRef> ArchiveBuffers;
62 
63   auto ErrOrFat =
64       object::MachOUniversalBinary::create(MemBuffer->getMemBufferRef());
65   if (!ErrOrFat) {
66     consumeError(ErrOrFat.takeError());
67     ArchiveBuffers.push_back(MemBuffer->getMemBufferRef());
68   } else {
69     FatBinary = std::move(*ErrOrFat);
70     FatBinaryName = ArchiveFilename;
71     ArchiveBuffers =
72         getMachOFatMemoryBuffers(FatBinaryName, *MemBuffer, *FatBinary);
73   }
74 
75   // Finally, try to load the archives.
76   Archives.reserve(ArchiveBuffers.size());
77   for (auto MemRef : ArchiveBuffers) {
78     auto ErrOrArchive = object::Archive::create(MemRef);
79     if (!ErrOrArchive)
80       return ErrOrArchive.takeError();
81     Archives.push_back(std::move(*ErrOrArchive));
82   }
83 
84   return Error::success();
85 }
86 
load(StringRef Filename,bool Verbose)87 Error BinaryHolder::ObjectEntry::load(StringRef Filename, bool Verbose) {
88   // Try to load regular binary and force it to be memory mapped.
89   auto ErrOrBuff = MemoryBuffer::getFileOrSTDIN(Filename, -1, false);
90   if (auto Err = ErrOrBuff.getError())
91     return errorCodeToError(Err);
92 
93   MemBuffer = std::move(*ErrOrBuff);
94 
95   if (Verbose)
96     WithColor::note() << "loaded object.\n";
97 
98   // Load one or more object buffers, depending on whether we're dealing with a
99   // fat binary.
100   std::vector<MemoryBufferRef> ObjectBuffers;
101 
102   auto ErrOrFat =
103       object::MachOUniversalBinary::create(MemBuffer->getMemBufferRef());
104   if (!ErrOrFat) {
105     consumeError(ErrOrFat.takeError());
106     ObjectBuffers.push_back(MemBuffer->getMemBufferRef());
107   } else {
108     FatBinary = std::move(*ErrOrFat);
109     FatBinaryName = Filename;
110     ObjectBuffers =
111         getMachOFatMemoryBuffers(FatBinaryName, *MemBuffer, *FatBinary);
112   }
113 
114   Objects.reserve(ObjectBuffers.size());
115   for (auto MemRef : ObjectBuffers) {
116     auto ErrOrObjectFile = object::ObjectFile::createObjectFile(MemRef);
117     if (!ErrOrObjectFile)
118       return ErrOrObjectFile.takeError();
119     Objects.push_back(std::move(*ErrOrObjectFile));
120   }
121 
122   return Error::success();
123 }
124 
125 std::vector<const object::ObjectFile *>
getObjects() const126 BinaryHolder::ObjectEntry::getObjects() const {
127   std::vector<const object::ObjectFile *> Result;
128   Result.reserve(Objects.size());
129   for (auto &Object : Objects) {
130     Result.push_back(Object.get());
131   }
132   return Result;
133 }
134 Expected<const object::ObjectFile &>
getObject(const Triple & T) const135 BinaryHolder::ObjectEntry::getObject(const Triple &T) const {
136   for (const auto &Obj : Objects) {
137     if (const auto *MachO = dyn_cast<object::MachOObjectFile>(Obj.get())) {
138       if (MachO->getArchTriple().str() == T.str())
139         return *MachO;
140     } else if (Obj->getArch() == T.getArch())
141       return *Obj;
142   }
143   return errorCodeToError(object::object_error::arch_not_found);
144 }
145 
146 Expected<const BinaryHolder::ObjectEntry &>
getObjectEntry(StringRef Filename,TimestampTy Timestamp,bool Verbose)147 BinaryHolder::ArchiveEntry::getObjectEntry(StringRef Filename,
148                                            TimestampTy Timestamp,
149                                            bool Verbose) {
150   StringRef ArchiveFilename;
151   StringRef ObjectFilename;
152   std::tie(ArchiveFilename, ObjectFilename) = getArchiveAndObjectName(Filename);
153 
154   // Try the cache first.
155   KeyTy Key = {ObjectFilename, Timestamp};
156 
157   {
158     std::lock_guard<std::mutex> Lock(MemberCacheMutex);
159     if (MemberCache.count(Key))
160       return MemberCache[Key];
161   }
162 
163   // Create a new ObjectEntry, but don't add it to the cache yet. Loading of
164   // the archive members might fail and we don't want to lock the whole archive
165   // during this operation.
166   ObjectEntry OE;
167 
168   for (const auto &Archive : Archives) {
169     Error Err = Error::success();
170     for (auto Child : Archive->children(Err)) {
171       if (auto NameOrErr = Child.getName()) {
172         if (*NameOrErr == ObjectFilename) {
173           auto ModTimeOrErr = Child.getLastModified();
174           if (!ModTimeOrErr)
175             return ModTimeOrErr.takeError();
176 
177           if (Timestamp != sys::TimePoint<>() &&
178               Timestamp != ModTimeOrErr.get()) {
179             if (Verbose)
180               WithColor::warning() << "member has timestamp mismatch.\n";
181             continue;
182           }
183 
184           if (Verbose)
185             WithColor::note() << "found member in archive.\n";
186 
187           auto ErrOrMem = Child.getMemoryBufferRef();
188           if (!ErrOrMem)
189             return ErrOrMem.takeError();
190 
191           auto ErrOrObjectFile =
192               object::ObjectFile::createObjectFile(*ErrOrMem);
193           if (!ErrOrObjectFile)
194             return ErrOrObjectFile.takeError();
195 
196           OE.Objects.push_back(std::move(*ErrOrObjectFile));
197         }
198       }
199     }
200     if (Err)
201       return std::move(Err);
202   }
203 
204   if (OE.Objects.empty())
205     return errorCodeToError(errc::no_such_file_or_directory);
206 
207   std::lock_guard<std::mutex> Lock(MemberCacheMutex);
208   MemberCache.try_emplace(Key, std::move(OE));
209   return MemberCache[Key];
210 }
211 
212 Expected<const BinaryHolder::ObjectEntry &>
getObjectEntry(StringRef Filename,TimestampTy Timestamp)213 BinaryHolder::getObjectEntry(StringRef Filename, TimestampTy Timestamp) {
214   if (Verbose)
215     WithColor::note() << "trying to open '" << Filename << "'\n";
216 
217   // If this is an archive, we might have either the object or the archive
218   // cached. In this case we can load it without accessing the file system.
219   if (isArchive(Filename)) {
220     StringRef ArchiveFilename = getArchiveAndObjectName(Filename).first;
221     std::lock_guard<std::mutex> Lock(ArchiveCacheMutex);
222     if (ArchiveCache.count(ArchiveFilename)) {
223       return ArchiveCache[ArchiveFilename].getObjectEntry(Filename, Timestamp,
224                                                           Verbose);
225     } else {
226       ArchiveEntry &AE = ArchiveCache[ArchiveFilename];
227       auto Err = AE.load(Filename, Timestamp, Verbose);
228       if (Err) {
229         ArchiveCache.erase(ArchiveFilename);
230         // Don't return the error here: maybe the file wasn't an archive.
231         llvm::consumeError(std::move(Err));
232       } else {
233         return ArchiveCache[ArchiveFilename].getObjectEntry(Filename, Timestamp,
234                                                             Verbose);
235       }
236     }
237   }
238 
239   // If this is an object, we might have it cached. If not we'll have to load
240   // it from the file system and cache it now.
241   std::lock_guard<std::mutex> Lock(ObjectCacheMutex);
242   if (!ObjectCache.count(Filename)) {
243     ObjectEntry &OE = ObjectCache[Filename];
244     auto Err = OE.load(Filename, Verbose);
245     if (Err) {
246       ObjectCache.erase(Filename);
247       return std::move(Err);
248     }
249   }
250 
251   return ObjectCache[Filename];
252 }
253 
clear()254 void BinaryHolder::clear() {
255   std::lock_guard<std::mutex> ArchiveLock(ArchiveCacheMutex);
256   std::lock_guard<std::mutex> ObjectLock(ObjectCacheMutex);
257   ArchiveCache.clear();
258   ObjectCache.clear();
259 }
260 
261 } // namespace dsymutil
262 } // namespace llvm
263