123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280 |
- //===-- BinaryHolder.cpp --------------------------------------------------===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- //
- // This program is a utility that aims to be a dropin replacement for
- // Darwin's dsymutil.
- //
- //===----------------------------------------------------------------------===//
- #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.rfind('('));
- 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) {
- std::vector<MemoryBufferRef> Buffers;
- StringRef FatData = Fat.getData();
- for (auto It = Fat.begin_objects(), End = Fat.end_objects(); It != End;
- ++It) {
- StringRef ObjData = FatData.substr(It->getOffset(), It->getSize());
- Buffers.emplace_back(ObjData, Filename);
- }
- return Buffers;
- }
- Error BinaryHolder::ArchiveEntry::load(IntrusiveRefCntPtr<vfs::FileSystem> VFS,
- StringRef Filename,
- TimestampTy Timestamp, bool Verbose) {
- StringRef ArchiveFilename = getArchiveAndObjectName(Filename).first;
- // Try to load archive and force it to be memory mapped.
- auto ErrOrBuff = (ArchiveFilename == "-")
- ? MemoryBuffer::getSTDIN()
- : VFS->getBufferForFile(ArchiveFilename, -1, false);
- if (auto Err = ErrOrBuff.getError())
- return errorCodeToError(Err);
- MemBuffer = std::move(*ErrOrBuff);
- if (Verbose)
- WithColor::note() << "loaded 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(MemBuffer->getMemBufferRef());
- if (!ErrOrFat) {
- consumeError(ErrOrFat.takeError());
- ArchiveBuffers.push_back(MemBuffer->getMemBufferRef());
- } else {
- FatBinary = std::move(*ErrOrFat);
- FatBinaryName = std::string(ArchiveFilename);
- ArchiveBuffers =
- getMachOFatMemoryBuffers(FatBinaryName, *MemBuffer, *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 BinaryHolder::ObjectEntry::load(IntrusiveRefCntPtr<vfs::FileSystem> VFS,
- StringRef Filename, TimestampTy Timestamp,
- bool Verbose) {
- // Try to load regular binary and force it to be memory mapped.
- auto ErrOrBuff = (Filename == "-")
- ? MemoryBuffer::getSTDIN()
- : VFS->getBufferForFile(Filename, -1, false);
- if (auto Err = ErrOrBuff.getError())
- return errorCodeToError(Err);
- if (Filename != "-" && Timestamp != sys::TimePoint<>()) {
- llvm::ErrorOr<vfs::Status> Stat = VFS->status(Filename);
- if (!Stat)
- return errorCodeToError(Stat.getError());
- if (Timestamp != std::chrono::time_point_cast<std::chrono::seconds>(
- Stat->getLastModificationTime()))
- WithColor::warning() << Filename
- << ": timestamp mismatch between object file ("
- << Stat->getLastModificationTime()
- << ") and debug map (" << Timestamp << ")\n";
- }
- MemBuffer = std::move(*ErrOrBuff);
- if (Verbose)
- WithColor::note() << "loaded 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(MemBuffer->getMemBufferRef());
- if (!ErrOrFat) {
- consumeError(ErrOrFat.takeError());
- ObjectBuffers.push_back(MemBuffer->getMemBufferRef());
- } else {
- FatBinary = std::move(*ErrOrFat);
- FatBinaryName = std::string(Filename);
- ObjectBuffers =
- getMachOFatMemoryBuffers(FatBinaryName, *MemBuffer, *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 *>
- BinaryHolder::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 &>
- BinaryHolder::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 BinaryHolder::ObjectEntry &>
- BinaryHolder::ArchiveEntry::getObjectEntry(StringRef Filename,
- TimestampTy Timestamp,
- bool Verbose) {
- StringRef ArchiveFilename;
- StringRef ObjectFilename;
- std::tie(ArchiveFilename, ObjectFilename) = getArchiveAndObjectName(Filename);
- KeyTy Key = {ObjectFilename, Timestamp};
- // Try the cache first.
- 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.
- auto OE = std::make_unique<ObjectEntry>();
- 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 != std::chrono::time_point_cast<std::chrono::seconds>(
- ModTimeOrErr.get())) {
- if (Verbose)
- WithColor::warning()
- << *NameOrErr
- << ": timestamp mismatch between archive member ("
- << ModTimeOrErr.get() << ") and debug map (" << Timestamp
- << ")\n";
- continue;
- }
- if (Verbose)
- WithColor::note() << "found member in 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);
- MemberCache[Key] = std::move(OE);
- return *MemberCache[Key];
- }
- Expected<const BinaryHolder::ObjectEntry &>
- BinaryHolder::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,
- Verbose);
- } else {
- auto AE = std::make_unique<ArchiveEntry>();
- auto Err = AE->load(VFS, Filename, Timestamp, Verbose);
- if (Err) {
- // Don't return the error here: maybe the file wasn't an archive.
- llvm::consumeError(std::move(Err));
- } else {
- ArchiveCache[ArchiveFilename] = std::move(AE);
- return ArchiveCache[ArchiveFilename]->getObjectEntry(
- Filename, Timestamp, Verbose);
- }
- }
- }
- // 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)) {
- auto OE = std::make_unique<ObjectEntry>();
- auto Err = OE->load(VFS, Filename, Timestamp, Verbose);
- if (Err)
- return std::move(Err);
- ObjectCache[Filename] = std::move(OE);
- }
- return *ObjectCache[Filename];
- }
- void BinaryHolder::clear() {
- std::lock_guard<std::mutex> ArchiveLock(ArchiveCacheMutex);
- std::lock_guard<std::mutex> ObjectLock(ObjectCacheMutex);
- ArchiveCache.clear();
- ObjectCache.clear();
- }
- } // namespace dsymutil
- } // namespace llvm
|