Files
swift-mirror/include/swift/Reflection/ReflectionContext.h
Saleem Abdulrasool df54b2af11 reflection: handle LMA != VMA for ELF
ELF is segment mapped, where the segment which contains a particular
section may be mapped to an address which does not correspond to the
address on disk.  Since the reflection dumper does not use the loader to
load the image into memory, we must manually account for any section
offsets.  Calculate this value when we query the mmap'ed image and wire
it through to the relative direct pointer accesses.

When switching to the linker table approach for the ELF metadata
introspection, this was uncovered as the segment containing the orphaned
sections was coalesced into a separate PT_LOAD header which had a non-0
offset for the mapping.
2017-11-29 17:56:15 -08:00

558 lines
19 KiB
C++

//===--- 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 "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>
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;
public:
using super::getBuilder;
using super::readIsaMask;
using super::readTypeFromMetadata;
using super::readGenericArgFromMetadata;
using super::readMetadataFromInstance;
using typename super::StoredPointer;
explicit ReflectionContext(std::shared_ptr<MemoryReader> reader)
: super(std::move(reader)) {}
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;
}
void dumpAllSections(std::ostream &OS) {
getBuilder().dumpAllSections();
}
void addReflectionInfo(ReflectionInfo I) {
getBuilder().addReflectionInfo(I);
}
/// 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.first) {
switch (kind.second) {
case MetadataKind::Class: {
// Figure out where the stored properties of this class begin
// by looking at the size of the superclass
bool valid;
unsigned start;
std::tie(valid, start) =
this->readInstanceStartAndAlignmentFromClassMetadata(MetadataAddress);
// Perform layout
if (valid)
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.first)
return nullptr;
auto kind = this->readKindFromMetadata(MetadataAddress.second);
if (!kind.first)
return nullptr;
switch (kind.second) {
case MetadataKind::Class:
return getMetadataTypeInfo(MetadataAddress.second);
case MetadataKind::HeapLocalVariable: {
auto CDAddr = this->readCaptureDescriptorFromMetadata(MetadataAddress.second);
if (!CDAddr.first)
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.second);
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.second)) {
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;
// Opaque existentials fall under two cases:
// If the value fits in three words, it starts at the beginning of the
// container. If it doesn't, the first word is a pointer to a heap box.
case RecordKind::OpaqueExistential: {
auto Fields = ExistentialRecordTI->getFields();
auto ExistentialMetadataField = std::find_if(Fields.begin(), Fields.end(),
[](const FieldInfo &FI) -> bool {
return FI.Name.compare("metadata") == 0;
});
if (ExistentialMetadataField == Fields.end())
return false;
// Get the metadata pointer for the contained instance type.
// This is equivalent to:
// auto PointerArray = reinterpret_cast<uintptr_t*>(ExistentialAddress);
// uintptr_t MetadataAddress = PointerArray[Offset];
auto MetadataAddressAddress
= RemoteAddress(ExistentialAddress.getAddressData() +
ExistentialMetadataField->Offset);
StoredPointer MetadataAddress = 0;
if (!getReader().readInteger(MetadataAddressAddress, &MetadataAddress))
return false;
auto InstanceTR = readTypeFromMetadata(MetadataAddress);
if (!InstanceTR)
return false;
*OutInstanceTR = InstanceTR;
auto InstanceTI = getTypeInfo(InstanceTR);
if (!InstanceTI)
return false;
if (InstanceTI->getSize() <= ExistentialMetadataField->Offset) {
// The value fits in the existential container, so it starts at the
// start of the container.
*OutInstanceAddress = ExistentialAddress;
} else {
// Otherwise it's in a box somewhere off in the heap. The first word
// of the container has the address to that box.
StoredPointer BoxAddress = 0;
if (!getReader().readInteger(ExistentialAddress, &BoxAddress))
return false;
// Address = BoxAddress + (sizeof(HeapObject) + alignMask) & ~alignMask)
auto Alignment = InstanceTI->getAlignment();
auto StartOfValue = BoxAddress + getSizeOfHeapObject();
// Align.
StartOfValue += Alignment - StartOfValue % Alignment;
*OutInstanceAddress = RemoteAddress(StartOfValue);
}
return true;
}
case RecordKind::ErrorExistential: {
// We have a pointer to an error existential, which is always heap object.
bool successfullyGotIsa = false;
StoredPointer MetadataAddress = 0;
std::tie(successfullyGotIsa, MetadataAddress)
= readMetadataFromInstance(ExistentialAddress.getAddressData());
if (!successfullyGotIsa)
return false;
bool isObjC = false;
// If we can determine the Objective-C class name, this is probably an
// error existential with NSError-compatible layout.
std::string ObjCClassName;
if (readObjCClassName(MetadataAddress, ObjCClassName)) {
if (ObjCClassName == "_SwiftNativeNSError")
isObjC = true;
} else {
// Otherwise, we can check to see if this is a class metadata with the
// kind value's least significant bit set, which indicates a pure
// Swift class.
auto Meta = readMetadata(MetadataAddress);
auto ClassMeta = dyn_cast<TargetClassMetadata<Runtime>>(Meta);
if (!ClassMeta)
return false;
isObjC = ClassMeta->isPureObjC();
}
// In addition to the isa pointer and two 32-bit reference counts, if the
// error existential is layout-compatible with NSError, we also need to
// skip over its three word-sized fields: the error code, the domain,
// and userInfo.
StoredPointer InstanceMetadataAddressAddress
= ExistentialAddress.getAddressData() +
(isObjC ? 5 : 2) * sizeof(StoredPointer);
// We need to get the instance's alignment info so we can get the exact
// offset of the start of its data in the class.
StoredPointer InstanceMetadataAddress = 0;
std::tie(successfullyGotIsa, InstanceMetadataAddress) =
readMetadataFromInstance(InstanceMetadataAddressAddress);
if (!successfullyGotIsa)
return false;
auto InstanceTR = readTypeFromMetadata(InstanceMetadataAddress);
if (!InstanceTR)
return false;
auto InstanceTI = getTypeInfo(InstanceTR);
if (!InstanceTI)
return false;
// Now we need to skip over the instance metadata pointer and instance's
// conformance pointer for Swift.Error.
StoredPointer InstanceAddress = InstanceMetadataAddressAddress +
2 * sizeof(StoredPointer);
// Round up to alignment, and we have the start address of the
// instance payload.
auto Alignment = InstanceTI->getAlignment();
InstanceAddress += Alignment - InstanceAddress % Alignment;
*OutInstanceTR = InstanceTR;
*OutInstanceAddress = RemoteAddress(InstanceAddress);
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.first)
return nullptr;
// Calculate the offset of the first capture.
// See GenHeap.cpp, buildPrivateMetadata().
auto OffsetToFirstCapture =
this->readOffsetToFirstCaptureFromMetadata(Metadata.second);
if (!OffsetToFirstCapture.first)
return nullptr;
// Initialize the builder.
Builder.addField(OffsetToFirstCapture.second, sizeof(StoredPointer),
/*numExtraInhabitants=*/0);
// Skip the closure's necessary bindings struct, if it's present.
auto SizeOfNecessaryBindings = Info.NumBindings * sizeof(StoredPointer);
Builder.addField(SizeOfNecessaryBindings, sizeof(StoredPointer),
/*numExtraInhabitants=*/0);
// 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.first)
return nullptr;
auto *SubstTR = readTypeFromMetadata(Metadata.second);
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.
std::pair<bool, 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 std::make_pair(true, 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 std::make_pair(true, CaptureAddress);
}
case MetadataSourceKind::GenericArgument: {
auto *GAMS = cast<GenericArgumentMetadataSource>(MS);
auto Base = readMetadataSource(Context, GAMS->getSource(), Builder);
if (!Base.first)
break;
unsigned Index = GAMS->getIndex();
auto Arg = readGenericArgFromMetadata(Base.second, Index);
if (!Arg.first)
break;
return Arg;
}
case MetadataSourceKind::Self:
case MetadataSourceKind::SelfWitnessTable:
break;
}
return std::make_pair(false, StoredPointer(0));
}
};
} // end namespace reflection
} // end namespace swift
#endif // SWIFT_REFLECTION_REFLECTIONCONTEXT_H