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