Fix test failures introduced by PR #113697 (#116941)
[llvm-project.git] / llvm / tools / dsymutil / BinaryHolder.cpp
blob5daaa6755b2957f43b1b99e71b3546e2869c11b0
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.
12 //===----------------------------------------------------------------------===//
14 #include "BinaryHolder.h"
15 #include "llvm/Object/MachO.h"
16 #include "llvm/Support/WithColor.h"
17 #include "llvm/Support/raw_ostream.h"
19 namespace llvm {
20 namespace dsymutil {
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};
29 static bool isArchive(StringRef Filename) { return Filename.ends_with(")"); }
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);
41 return Buffers;
44 BinaryHolder::BinaryHolder(IntrusiveRefCntPtr<vfs::FileSystem> VFS,
45 BinaryHolder::Options Opts)
46 : VFS(VFS), Opts(Opts) {}
48 Error BinaryHolder::ArchiveEntry::load(IntrusiveRefCntPtr<vfs::FileSystem> VFS,
49 StringRef Filename,
50 TimestampTy Timestamp, Options Opts) {
51 StringRef ArchiveFilename = getArchiveAndObjectName(Filename).first;
53 // Try to load archive and force it to be memory mapped.
54 auto ErrOrBuff = (ArchiveFilename == "-")
55 ? MemoryBuffer::getSTDIN()
56 : VFS->getBufferForFile(ArchiveFilename, -1, false);
57 if (auto Err = ErrOrBuff.getError())
58 return errorCodeToError(Err);
60 MemBuffer = std::move(*ErrOrBuff);
62 if (Opts.Verbose)
63 WithColor::note() << "loaded archive '" << ArchiveFilename << "'\n";
65 // Load one or more archive buffers, depending on whether we're dealing with
66 // a fat binary.
67 std::vector<MemoryBufferRef> ArchiveBuffers;
69 auto ErrOrFat =
70 object::MachOUniversalBinary::create(MemBuffer->getMemBufferRef());
71 if (!ErrOrFat) {
72 consumeError(ErrOrFat.takeError());
73 ArchiveBuffers.push_back(MemBuffer->getMemBufferRef());
74 } else {
75 FatBinary = std::move(*ErrOrFat);
76 FatBinaryName = std::string(ArchiveFilename);
77 ArchiveBuffers =
78 getMachOFatMemoryBuffers(FatBinaryName, *MemBuffer, *FatBinary);
81 // Finally, try to load the archives.
82 Archives.reserve(ArchiveBuffers.size());
83 for (auto MemRef : ArchiveBuffers) {
84 auto ErrOrArchive = object::Archive::create(MemRef);
85 if (!ErrOrArchive)
86 return ErrOrArchive.takeError();
87 Archives.push_back(std::move(*ErrOrArchive));
90 return Error::success();
93 Error BinaryHolder::ObjectEntry::load(IntrusiveRefCntPtr<vfs::FileSystem> VFS,
94 StringRef Filename, TimestampTy Timestamp,
95 Options Opts) {
96 // Try to load regular binary and force it to be memory mapped.
97 auto ErrOrBuff = (Filename == "-")
98 ? MemoryBuffer::getSTDIN()
99 : VFS->getBufferForFile(Filename, -1, false);
100 if (auto Err = ErrOrBuff.getError())
101 return errorCodeToError(Err);
103 if (Opts.Warn && Filename != "-" && Timestamp != sys::TimePoint<>()) {
104 llvm::ErrorOr<vfs::Status> Stat = VFS->status(Filename);
105 if (!Stat)
106 return errorCodeToError(Stat.getError());
107 if (Timestamp != std::chrono::time_point_cast<std::chrono::seconds>(
108 Stat->getLastModificationTime()))
109 WithColor::warning() << Filename
110 << ": timestamp mismatch between object file ("
111 << Stat->getLastModificationTime()
112 << ") and debug map (" << Timestamp << ")\n";
115 MemBuffer = std::move(*ErrOrBuff);
117 if (Opts.Verbose)
118 WithColor::note() << "loaded object.\n";
120 // Load one or more object buffers, depending on whether we're dealing with a
121 // fat binary.
122 std::vector<MemoryBufferRef> ObjectBuffers;
124 auto ErrOrFat =
125 object::MachOUniversalBinary::create(MemBuffer->getMemBufferRef());
126 if (!ErrOrFat) {
127 consumeError(ErrOrFat.takeError());
128 ObjectBuffers.push_back(MemBuffer->getMemBufferRef());
129 } else {
130 FatBinary = std::move(*ErrOrFat);
131 FatBinaryName = std::string(Filename);
132 ObjectBuffers =
133 getMachOFatMemoryBuffers(FatBinaryName, *MemBuffer, *FatBinary);
136 Objects.reserve(ObjectBuffers.size());
137 for (auto MemRef : ObjectBuffers) {
138 auto ErrOrObjectFile = object::ObjectFile::createObjectFile(MemRef);
139 if (!ErrOrObjectFile)
140 return ErrOrObjectFile.takeError();
141 Objects.push_back(std::move(*ErrOrObjectFile));
144 return Error::success();
147 std::vector<const object::ObjectFile *>
148 BinaryHolder::ObjectEntry::getObjects() const {
149 std::vector<const object::ObjectFile *> Result;
150 Result.reserve(Objects.size());
151 for (auto &Object : Objects) {
152 Result.push_back(Object.get());
154 return Result;
156 Expected<const object::ObjectFile &>
157 BinaryHolder::ObjectEntry::getObject(const Triple &T) const {
158 for (const auto &Obj : Objects) {
159 if (const auto *MachO = dyn_cast<object::MachOObjectFile>(Obj.get())) {
160 if (MachO->getArchTriple().str() == T.str())
161 return *MachO;
162 } else if (Obj->getArch() == T.getArch())
163 return *Obj;
165 return errorCodeToError(object::object_error::arch_not_found);
168 Expected<const BinaryHolder::ObjectEntry &>
169 BinaryHolder::ArchiveEntry::getObjectEntry(StringRef Filename,
170 TimestampTy Timestamp,
171 Options Opts) {
172 StringRef ArchiveFilename;
173 StringRef ObjectFilename;
174 std::tie(ArchiveFilename, ObjectFilename) = getArchiveAndObjectName(Filename);
175 KeyTy Key = {ObjectFilename, Timestamp};
177 // Try the cache first.
178 std::lock_guard<std::mutex> Lock(MemberCacheMutex);
179 if (MemberCache.count(Key))
180 return *MemberCache[Key];
182 // Create a new ObjectEntry, but don't add it to the cache yet. Loading of
183 // the archive members might fail and we don't want to lock the whole archive
184 // during this operation.
185 auto OE = std::make_unique<ObjectEntry>();
187 for (const auto &Archive : Archives) {
188 Error Err = Error::success();
189 for (const auto &Child : Archive->children(Err)) {
190 if (auto NameOrErr = Child.getName()) {
191 if (*NameOrErr == ObjectFilename) {
192 auto ModTimeOrErr = Child.getLastModified();
193 if (!ModTimeOrErr)
194 return ModTimeOrErr.takeError();
196 if (Timestamp != sys::TimePoint<>() &&
197 Timestamp != std::chrono::time_point_cast<std::chrono::seconds>(
198 ModTimeOrErr.get())) {
199 if (Opts.Verbose)
200 WithColor::warning()
201 << *NameOrErr
202 << ": timestamp mismatch between archive member ("
203 << ModTimeOrErr.get() << ") and debug map (" << Timestamp
204 << ")\n";
205 continue;
208 if (Opts.Verbose)
209 WithColor::note() << "found member in archive.\n";
211 auto ErrOrMem = Child.getMemoryBufferRef();
212 if (!ErrOrMem)
213 return ErrOrMem.takeError();
215 auto ErrOrObjectFile =
216 object::ObjectFile::createObjectFile(*ErrOrMem);
217 if (!ErrOrObjectFile)
218 return ErrOrObjectFile.takeError();
220 OE->Objects.push_back(std::move(*ErrOrObjectFile));
224 if (Err)
225 return std::move(Err);
228 if (OE->Objects.empty())
229 return errorCodeToError(errc::no_such_file_or_directory);
231 MemberCache[Key] = std::move(OE);
232 return *MemberCache[Key];
235 Expected<const BinaryHolder::ObjectEntry &>
236 BinaryHolder::getObjectEntry(StringRef Filename, TimestampTy Timestamp) {
237 if (Opts.Verbose)
238 WithColor::note() << "trying to open '" << Filename << "'\n";
240 // If this is an archive, we might have either the object or the archive
241 // cached. In this case we can load it without accessing the file system.
242 if (isArchive(Filename)) {
243 StringRef ArchiveFilename = getArchiveAndObjectName(Filename).first;
244 std::lock_guard<std::mutex> Lock(ArchiveCacheMutex);
245 ArchiveRefCounter[ArchiveFilename]++;
246 if (ArchiveCache.count(ArchiveFilename)) {
247 return ArchiveCache[ArchiveFilename]->getObjectEntry(Filename, Timestamp,
248 Opts);
249 } else {
250 auto AE = std::make_unique<ArchiveEntry>();
251 auto Err = AE->load(VFS, Filename, Timestamp, Opts);
252 if (Err) {
253 // Don't return the error here: maybe the file wasn't an archive.
254 llvm::consumeError(std::move(Err));
255 } else {
256 ArchiveCache[ArchiveFilename] = std::move(AE);
257 return ArchiveCache[ArchiveFilename]->getObjectEntry(Filename,
258 Timestamp, Opts);
263 // If this is an object, we might have it cached. If not we'll have to load
264 // it from the file system and cache it now.
265 std::lock_guard<std::mutex> Lock(ObjectCacheMutex);
266 ObjectRefCounter[Filename]++;
267 if (!ObjectCache.count(Filename)) {
268 auto OE = std::make_unique<ObjectEntry>();
269 auto Err = OE->load(VFS, Filename, Timestamp, Opts);
270 if (Err)
271 return std::move(Err);
272 ObjectCache[Filename] = std::move(OE);
275 return *ObjectCache[Filename];
278 void BinaryHolder::clear() {
279 std::lock_guard<std::mutex> ArchiveLock(ArchiveCacheMutex);
280 std::lock_guard<std::mutex> ObjectLock(ObjectCacheMutex);
281 ArchiveCache.clear();
282 ObjectCache.clear();
285 void BinaryHolder::eraseObjectEntry(StringRef Filename) {
286 if (Opts.Verbose)
287 WithColor::note() << "erasing '" << Filename << "' from cache\n";
289 if (isArchive(Filename)) {
290 StringRef ArchiveFilename = getArchiveAndObjectName(Filename).first;
291 std::lock_guard<std::mutex> Lock(ArchiveCacheMutex);
292 ArchiveRefCounter[ArchiveFilename]--;
293 if (ArchiveRefCounter[ArchiveFilename] == 0)
294 ArchiveCache.erase(ArchiveFilename);
295 return;
298 std::lock_guard<std::mutex> Lock(ObjectCacheMutex);
299 ObjectRefCounter[Filename]--;
300 if (ObjectRefCounter[Filename] == 0)
301 ObjectCache.erase(Filename);
304 } // namespace dsymutil
305 } // namespace llvm