| //===--- ReflectionContext.h - Swift Type Reflection Context ----*- C++ -*-===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors |
| // Licensed under Apache License v2.0 with Runtime Library Exception |
| // |
| // See https://swift.org/LICENSE.txt for license information |
| // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Implements the context for reflection of values in the address space of a |
| // remote process. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef SWIFT_REFLECTION_REFLECTIONCONTEXT_H |
| #define SWIFT_REFLECTION_REFLECTIONCONTEXT_H |
| |
| #include "llvm/BinaryFormat/COFF.h" |
| #include "llvm/BinaryFormat/MachO.h" |
| #include "llvm/BinaryFormat/ELF.h" |
| #include "llvm/Object/COFF.h" |
| |
| #include "swift/Remote/MemoryReader.h" |
| #include "swift/Remote/MetadataReader.h" |
| #include "swift/Reflection/Records.h" |
| #include "swift/Reflection/TypeLowering.h" |
| #include "swift/Reflection/TypeRef.h" |
| #include "swift/Reflection/TypeRefBuilder.h" |
| #include "swift/Runtime/Unreachable.h" |
| |
| #include <iostream> |
| #include <set> |
| #include <vector> |
| #include <unordered_map> |
| #include <utility> |
| |
| namespace { |
| |
| template <unsigned PointerSize> struct MachOTraits; |
| |
| template <> struct MachOTraits<4> { |
| using Header = const struct llvm::MachO::mach_header; |
| using SegmentCmd = const struct llvm::MachO::segment_command; |
| using Section = const struct llvm::MachO::section; |
| static constexpr size_t MagicNumber = llvm::MachO::MH_MAGIC; |
| }; |
| |
| template <> struct MachOTraits<8> { |
| using Header = const struct llvm::MachO::mach_header_64; |
| using SegmentCmd = const struct llvm::MachO::segment_command_64; |
| using Section = const struct llvm::MachO::section_64; |
| static constexpr size_t MagicNumber = llvm::MachO::MH_MAGIC_64; |
| }; |
| |
| template <unsigned char ELFClass> struct ELFTraits; |
| |
| template <> struct ELFTraits<llvm::ELF::ELFCLASS32> { |
| using Header = const struct llvm::ELF::Elf32_Ehdr; |
| using Section = const struct llvm::ELF::Elf32_Shdr; |
| using Offset = llvm::ELF::Elf32_Off; |
| using Size = llvm::ELF::Elf32_Word; |
| static constexpr unsigned char ELFClass = llvm::ELF::ELFCLASS32; |
| }; |
| |
| template <> struct ELFTraits<llvm::ELF::ELFCLASS64> { |
| using Header = const struct llvm::ELF::Elf64_Ehdr; |
| using Section = const struct llvm::ELF::Elf64_Shdr; |
| using Offset = llvm::ELF::Elf64_Off; |
| using Size = llvm::ELF::Elf64_Xword; |
| static constexpr unsigned char ELFClass = llvm::ELF::ELFCLASS64; |
| }; |
| |
| } // namespace |
| |
| namespace swift { |
| namespace reflection { |
| |
| using swift::remote::MemoryReader; |
| using swift::remote::RemoteAddress; |
| |
| template <typename Runtime> |
| class ReflectionContext |
| : public remote::MetadataReader<Runtime, TypeRefBuilder> { |
| using super = remote::MetadataReader<Runtime, TypeRefBuilder>; |
| using super::readMetadata; |
| using super::readObjCClassName; |
| |
| std::unordered_map<typename super::StoredPointer, const TypeInfo *> Cache; |
| |
| /// All buffers we need to keep around long term. This will automatically free them |
| /// when this object is destroyed. |
| std::vector<MemoryReader::ReadBytesResult> savedBuffers; |
| std::vector<std::tuple<RemoteAddress, RemoteAddress>> imageRanges; |
| |
| public: |
| using super::getBuilder; |
| using super::readDemanglingForContextDescriptor; |
| using super::readGenericArgFromMetadata; |
| using super::readIsaMask; |
| using super::readMetadataAndValueErrorExistential; |
| using super::readMetadataAndValueOpaqueExistential; |
| using super::readMetadataFromInstance; |
| using super::readTypeFromMetadata; |
| using typename super::StoredPointer; |
| |
| explicit ReflectionContext(std::shared_ptr<MemoryReader> reader) |
| : super(std::move(reader)) { |
| getBuilder().setSymbolicReferenceResolverReader(*this); |
| } |
| |
| ReflectionContext(const ReflectionContext &other) = delete; |
| ReflectionContext &operator=(const ReflectionContext &other) = delete; |
| |
| MemoryReader &getReader() { |
| return *this->Reader; |
| } |
| |
| unsigned getSizeOfHeapObject() { |
| // This must match sizeof(HeapObject) for the target. |
| return sizeof(StoredPointer) * 2; |
| } |
| |
| #if defined(__APPLE__) && defined(__MACH__) |
| template <typename T> bool readMachOSections(RemoteAddress ImageStart) { |
| auto Buf = |
| this->getReader().readBytes(ImageStart, sizeof(typename T::Header)); |
| if (!Buf) |
| return false; |
| auto Header = reinterpret_cast<typename T::Header *>(Buf.get()); |
| assert(Header->magic == T::MagicNumber && "invalid MachO file"); |
| |
| auto NumCommands = Header->sizeofcmds; |
| |
| // The layout of the executable is such that the commands immediately follow |
| // the header. |
| auto CmdStartAddress = |
| RemoteAddress(ImageStart.getAddressData() + sizeof(typename T::Header)); |
| uint32_t SegmentCmdHdrSize = sizeof(typename T::SegmentCmd); |
| uint64_t Offset = 0; |
| |
| // Find the __TEXT segment. |
| typename T::SegmentCmd *Command = nullptr; |
| for (unsigned I = 0; I < NumCommands; ++I) { |
| auto CmdBuf = this->getReader().readBytes( |
| RemoteAddress(CmdStartAddress.getAddressData() + Offset), |
| SegmentCmdHdrSize); |
| auto CmdHdr = reinterpret_cast<typename T::SegmentCmd *>(CmdBuf.get()); |
| if (strncmp(CmdHdr->segname, "__TEXT", sizeof(CmdHdr->segname)) == 0) { |
| Command = CmdHdr; |
| savedBuffers.push_back(std::move(CmdBuf)); |
| break; |
| } |
| Offset += CmdHdr->cmdsize; |
| } |
| |
| // No __TEXT segment, bail out. |
| if (!Command) |
| return false; |
| |
| // Find the load command offset. |
| auto loadCmdOffset = ImageStart.getAddressData() + Offset + sizeof(typename T::Header); |
| |
| // Read the load command. |
| auto LoadCmdAddress = reinterpret_cast<const char *>(loadCmdOffset); |
| auto LoadCmdBuf = this->getReader().readBytes( |
| RemoteAddress(LoadCmdAddress), sizeof(typename T::SegmentCmd)); |
| auto LoadCmd = reinterpret_cast<typename T::SegmentCmd *>(LoadCmdBuf.get()); |
| |
| // The sections start immediately after the load command. |
| unsigned NumSect = LoadCmd->nsects; |
| auto SectAddress = reinterpret_cast<const char *>(loadCmdOffset) + |
| sizeof(typename T::SegmentCmd); |
| auto Sections = this->getReader().readBytes( |
| RemoteAddress(SectAddress), NumSect * sizeof(typename T::Section)); |
| |
| auto Slide = ImageStart.getAddressData() - Command->vmaddr; |
| std::string Prefix = "__swift5"; |
| uint64_t RangeStart = UINT64_MAX; |
| uint64_t RangeEnd = UINT64_MAX; |
| auto SectionsBuf = reinterpret_cast<const char *>(Sections.get()); |
| for (unsigned I = 0; I < NumSect; ++I) { |
| auto S = reinterpret_cast<typename T::Section *>( |
| SectionsBuf + (I * sizeof(typename T::Section))); |
| if (strncmp(S->sectname, Prefix.c_str(), strlen(Prefix.c_str())) != 0) |
| continue; |
| if (RangeStart == UINT64_MAX && RangeEnd == UINT64_MAX) { |
| RangeStart = S->addr + Slide; |
| RangeEnd = S->addr + S->size + Slide; |
| continue; |
| } |
| RangeStart = std::min(RangeStart, (uint64_t)S->addr + Slide); |
| RangeEnd = std::max(RangeEnd, (uint64_t)(S->addr + S->size + Slide)); |
| } |
| |
| if (RangeStart == UINT64_MAX && RangeEnd == UINT64_MAX) |
| return false; |
| |
| auto SectBuf = this->getReader().readBytes(RemoteAddress(RangeStart), |
| RangeEnd - RangeStart); |
| |
| auto findMachOSectionByName = [&](std::string Name) |
| -> std::pair<const char *, const char *> { |
| for (unsigned I = 0; I < NumSect; ++I) { |
| auto S = reinterpret_cast<typename T::Section *>( |
| SectionsBuf + (I * sizeof(typename T::Section))); |
| if (strncmp(S->sectname, Name.c_str(), strlen(Name.c_str())) != 0) |
| continue; |
| auto RemoteSecStart = S->addr + Slide; |
| auto SectBufData = reinterpret_cast<const char *>(SectBuf.get()); |
| auto LocalSectStart = |
| reinterpret_cast<const char *>(SectBufData + RemoteSecStart - RangeStart); |
| auto LocalSectEnd = reinterpret_cast<const char *>(LocalSectStart + S->size); |
| return {LocalSectStart, LocalSectEnd}; |
| } |
| return {nullptr, nullptr}; |
| }; |
| |
| auto FieldMdSec = findMachOSectionByName("__swift5_fieldmd"); |
| auto AssocTySec = findMachOSectionByName("__swift5_assocty"); |
| auto BuiltinTySec = findMachOSectionByName("__swift5_builtin"); |
| auto CaptureSec = findMachOSectionByName("__swift5_capture"); |
| auto TypeRefMdSec = findMachOSectionByName("__swift5_typeref"); |
| auto ReflStrMdSec = findMachOSectionByName("__swift5_reflstr"); |
| |
| if (FieldMdSec.first == nullptr && |
| AssocTySec.first == nullptr && |
| BuiltinTySec.first == nullptr && |
| CaptureSec.first == nullptr && |
| TypeRefMdSec.first == nullptr && |
| ReflStrMdSec.first == nullptr) |
| return false; |
| |
| auto LocalStartAddress = reinterpret_cast<uint64_t>(SectBuf.get()); |
| auto RemoteStartAddress = static_cast<uint64_t>(RangeStart); |
| |
| ReflectionInfo info = { |
| {{FieldMdSec.first, FieldMdSec.second}, 0}, |
| {{AssocTySec.first, AssocTySec.second}, 0}, |
| {{BuiltinTySec.first, BuiltinTySec.second}, 0}, |
| {{CaptureSec.first, CaptureSec.second}, 0}, |
| {{TypeRefMdSec.first, TypeRefMdSec.second}, 0}, |
| {{ReflStrMdSec.first, ReflStrMdSec.second}, 0}, |
| LocalStartAddress, |
| RemoteStartAddress}; |
| |
| this->addReflectionInfo(info); |
| |
| // Find the __DATA segment. |
| for (unsigned I = 0; I < NumCommands; ++I) { |
| auto CmdBuf = this->getReader().readBytes( |
| RemoteAddress(CmdStartAddress.getAddressData() + Offset), |
| SegmentCmdHdrSize); |
| auto CmdHdr = reinterpret_cast<typename T::SegmentCmd *>(CmdBuf.get()); |
| if (strncmp(CmdHdr->segname, "__DATA", sizeof(CmdHdr->segname)) == 0) { |
| auto DataSegmentEnd = |
| ImageStart.getAddressData() + CmdHdr->vmaddr + CmdHdr->vmsize; |
| assert(DataSegmentEnd > ImageStart.getAddressData() && |
| "invalid range for __DATA"); |
| imageRanges.push_back( |
| std::make_tuple(ImageStart, RemoteAddress(DataSegmentEnd))); |
| break; |
| } |
| Offset += CmdHdr->cmdsize; |
| } |
| |
| savedBuffers.push_back(std::move(Buf)); |
| savedBuffers.push_back(std::move(SectBuf)); |
| savedBuffers.push_back(std::move(Sections)); |
| return true; |
| } |
| |
| bool addImage(RemoteAddress ImageStart) { |
| // We start reading 4 bytes. The first 4 bytes are supposed to be |
| // the magic, so we understand whether this is a 32-bit executable or |
| // a 64-bit one. |
| auto Buf = this->getReader().readBytes(ImageStart, sizeof(uint32_t)); |
| if (!Buf) |
| return false; |
| auto HeaderMagic = reinterpret_cast<const uint32_t *>(Buf.get()); |
| if (*HeaderMagic == llvm::MachO::MH_MAGIC) |
| return readMachOSections<MachOTraits<4>>(ImageStart); |
| if (*HeaderMagic == llvm::MachO::MH_MAGIC_64) |
| return readMachOSections<MachOTraits<8>>(ImageStart); |
| return false; |
| } |
| |
| #elif defined(_WIN32) |
| bool readPECOFFSections(RemoteAddress ImageStart) { |
| auto DOSHdrBuf = this->getReader().readBytes( |
| ImageStart, sizeof(llvm::object::dos_header)); |
| auto DOSHdr = |
| reinterpret_cast<const llvm::object::dos_header *>(DOSHdrBuf.get()); |
| auto COFFFileHdrAddr = ImageStart.getAddressData() + |
| DOSHdr->AddressOfNewExeHeader + |
| sizeof(llvm::COFF::PEMagic); |
| |
| auto COFFFileHdrBuf = this->getReader().readBytes( |
| RemoteAddress(COFFFileHdrAddr), sizeof(llvm::object::coff_file_header)); |
| auto COFFFileHdr = reinterpret_cast<const llvm::object::coff_file_header *>( |
| COFFFileHdrBuf.get()); |
| |
| auto SectionTableAddr = COFFFileHdrAddr + |
| sizeof(llvm::object::coff_file_header) + |
| COFFFileHdr->SizeOfOptionalHeader; |
| auto SectionTableBuf = this->getReader().readBytes( |
| RemoteAddress(SectionTableAddr), |
| sizeof(llvm::object::coff_section) * COFFFileHdr->NumberOfSections); |
| |
| auto findCOFFSectionByName = [&](llvm::StringRef Name) |
| -> std::pair<const char *, const char *> { |
| for (size_t i = 0; i < COFFFileHdr->NumberOfSections; ++i) { |
| const llvm::object::coff_section *COFFSec = |
| reinterpret_cast<const llvm::object::coff_section *>( |
| SectionTableBuf.get()) + |
| i; |
| llvm::StringRef SectionName = |
| (COFFSec->Name[llvm::COFF::NameSize - 1] == 0) |
| ? COFFSec->Name |
| : llvm::StringRef(COFFSec->Name, llvm::COFF::NameSize); |
| if (SectionName != Name) |
| continue; |
| auto Addr = ImageStart.getAddressData() + COFFSec->VirtualAddress; |
| auto Buf = this->getReader().readBytes(RemoteAddress(Addr), |
| COFFSec->VirtualSize); |
| const char *Begin = reinterpret_cast<const char *>(Buf.get()); |
| const char *End = Begin + COFFSec->VirtualSize; |
| savedBuffers.push_back(std::move(Buf)); |
| |
| // FIXME: This code needs to be cleaned up and updated |
| // to make it work for 32 bit platforms. |
| if (SectionName != ".sw5cptr" && SectionName != ".sw5bltn") { |
| Begin += 8; |
| End -= 8; |
| } |
| |
| return {Begin, End}; |
| } |
| return {nullptr, nullptr}; |
| }; |
| |
| std::pair<const char *, const char *> CaptureSec = |
| findCOFFSectionByName(".sw5cptr"); |
| std::pair<const char *, const char *> TypeRefMdSec = |
| findCOFFSectionByName(".sw5tyrf"); |
| std::pair<const char *, const char *> FieldMdSec = |
| findCOFFSectionByName(".sw5flmd"); |
| std::pair<const char *, const char *> AssocTySec = |
| findCOFFSectionByName(".sw5asty"); |
| std::pair<const char *, const char *> BuiltinTySec = |
| findCOFFSectionByName(".sw5bltn"); |
| std::pair<const char *, const char *> ReflStrMdSec = |
| findCOFFSectionByName(".sw5rfst"); |
| |
| if (FieldMdSec.first == nullptr && |
| AssocTySec.first == nullptr && |
| BuiltinTySec.first == nullptr && |
| CaptureSec.first == nullptr && |
| TypeRefMdSec.first == nullptr && |
| ReflStrMdSec.first == nullptr) |
| return false; |
| |
| auto LocalStartAddress = reinterpret_cast<uintptr_t>(DOSHdrBuf.get()); |
| auto RemoteStartAddress = |
| static_cast<uintptr_t>(ImageStart.getAddressData()); |
| |
| ReflectionInfo Info = { |
| {{FieldMdSec.first, FieldMdSec.second}, 0}, |
| {{AssocTySec.first, AssocTySec.second}, 0}, |
| {{BuiltinTySec.first, BuiltinTySec.second}, 0}, |
| {{CaptureSec.first, CaptureSec.second}, 0}, |
| {{TypeRefMdSec.first, TypeRefMdSec.second}, 0}, |
| {{ReflStrMdSec.first, ReflStrMdSec.second}, 0}, |
| LocalStartAddress, |
| RemoteStartAddress}; |
| this->addReflectionInfo(Info); |
| return true; |
| } |
| |
| bool addImage(RemoteAddress ImageStart) { |
| auto Buf = this->getReader().readBytes(ImageStart, |
| sizeof(llvm::object::dos_header)); |
| if (!Buf) |
| return false; |
| |
| auto DOSHdr = reinterpret_cast<const llvm::object::dos_header *>(Buf.get()); |
| if (!(DOSHdr->Magic[0] == 'M' && DOSHdr->Magic[1] == 'Z')) |
| return false; |
| |
| auto PEHeaderAddress = |
| ImageStart.getAddressData() + DOSHdr->AddressOfNewExeHeader; |
| |
| Buf = this->getReader().readBytes(RemoteAddress(PEHeaderAddress), |
| sizeof(llvm::COFF::PEMagic)); |
| if (!Buf) |
| return false; |
| |
| if (memcmp(Buf.get(), llvm::COFF::PEMagic, sizeof(llvm::COFF::PEMagic))) |
| return false; |
| |
| return readPECOFFSections(ImageStart); |
| } |
| #else // ELF platforms. |
| template <typename T> bool readELFSections(RemoteAddress ImageStart) { |
| auto Buf = |
| this->getReader().readBytes(ImageStart, sizeof(typename T::Header)); |
| |
| auto Hdr = reinterpret_cast<const typename T::Header *>(Buf.get()); |
| assert(Hdr->getFileClass() == T::ELFClass && "invalid ELF file class"); |
| |
| // From the header, grab informations about the section header table. |
| auto SectionHdrAddress = ImageStart.getAddressData() + Hdr->e_shoff; |
| auto SectionHdrNumEntries = Hdr->e_shnum; |
| auto SectionEntrySize = Hdr->e_shentsize; |
| |
| // Collect all the section headers, we need them to look up the |
| // reflection sections (by name) and the string table. |
| std::vector<const typename T::Section *> SecHdrVec; |
| for (unsigned I = 0; I < SectionHdrNumEntries; ++I) { |
| auto SecBuf = this->getReader().readBytes( |
| RemoteAddress(SectionHdrAddress + (I * SectionEntrySize)), |
| SectionEntrySize); |
| auto SecHdr = |
| reinterpret_cast<const typename T::Section *>(SecBuf.get()); |
| SecHdrVec.push_back(SecHdr); |
| } |
| |
| // This provides quick access to the section header string table index. |
| // We also here handle the unlikely even where the section index overflows |
| // and it's just a pointer to secondary storage (SHN_XINDEX). |
| uint32_t SecIdx = Hdr->e_shstrndx; |
| if (SecIdx == llvm::ELF::SHN_XINDEX) { |
| assert(!SecHdrVec.empty() && "malformed ELF object"); |
| SecIdx = SecHdrVec[0]->sh_link; |
| } |
| |
| assert(SecIdx < SecHdrVec.size() && "malformed ELF object"); |
| |
| const typename T::Section *SecHdrStrTab = SecHdrVec[SecIdx]; |
| typename T::Offset StrTabOffset = SecHdrStrTab->sh_offset; |
| typename T::Size StrTabSize = SecHdrStrTab->sh_size; |
| |
| auto StrTabStart = |
| RemoteAddress(ImageStart.getAddressData() + StrTabOffset); |
| auto StrTabBuf = this->getReader().readBytes(StrTabStart, StrTabSize); |
| auto StrTab = reinterpret_cast<const char *>(StrTabBuf.get()); |
| |
| auto findELFSectionByName = [&](std::string Name) |
| -> std::pair<const char *, const char *> { |
| // Now for all the sections, find their name. |
| for (const typename T::Section *Hdr : SecHdrVec) { |
| uint32_t Offset = Hdr->sh_name; |
| auto SecName = std::string(StrTab + Offset); |
| if (SecName != Name) |
| continue; |
| auto SecStart = |
| RemoteAddress(ImageStart.getAddressData() + Hdr->sh_addr); |
| auto SecSize = Hdr->sh_size; |
| auto SecBuf = this->getReader().readBytes(SecStart, SecSize); |
| auto SecContents = reinterpret_cast<const char *>(SecBuf.get()); |
| return {SecContents, SecContents + SecSize}; |
| } |
| return {nullptr, nullptr}; |
| }; |
| |
| auto FieldMdSec = findELFSectionByName("swift5_fieldmd"); |
| auto AssocTySec = findELFSectionByName("swift5_assocty"); |
| auto BuiltinTySec = findELFSectionByName("swift5_builtin"); |
| auto CaptureSec = findELFSectionByName("swift5_capture"); |
| auto TypeRefMdSec = findELFSectionByName("swift5_typeref"); |
| auto ReflStrMdSec = findELFSectionByName("swift5_reflstr"); |
| |
| // We succeed if at least one of the sections is present in the |
| // ELF executable. |
| if (FieldMdSec.first == nullptr && |
| AssocTySec.first == nullptr && |
| BuiltinTySec.first == nullptr && |
| CaptureSec.first == nullptr && |
| TypeRefMdSec.first == nullptr && |
| ReflStrMdSec.first == nullptr) |
| return false; |
| |
| auto LocalStartAddress = reinterpret_cast<uint64_t>(Buf.get()); |
| auto RemoteStartAddress = |
| static_cast<uint64_t>(ImageStart.getAddressData()); |
| |
| ReflectionInfo info = { |
| {{FieldMdSec.first, FieldMdSec.second}, 0}, |
| {{AssocTySec.first, AssocTySec.second}, 0}, |
| {{BuiltinTySec.first, BuiltinTySec.second}, 0}, |
| {{CaptureSec.first, CaptureSec.second}, 0}, |
| {{TypeRefMdSec.first, TypeRefMdSec.second}, 0}, |
| {{ReflStrMdSec.first, ReflStrMdSec.second}, 0}, |
| LocalStartAddress, |
| RemoteStartAddress}; |
| |
| this->addReflectionInfo(info); |
| |
| savedBuffers.push_back(std::move(Buf)); |
| return true; |
| } |
| |
| bool addImage(RemoteAddress ImageStart) { |
| auto Buf = |
| this->getReader().readBytes(ImageStart, sizeof(llvm::ELF::Elf64_Ehdr)); |
| |
| // Read the header. |
| auto Hdr = reinterpret_cast<const llvm::ELF::Elf64_Ehdr *>(Buf.get()); |
| |
| if (!Hdr->checkMagic()) |
| return false; |
| |
| // Check if we have a ELFCLASS32 or ELFCLASS64 |
| unsigned char FileClass = Hdr->getFileClass(); |
| if (FileClass == llvm::ELF::ELFCLASS64) { |
| return readELFSections<ELFTraits<llvm::ELF::ELFCLASS64>>(ImageStart); |
| } else if (FileClass == llvm::ELF::ELFCLASS32) { |
| return readELFSections<ELFTraits<llvm::ELF::ELFCLASS32>>(ImageStart); |
| } else { |
| return false; |
| } |
| } |
| #endif |
| |
| void addReflectionInfo(ReflectionInfo I) { |
| getBuilder().addReflectionInfo(I); |
| } |
| |
| bool ownsObject(RemoteAddress ObjectAddress) { |
| auto MetadataAddress = readMetadataFromInstance(ObjectAddress.getAddressData()); |
| if (!MetadataAddress) |
| return true; |
| return ownsAddress(RemoteAddress(*MetadataAddress)); |
| } |
| |
| /// Returns true if the address falls within a registered image. |
| bool ownsAddress(RemoteAddress Address) { |
| for (auto Range : imageRanges) { |
| auto Start = std::get<0>(Range); |
| auto End = std::get<1>(Range); |
| if (Start.getAddressData() <= Address.getAddressData() |
| && Address.getAddressData() < End.getAddressData()) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /// Return a description of the layout of a class instance with the given |
| /// metadata as its isa pointer. |
| const TypeInfo *getMetadataTypeInfo(StoredPointer MetadataAddress) { |
| // See if we cached the layout already |
| auto found = Cache.find(MetadataAddress); |
| if (found != Cache.end()) |
| return found->second; |
| |
| auto &TC = getBuilder().getTypeConverter(); |
| |
| const TypeInfo *TI = nullptr; |
| |
| auto TR = readTypeFromMetadata(MetadataAddress); |
| auto kind = this->readKindFromMetadata(MetadataAddress); |
| if (TR != nullptr && kind) { |
| switch (*kind) { |
| case MetadataKind::Class: { |
| // Figure out where the stored properties of this class begin |
| // by looking at the size of the superclass |
| auto start = |
| this->readInstanceStartAndAlignmentFromClassMetadata(MetadataAddress); |
| |
| // Perform layout |
| if (start) |
| TI = TC.getClassInstanceTypeInfo(TR, *start); |
| |
| break; |
| } |
| default: |
| break; |
| } |
| } |
| |
| // Cache the result for future lookups |
| Cache[MetadataAddress] = TI; |
| return TI; |
| } |
| |
| /// Return a description of the layout of a class instance with the given |
| /// metadata as its isa pointer. |
| const TypeInfo *getInstanceTypeInfo(StoredPointer ObjectAddress) { |
| auto MetadataAddress = readMetadataFromInstance(ObjectAddress); |
| if (!MetadataAddress) |
| return nullptr; |
| |
| auto kind = this->readKindFromMetadata(*MetadataAddress); |
| if (!kind) |
| return nullptr; |
| |
| switch (*kind) { |
| case MetadataKind::Class: |
| return getMetadataTypeInfo(*MetadataAddress); |
| |
| case MetadataKind::HeapLocalVariable: { |
| auto CDAddr = this->readCaptureDescriptorFromMetadata(*MetadataAddress); |
| if (!CDAddr) |
| return nullptr; |
| |
| // FIXME: Non-generic SIL boxes also use the HeapLocalVariable metadata |
| // kind, but with a null capture descriptor right now (see |
| // FixedBoxTypeInfoBase::allocate). |
| // |
| // Non-generic SIL boxes share metadata among types with compatible |
| // layout, but we need some way to get an outgoing pointer map for them. |
| auto *CD = getBuilder().getCaptureDescriptor(*CDAddr); |
| if (CD == nullptr) |
| return nullptr; |
| |
| auto Info = getBuilder().getClosureContextInfo(*CD, 0); |
| |
| return getClosureContextInfo(ObjectAddress, Info); |
| } |
| |
| case MetadataKind::HeapGenericLocalVariable: { |
| // Generic SIL @box type - there is always an instantiated metadata |
| // pointer for the boxed type. |
| if (auto Meta = readMetadata(*MetadataAddress)) { |
| auto GenericHeapMeta = |
| cast<TargetGenericBoxHeapMetadata<Runtime>>(Meta.getLocalBuffer()); |
| return getMetadataTypeInfo(GenericHeapMeta->BoxedType); |
| } |
| return nullptr; |
| } |
| |
| case MetadataKind::ErrorObject: |
| // Error boxed existential on non-Objective-C runtime target |
| return nullptr; |
| |
| default: |
| return nullptr; |
| } |
| } |
| |
| bool |
| projectExistential(RemoteAddress ExistentialAddress, |
| const TypeRef *ExistentialTR, |
| const TypeRef **OutInstanceTR, |
| RemoteAddress *OutInstanceAddress) { |
| if (ExistentialTR == nullptr) |
| return false; |
| |
| auto ExistentialTI = getTypeInfo(ExistentialTR); |
| if (ExistentialTI == nullptr) |
| return false; |
| |
| auto ExistentialRecordTI = dyn_cast<const RecordTypeInfo>(ExistentialTI); |
| if (ExistentialRecordTI == nullptr) |
| return false; |
| |
| switch (ExistentialRecordTI->getRecordKind()) { |
| // Class existentials have trivial layout. |
| // It is itself the pointer to the instance followed by the witness tables. |
| case RecordKind::ClassExistential: |
| // This is just Builtin.UnknownObject |
| *OutInstanceTR = ExistentialRecordTI->getFields()[0].TR; |
| *OutInstanceAddress = ExistentialAddress; |
| return true; |
| |
| case RecordKind::OpaqueExistential: { |
| auto OptMetaAndValue = |
| readMetadataAndValueOpaqueExistential(ExistentialAddress); |
| if (!OptMetaAndValue) |
| return false; |
| |
| auto InstanceTR = readTypeFromMetadata( |
| OptMetaAndValue->MetadataAddress.getAddressData()); |
| if (!InstanceTR) |
| return false; |
| |
| *OutInstanceTR = InstanceTR; |
| *OutInstanceAddress = OptMetaAndValue->PayloadAddress; |
| return true; |
| } |
| case RecordKind::ErrorExistential: { |
| auto OptMetaAndValue = |
| readMetadataAndValueErrorExistential(ExistentialAddress); |
| if (!OptMetaAndValue) |
| return false; |
| |
| // FIXME: Check third value, 'IsBridgedError' |
| |
| auto InstanceTR = readTypeFromMetadata( |
| OptMetaAndValue->MetadataAddress.getAddressData()); |
| if (!InstanceTR) |
| return false; |
| |
| *OutInstanceTR = InstanceTR; |
| *OutInstanceAddress = OptMetaAndValue->PayloadAddress; |
| return true; |
| } |
| default: |
| return false; |
| } |
| } |
| |
| /// Return a description of the layout of a value with the given type. |
| const TypeInfo *getTypeInfo(const TypeRef *TR) { |
| return getBuilder().getTypeConverter().getTypeInfo(TR); |
| } |
| |
| private: |
| const TypeInfo *getClosureContextInfo(StoredPointer Context, |
| const ClosureContextInfo &Info) { |
| RecordTypeInfoBuilder Builder(getBuilder().getTypeConverter(), |
| RecordKind::ClosureContext); |
| |
| auto Metadata = readMetadataFromInstance(Context); |
| if (!Metadata) |
| return nullptr; |
| |
| // Calculate the offset of the first capture. |
| // See GenHeap.cpp, buildPrivateMetadata(). |
| auto OffsetToFirstCapture = |
| this->readOffsetToFirstCaptureFromMetadata(*Metadata); |
| if (!OffsetToFirstCapture) |
| return nullptr; |
| |
| // Initialize the builder. |
| Builder.addField(*OffsetToFirstCapture, |
| /*alignment=*/sizeof(StoredPointer), |
| /*numExtraInhabitants=*/0, |
| /*bitwiseTakable=*/true); |
| |
| // Skip the closure's necessary bindings struct, if it's present. |
| auto SizeOfNecessaryBindings = Info.NumBindings * sizeof(StoredPointer); |
| Builder.addField(/*size=*/SizeOfNecessaryBindings, |
| /*alignment=*/sizeof(StoredPointer), |
| /*numExtraInhabitants=*/0, |
| /*bitwiseTakable=*/true); |
| |
| // FIXME: should be unordered_set but I'm too lazy to write a hash |
| // functor |
| std::set<std::pair<const TypeRef *, const MetadataSource *>> Done; |
| GenericArgumentMap Subs; |
| |
| ArrayRef<const TypeRef *> CaptureTypes = Info.CaptureTypes; |
| |
| // Closure context element layout depends on the layout of the |
| // captured types, but captured types might depend on element |
| // layout (of previous elements). Use an iterative approach to |
| // solve the problem. |
| while (!CaptureTypes.empty()) { |
| const TypeRef *OrigCaptureTR = CaptureTypes[0]; |
| |
| // If we failed to demangle the capture type, we cannot proceed. |
| if (OrigCaptureTR == nullptr) |
| return nullptr; |
| |
| const TypeRef *SubstCaptureTR = nullptr; |
| |
| // If we have enough substitutions to make this captured value's |
| // type concrete, or we know it's size anyway (because it is a |
| // class reference or metatype, for example), go ahead and add |
| // it to the layout. |
| if (OrigCaptureTR->isConcreteAfterSubstitutions(Subs)) |
| SubstCaptureTR = OrigCaptureTR->subst(getBuilder(), Subs); |
| else if (getBuilder().getTypeConverter().hasFixedSize(OrigCaptureTR)) |
| SubstCaptureTR = OrigCaptureTR; |
| |
| if (SubstCaptureTR != nullptr) { |
| Builder.addField("", SubstCaptureTR); |
| if (Builder.isInvalid()) |
| return nullptr; |
| |
| // Try the next capture type. |
| CaptureTypes = CaptureTypes.slice(1); |
| continue; |
| } |
| |
| // Ok, we do not have enough substitutions yet. Perhaps we have |
| // enough elements figured out that we can pick off some |
| // metadata sources though, and use those to derive some new |
| // substitutions. |
| bool Progress = false; |
| for (auto Source : Info.MetadataSources) { |
| // Don't read a source more than once. |
| if (Done.count(Source)) |
| continue; |
| |
| // If we don't have enough information to read this source |
| // (because it is fulfilled by metadata from a capture at |
| // at unknown offset), keep going. |
| if (!isMetadataSourceReady(Source.second, Builder)) |
| continue; |
| |
| auto Metadata = readMetadataSource(Context, Source.second, Builder); |
| if (!Metadata) |
| return nullptr; |
| |
| auto *SubstTR = readTypeFromMetadata(*Metadata); |
| if (SubstTR == nullptr) |
| return nullptr; |
| |
| if (!TypeRef::deriveSubstitutions(Subs, Source.first, SubstTR)) |
| return nullptr; |
| |
| Done.insert(Source); |
| Progress = true; |
| } |
| |
| // If we failed to make any forward progress above, we're stuck |
| // and cannot close out this layout. |
| if (!Progress) |
| return nullptr; |
| } |
| |
| // Ok, we have a complete picture now. |
| return Builder.build(); |
| } |
| |
| /// Checks if we have enough information to read the given metadata |
| /// source. |
| /// |
| /// \param Builder Used to obtain offsets of elements known so far. |
| bool isMetadataSourceReady(const MetadataSource *MS, |
| const RecordTypeInfoBuilder &Builder) { |
| switch (MS->getKind()) { |
| case MetadataSourceKind::ClosureBinding: |
| return true; |
| case MetadataSourceKind::ReferenceCapture: { |
| unsigned Index = cast<ReferenceCaptureMetadataSource>(MS)->getIndex(); |
| return Index < Builder.getNumFields(); |
| } |
| case MetadataSourceKind::MetadataCapture: { |
| unsigned Index = cast<MetadataCaptureMetadataSource>(MS)->getIndex(); |
| return Index < Builder.getNumFields(); |
| } |
| case MetadataSourceKind::GenericArgument: { |
| auto Base = cast<GenericArgumentMetadataSource>(MS)->getSource(); |
| return isMetadataSourceReady(Base, Builder); |
| } |
| case MetadataSourceKind::Self: |
| case MetadataSourceKind::SelfWitnessTable: |
| return true; |
| } |
| |
| swift_runtime_unreachable("Unhandled MetadataSourceKind in switch."); |
| } |
| |
| /// Read metadata for a captured generic type from a closure context. |
| /// |
| /// \param Context The closure context in the remote process. |
| /// |
| /// \param MS The metadata source, which must be "ready" as per the |
| /// above. |
| /// |
| /// \param Builder Used to obtain offsets of elements known so far. |
| llvm::Optional<StoredPointer> |
| readMetadataSource(StoredPointer Context, |
| const MetadataSource *MS, |
| const RecordTypeInfoBuilder &Builder) { |
| switch (MS->getKind()) { |
| case MetadataSourceKind::ClosureBinding: { |
| unsigned Index = cast<ClosureBindingMetadataSource>(MS)->getIndex(); |
| |
| // Skip the context's HeapObject header |
| // (one word each for isa pointer and reference counts). |
| // |
| // Metadata and conformance tables are stored consecutively after |
| // the heap object header, in the 'necessary bindings' area. |
| // |
| // We should only have the index of a type metadata record here. |
| unsigned Offset = getSizeOfHeapObject() + |
| sizeof(StoredPointer) * Index; |
| |
| StoredPointer MetadataAddress; |
| if (!getReader().readInteger(RemoteAddress(Context + Offset), |
| &MetadataAddress)) |
| break; |
| |
| return MetadataAddress; |
| } |
| case MetadataSourceKind::ReferenceCapture: { |
| unsigned Index = cast<ReferenceCaptureMetadataSource>(MS)->getIndex(); |
| |
| // We should already have enough type information to know the offset |
| // of this capture in the context. |
| unsigned CaptureOffset = Builder.getFieldOffset(Index); |
| |
| StoredPointer CaptureAddress; |
| if (!getReader().readInteger(RemoteAddress(Context + CaptureOffset), |
| &CaptureAddress)) |
| break; |
| |
| // Read the requested capture's isa pointer. |
| return readMetadataFromInstance(CaptureAddress); |
| } |
| case MetadataSourceKind::MetadataCapture: { |
| unsigned Index = cast<MetadataCaptureMetadataSource>(MS)->getIndex(); |
| |
| // We should already have enough type information to know the offset |
| // of this capture in the context. |
| unsigned CaptureOffset = Builder.getFieldOffset(Index); |
| |
| StoredPointer CaptureAddress; |
| if (!getReader().readInteger(RemoteAddress(Context + CaptureOffset), |
| &CaptureAddress)) |
| break; |
| |
| return CaptureAddress; |
| } |
| case MetadataSourceKind::GenericArgument: { |
| auto *GAMS = cast<GenericArgumentMetadataSource>(MS); |
| auto Base = readMetadataSource(Context, GAMS->getSource(), Builder); |
| if (!Base) |
| break; |
| |
| unsigned Index = GAMS->getIndex(); |
| auto Arg = readGenericArgFromMetadata(*Base, Index); |
| if (!Arg) |
| break; |
| |
| return *Arg; |
| } |
| case MetadataSourceKind::Self: |
| case MetadataSourceKind::SelfWitnessTable: |
| break; |
| } |
| |
| return llvm::None; |
| } |
| }; |
| |
| } // end namespace reflection |
| } // end namespace swift |
| |
| #endif // SWIFT_REFLECTION_REFLECTIONCONTEXT_H |