summaryrefslogtreecommitdiffstats
path: root/llvm/tools/dsymutil/BinaryHolder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/tools/dsymutil/BinaryHolder.cpp')
-rw-r--r--llvm/tools/dsymutil/BinaryHolder.cpp226
1 files changed, 226 insertions, 0 deletions
diff --git a/llvm/tools/dsymutil/BinaryHolder.cpp b/llvm/tools/dsymutil/BinaryHolder.cpp
index 356156a5ebf..8264f465a5d 100644
--- a/llvm/tools/dsymutil/BinaryHolder.cpp
+++ b/llvm/tools/dsymutil/BinaryHolder.cpp
@@ -14,11 +14,21 @@
#include "BinaryHolder.h"
#include "llvm/Object/MachO.h"
+#include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h"
namespace llvm {
namespace dsymutil {
+static std::pair<StringRef, StringRef>
+getArchiveAndObjectName(StringRef Filename) {
+ StringRef Archive = Filename.substr(0, Filename.find('('));
+ StringRef Object = Filename.substr(Archive.size() + 1).drop_back();
+ return {Archive, Object};
+}
+
+static bool isArchive(StringRef Filename) { return Filename.endswith(")"); }
+
static std::vector<MemoryBufferRef>
getMachOFatMemoryBuffers(StringRef Filename, MemoryBuffer &Mem,
object::MachOUniversalBinary &Fat) {
@@ -32,6 +42,222 @@ getMachOFatMemoryBuffers(StringRef Filename, MemoryBuffer &Mem,
return Buffers;
}
+Error CachedBinaryHolder::ArchiveEntry::load(StringRef Filename,
+ TimestampTy Timestamp,
+ bool Verbose) {
+ StringRef ArchiveFilename = getArchiveAndObjectName(Filename).first;
+
+ // Try to load archive and force it to be memory mapped.
+ auto ErrOrBuff = MemoryBuffer::getFileOrSTDIN(ArchiveFilename, -1, false);
+ if (auto Err = ErrOrBuff.getError())
+ return errorCodeToError(Err);
+
+ MemoryBuffer = std::move(*ErrOrBuff);
+
+ if (Verbose)
+ WithColor::note() << "opened archive '" << ArchiveFilename << "'\n";
+
+ // Load one or more archive buffers, depending on whether we're dealing with
+ // a fat binary.
+ std::vector<MemoryBufferRef> ArchiveBuffers;
+
+ auto ErrOrFat =
+ object::MachOUniversalBinary::create(MemoryBuffer->getMemBufferRef());
+ if (!ErrOrFat) {
+ consumeError(ErrOrFat.takeError());
+ ArchiveBuffers.push_back(MemoryBuffer->getMemBufferRef());
+ } else {
+ FatBinary = std::move(*ErrOrFat);
+ FatBinaryName = ArchiveFilename;
+ ArchiveBuffers =
+ getMachOFatMemoryBuffers(FatBinaryName, *MemoryBuffer, *FatBinary);
+ }
+
+ // Finally, try to load the archives.
+ Archives.reserve(ArchiveBuffers.size());
+ for (auto MemRef : ArchiveBuffers) {
+ auto ErrOrArchive = object::Archive::create(MemRef);
+ if (!ErrOrArchive)
+ return ErrOrArchive.takeError();
+ Archives.push_back(std::move(*ErrOrArchive));
+ }
+
+ return Error::success();
+}
+
+Error CachedBinaryHolder::ObjectEntry::load(StringRef Filename, bool Verbose) {
+ // Try to load regular binary and force it to be memory mapped.
+ auto ErrOrBuff = MemoryBuffer::getFileOrSTDIN(Filename, -1, false);
+ if (auto Err = ErrOrBuff.getError())
+ return errorCodeToError(Err);
+
+ MemoryBuffer = std::move(*ErrOrBuff);
+
+ if (Verbose)
+ WithColor::note() << "opened object.\n";
+
+ // Load one or more object buffers, depending on whether we're dealing with a
+ // fat binary.
+ std::vector<MemoryBufferRef> ObjectBuffers;
+
+ auto ErrOrFat =
+ object::MachOUniversalBinary::create(MemoryBuffer->getMemBufferRef());
+ if (!ErrOrFat) {
+ consumeError(ErrOrFat.takeError());
+ ObjectBuffers.push_back(MemoryBuffer->getMemBufferRef());
+ } else {
+ FatBinary = std::move(*ErrOrFat);
+ FatBinaryName = Filename;
+ ObjectBuffers =
+ getMachOFatMemoryBuffers(FatBinaryName, *MemoryBuffer, *FatBinary);
+ }
+
+ Objects.reserve(ObjectBuffers.size());
+ for (auto MemRef : ObjectBuffers) {
+ auto ErrOrObjectFile = object::ObjectFile::createObjectFile(MemRef);
+ if (!ErrOrObjectFile)
+ return ErrOrObjectFile.takeError();
+ Objects.push_back(std::move(*ErrOrObjectFile));
+ }
+
+ return Error::success();
+}
+
+std::vector<const object::ObjectFile *>
+CachedBinaryHolder::ObjectEntry::getObjects() const {
+ std::vector<const object::ObjectFile *> Result;
+ Result.reserve(Objects.size());
+ for (auto &Object : Objects) {
+ Result.push_back(Object.get());
+ }
+ return Result;
+}
+Expected<const object::ObjectFile &>
+CachedBinaryHolder::ObjectEntry::getObject(const Triple &T) const {
+ for (const auto &Obj : Objects) {
+ if (const auto *MachO = dyn_cast<object::MachOObjectFile>(Obj.get())) {
+ if (MachO->getArchTriple().str() == T.str())
+ return *MachO;
+ } else if (Obj->getArch() == T.getArch())
+ return *Obj;
+ }
+ return errorCodeToError(object::object_error::arch_not_found);
+}
+
+Expected<const CachedBinaryHolder::ObjectEntry &>
+CachedBinaryHolder::ArchiveEntry::getObjectEntry(StringRef Filename,
+ TimestampTy Timestamp,
+ bool Verbose) {
+ StringRef ArchiveFilename;
+ StringRef ObjectFilename;
+ std::tie(ArchiveFilename, ObjectFilename) = getArchiveAndObjectName(Filename);
+
+ // Try the cache first.
+ KeyTy Key = {ObjectFilename, Timestamp};
+
+ {
+ std::lock_guard<std::mutex> Lock(MemberCacheMutex);
+ if (MemberCache.count(Key))
+ return MemberCache[Key];
+ }
+
+ // Create a new ObjectEntry, but don't add it to the cache yet. Loading of
+ // the archive members might fail and we don't want to lock the whole archive
+ // during this operation.
+ ObjectEntry OE;
+
+ for (const auto &Archive : Archives) {
+ Error Err = Error::success();
+ for (auto Child : Archive->children(Err)) {
+ if (auto NameOrErr = Child.getName()) {
+ if (*NameOrErr == ObjectFilename) {
+ auto ModTimeOrErr = Child.getLastModified();
+ if (!ModTimeOrErr)
+ return ModTimeOrErr.takeError();
+
+ if (Timestamp != sys::TimePoint<>() &&
+ Timestamp != ModTimeOrErr.get()) {
+ if (Verbose)
+ WithColor::warning() << "member has timestamp mismatch.\n";
+ continue;
+ }
+
+ if (Verbose)
+ WithColor::note() << "found member in current archive.\n";
+
+ auto ErrOrMem = Child.getMemoryBufferRef();
+ if (!ErrOrMem)
+ return ErrOrMem.takeError();
+
+ auto ErrOrObjectFile =
+ object::ObjectFile::createObjectFile(*ErrOrMem);
+ if (!ErrOrObjectFile)
+ return ErrOrObjectFile.takeError();
+
+ OE.Objects.push_back(std::move(*ErrOrObjectFile));
+ }
+ }
+ }
+ if (Err)
+ return std::move(Err);
+ }
+
+ if (OE.Objects.empty())
+ return errorCodeToError(errc::no_such_file_or_directory);
+
+ std::lock_guard<std::mutex> Lock(MemberCacheMutex);
+ MemberCache.try_emplace(Key, std::move(OE));
+ return MemberCache[Key];
+}
+
+Expected<const CachedBinaryHolder::ObjectEntry &>
+CachedBinaryHolder::getObjectEntry(StringRef Filename, TimestampTy Timestamp) {
+ if (Verbose)
+ WithColor::note() << "trying to open '" << Filename << "'\n";
+
+ // If this is an archive, we might have either the object or the archive
+ // cached. In this case we can load it without accessing the file system.
+ if (isArchive(Filename)) {
+ StringRef ArchiveFilename = getArchiveAndObjectName(Filename).first;
+ std::lock_guard<std::mutex> Lock(ArchiveCacheMutex);
+ if (ArchiveCache.count(ArchiveFilename)) {
+ return ArchiveCache[ArchiveFilename].getObjectEntry(Filename, Timestamp);
+ } else {
+ ArchiveEntry &AE = ArchiveCache[ArchiveFilename];
+ auto Err = AE.load(Filename, Timestamp, Verbose);
+ if (Err) {
+ ArchiveCache.erase(ArchiveFilename);
+ // Don't return the error here: maybe the file wasn't an archive.
+ llvm::consumeError(std::move(Err));
+ } else {
+ return ArchiveCache[ArchiveFilename].getObjectEntry(Filename,
+ Timestamp);
+ }
+ }
+ }
+
+ // If this is an object, we might have it cached. If not we'll have to load
+ // it from the file system and cache it now.
+ std::lock_guard<std::mutex> Lock(ObjectCacheMutex);
+ if (!ObjectCache.count(Filename)) {
+ ObjectEntry &OE = ObjectCache[Filename];
+ auto Err = OE.load(Filename);
+ if (Err) {
+ ObjectCache.erase(Filename);
+ return std::move(Err);
+ }
+ }
+
+ return ObjectCache[Filename];
+}
+
+void CachedBinaryHolder::clear() {
+ std::lock_guard<std::mutex> ArchiveLock(ArchiveCacheMutex);
+ std::lock_guard<std::mutex> ObjectLock(ObjectCacheMutex);
+ ArchiveCache.clear();
+ ObjectCache.clear();
+}
+
void BinaryHolder::changeBackingMemoryBuffer(
std::unique_ptr<MemoryBuffer> &&Buf) {
CurrentArchives.clear();
OpenPOWER on IntegriCloud