diff options
Diffstat (limited to 'llvm/tools/dsymutil/BinaryHolder.cpp')
-rw-r--r-- | llvm/tools/dsymutil/BinaryHolder.cpp | 226 |
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(); |