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