1 //===-- BinaryHolder.cpp --------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // This program is a utility that aims to be a dropin replacement for
12 //===----------------------------------------------------------------------===//
14 #include "BinaryHolder.h"
15 #include "llvm/Object/MachO.h"
16 #include "llvm/Support/WithColor.h"
17 #include "llvm/Support/raw_ostream.h"
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
.endswith(")"); }
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
;
38 StringRef ObjData
= FatData
.substr(It
->getOffset(), It
->getSize());
39 Buffers
.emplace_back(ObjData
, Filename
);
44 Error
BinaryHolder::ArchiveEntry::load(IntrusiveRefCntPtr
<vfs::FileSystem
> VFS
,
46 TimestampTy Timestamp
, bool Verbose
) {
47 StringRef ArchiveFilename
= getArchiveAndObjectName(Filename
).first
;
49 // Try to load archive and force it to be memory mapped.
50 auto ErrOrBuff
= (ArchiveFilename
== "-")
51 ? MemoryBuffer::getSTDIN()
52 : VFS
->getBufferForFile(ArchiveFilename
, -1, false);
53 if (auto Err
= ErrOrBuff
.getError())
54 return errorCodeToError(Err
);
56 MemBuffer
= std::move(*ErrOrBuff
);
59 WithColor::note() << "loaded archive '" << ArchiveFilename
<< "'\n";
61 // Load one or more archive buffers, depending on whether we're dealing with
63 std::vector
<MemoryBufferRef
> ArchiveBuffers
;
66 object::MachOUniversalBinary::create(MemBuffer
->getMemBufferRef());
68 consumeError(ErrOrFat
.takeError());
69 ArchiveBuffers
.push_back(MemBuffer
->getMemBufferRef());
71 FatBinary
= std::move(*ErrOrFat
);
72 FatBinaryName
= std::string(ArchiveFilename
);
74 getMachOFatMemoryBuffers(FatBinaryName
, *MemBuffer
, *FatBinary
);
77 // Finally, try to load the archives.
78 Archives
.reserve(ArchiveBuffers
.size());
79 for (auto MemRef
: ArchiveBuffers
) {
80 auto ErrOrArchive
= object::Archive::create(MemRef
);
82 return ErrOrArchive
.takeError();
83 Archives
.push_back(std::move(*ErrOrArchive
));
86 return Error::success();
89 Error
BinaryHolder::ObjectEntry::load(IntrusiveRefCntPtr
<vfs::FileSystem
> VFS
,
90 StringRef Filename
, TimestampTy Timestamp
,
92 // Try to load regular binary and force it to be memory mapped.
93 auto ErrOrBuff
= (Filename
== "-")
94 ? MemoryBuffer::getSTDIN()
95 : VFS
->getBufferForFile(Filename
, -1, false);
96 if (auto Err
= ErrOrBuff
.getError())
97 return errorCodeToError(Err
);
99 if (Filename
!= "-" && Timestamp
!= sys::TimePoint
<>()) {
100 llvm::ErrorOr
<vfs::Status
> Stat
= VFS
->status(Filename
);
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";
111 MemBuffer
= std::move(*ErrOrBuff
);
114 WithColor::note() << "loaded object.\n";
116 // Load one or more object buffers, depending on whether we're dealing with a
118 std::vector
<MemoryBufferRef
> ObjectBuffers
;
121 object::MachOUniversalBinary::create(MemBuffer
->getMemBufferRef());
123 consumeError(ErrOrFat
.takeError());
124 ObjectBuffers
.push_back(MemBuffer
->getMemBufferRef());
126 FatBinary
= std::move(*ErrOrFat
);
127 FatBinaryName
= std::string(Filename
);
129 getMachOFatMemoryBuffers(FatBinaryName
, *MemBuffer
, *FatBinary
);
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
));
140 return Error::success();
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());
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())
158 } else if (Obj
->getArch() == T
.getArch())
161 return errorCodeToError(object::object_error::arch_not_found
);
164 Expected
<const BinaryHolder::ObjectEntry
&>
165 BinaryHolder::ArchiveEntry::getObjectEntry(StringRef Filename
,
166 TimestampTy Timestamp
,
168 StringRef ArchiveFilename
;
169 StringRef ObjectFilename
;
170 std::tie(ArchiveFilename
, ObjectFilename
) = getArchiveAndObjectName(Filename
);
171 KeyTy Key
= {ObjectFilename
, Timestamp
};
173 // Try the cache first.
174 std::lock_guard
<std::mutex
> Lock(MemberCacheMutex
);
175 if (MemberCache
.count(Key
))
176 return *MemberCache
[Key
];
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.
181 auto OE
= std::make_unique
<ObjectEntry
>();
183 for (const auto &Archive
: Archives
) {
184 Error Err
= Error::success();
185 for (const auto &Child
: Archive
->children(Err
)) {
186 if (auto NameOrErr
= Child
.getName()) {
187 if (*NameOrErr
== ObjectFilename
) {
188 auto ModTimeOrErr
= Child
.getLastModified();
190 return ModTimeOrErr
.takeError();
192 if (Timestamp
!= sys::TimePoint
<>() &&
193 Timestamp
!= std::chrono::time_point_cast
<std::chrono::seconds
>(
194 ModTimeOrErr
.get())) {
198 << ": timestamp mismatch between archive member ("
199 << ModTimeOrErr
.get() << ") and debug map (" << Timestamp
205 WithColor::note() << "found member in archive.\n";
207 auto ErrOrMem
= Child
.getMemoryBufferRef();
209 return ErrOrMem
.takeError();
211 auto ErrOrObjectFile
=
212 object::ObjectFile::createObjectFile(*ErrOrMem
);
213 if (!ErrOrObjectFile
)
214 return ErrOrObjectFile
.takeError();
216 OE
->Objects
.push_back(std::move(*ErrOrObjectFile
));
221 return std::move(Err
);
224 if (OE
->Objects
.empty())
225 return errorCodeToError(errc::no_such_file_or_directory
);
227 MemberCache
[Key
] = std::move(OE
);
228 return *MemberCache
[Key
];
231 Expected
<const BinaryHolder::ObjectEntry
&>
232 BinaryHolder::getObjectEntry(StringRef Filename
, TimestampTy Timestamp
) {
234 WithColor::note() << "trying to open '" << Filename
<< "'\n";
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 ArchiveRefCounter
[ArchiveFilename
]++;
242 if (ArchiveCache
.count(ArchiveFilename
)) {
243 return ArchiveCache
[ArchiveFilename
]->getObjectEntry(Filename
, Timestamp
,
246 auto AE
= std::make_unique
<ArchiveEntry
>();
247 auto Err
= AE
->load(VFS
, Filename
, Timestamp
, Verbose
);
249 // Don't return the error here: maybe the file wasn't an archive.
250 llvm::consumeError(std::move(Err
));
252 ArchiveCache
[ArchiveFilename
] = std::move(AE
);
253 return ArchiveCache
[ArchiveFilename
]->getObjectEntry(
254 Filename
, Timestamp
, Verbose
);
259 // If this is an object, we might have it cached. If not we'll have to load
260 // it from the file system and cache it now.
261 std::lock_guard
<std::mutex
> Lock(ObjectCacheMutex
);
262 ObjectRefCounter
[Filename
]++;
263 if (!ObjectCache
.count(Filename
)) {
264 auto OE
= std::make_unique
<ObjectEntry
>();
265 auto Err
= OE
->load(VFS
, Filename
, Timestamp
, Verbose
);
267 return std::move(Err
);
268 ObjectCache
[Filename
] = std::move(OE
);
271 return *ObjectCache
[Filename
];
274 void BinaryHolder::clear() {
275 std::lock_guard
<std::mutex
> ArchiveLock(ArchiveCacheMutex
);
276 std::lock_guard
<std::mutex
> ObjectLock(ObjectCacheMutex
);
277 ArchiveCache
.clear();
281 void BinaryHolder::eraseObjectEntry(StringRef Filename
) {
283 WithColor::note() << "erasing '" << Filename
<< "' from cache\n";
285 if (isArchive(Filename
)) {
286 StringRef ArchiveFilename
= getArchiveAndObjectName(Filename
).first
;
287 std::lock_guard
<std::mutex
> Lock(ArchiveCacheMutex
);
288 ArchiveRefCounter
[ArchiveFilename
]--;
289 if (ArchiveRefCounter
[ArchiveFilename
] == 0)
290 ArchiveCache
.erase(ArchiveFilename
);
294 std::lock_guard
<std::mutex
> Lock(ObjectCacheMutex
);
295 ObjectRefCounter
[Filename
]--;
296 if (ObjectRefCounter
[Filename
] == 0)
297 ObjectCache
.erase(Filename
);
300 } // namespace dsymutil