MetadataReader: Add an API for reading absolute pointers.

Pointer data in some remote reflection targets may required relocation, or may not be
fully resolvable, such as when we're dumping info from a single image on disk that
references other dynamic libraries. Add a `RemoteAbsolutePointer` type that can hold a
symbol, offset, or combination of both, and add APIs to `MemoryReader` and `MetadataReader`
for reading pointers that can get unresolved relocation info from an image, or apply
relocations to pointer information. MetadataReader can use the symbol name information to
fill in demanglings of symbolic-reference-bearing mangled names by using the information
from the symbol name to fill in the name even though the context descriptors are not
available.

For now, this is NFC (MemoryReader::resolvePointer just forwards the pointer data), but
lays the groundwork for implementation of relocation in ObjectMemoryReader.
This commit is contained in:
Joe Groff
2019-09-28 16:53:34 -07:00
parent 11b2c29125
commit 4012a207c8
6 changed files with 305 additions and 95 deletions

View File

@@ -433,27 +433,27 @@ struct MembersHelper<> {
LLVM_ATTRIBUTE_ALWAYS_INLINE
static void copyConstruct(void *self, int index, const void *other) {
llvm_unreachable("bad index");
assert(false && "bad index");
}
LLVM_ATTRIBUTE_ALWAYS_INLINE
static void moveConstruct(void *self, int index, void *other) {
llvm_unreachable("bad index");
assert(false && "bad index");
}
LLVM_ATTRIBUTE_ALWAYS_INLINE
static void copyAssignSame(int index, void *self, const void *other) {
llvm_unreachable("bad index");
assert(false && "bad index");
}
LLVM_ATTRIBUTE_ALWAYS_INLINE
static void moveAssignSame(int index, void *self, void *other) {
llvm_unreachable("bad index");
assert(false && "bad index");
}
LLVM_ATTRIBUTE_ALWAYS_INLINE
static void destruct(int index, void *self) {
llvm_unreachable("bad index");
assert(false && "bad index");
}
};

View File

@@ -608,6 +608,8 @@ public:
auto CDAddr = this->readCaptureDescriptorFromMetadata(*MetadataAddress);
if (!CDAddr)
return nullptr;
if (!CDAddr->isResolved())
return nullptr;
// FIXME: Non-generic SIL boxes also use the HeapLocalVariable metadata
// kind, but with a null capture descriptor right now (see
@@ -615,7 +617,8 @@ public:
//
// 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);
auto CD = getBuilder().getCaptureDescriptor(
CDAddr->getResolvedAddress().getAddressData());
if (CD == nullptr)
return nullptr;

View File

@@ -69,7 +69,7 @@ public:
/// NOTE: subclasses MUST override at least one of the readBytes functions. The default
/// implementation calls through to the other one.
virtual ReadBytesResult
readBytes(RemoteAddress address, uint64_t size) {
readBytes(RemoteAddress address, uint64_t size) {
auto *Buf = malloc(size);
ReadBytesResult Result(Buf, [](const void *ptr) {
free(const_cast<void *>(ptr));
@@ -96,6 +96,34 @@ public:
memcpy(dest, Ptr.get(), size);
return true;
}
/// Attempts to resolve a pointer value read from the given remote address.
virtual RemoteAbsolutePointer resolvePointer(RemoteAddress address,
uint64_t readValue) {
// Default implementation returns the read value as is.
return RemoteAbsolutePointer("", readValue);
}
/// Attempt to read and resolve a pointer value at the given remote address.
llvm::Optional<RemoteAbsolutePointer> readPointer(RemoteAddress address,
unsigned pointerSize) {
auto result = readBytes(address, pointerSize);
if (!result)
return llvm::None;
uint64_t pointerData;
if (pointerSize == 4) {
uint32_t theData;
memcpy(&theData, result.get(), 4);
pointerData = theData;
} else if (pointerSize == 8) {
memcpy(&pointerData, result.get(), 8);
} else {
return llvm::None;
}
return resolvePointer(address, pointerData);
}
virtual ~MemoryReader() = default;
};

View File

@@ -22,6 +22,7 @@
#include "swift/Demangling/Demangler.h"
#include "swift/Demangling/TypeDecoder.h"
#include "swift/Basic/Defer.h"
#include "swift/Basic/ExternalUnion.h"
#include "swift/Basic/Range.h"
#include "swift/Basic/LLVM.h"
#include "swift/ABI/TypeIdentity.h"
@@ -181,6 +182,75 @@ private:
std::unique_ptr<const TargetContextDescriptor<Runtime>,
delete_with_free>;
/// A reference to a context descriptor that may be in an unloaded image.
class ParentContextDescriptorRef {
bool IsResolved;
using Payloads = ExternalUnionMembers<std::string, ContextDescriptorRef>;
static typename Payloads::Index getPayloadIndex(bool IsResolved) {
return IsResolved ? Payloads::template indexOf<ContextDescriptorRef>()
: Payloads::template indexOf<std::string>();
}
ExternalUnion<bool, Payloads, getPayloadIndex> Payload;
public:
explicit ParentContextDescriptorRef(StringRef Symbol)
: IsResolved(false)
{
Payload.template emplace<std::string>(IsResolved, Symbol);
}
explicit ParentContextDescriptorRef(ContextDescriptorRef Resolved)
: IsResolved(true)
{
Payload.template emplace<ContextDescriptorRef>(IsResolved, Resolved);
}
ParentContextDescriptorRef()
: ParentContextDescriptorRef(ContextDescriptorRef())
{}
ParentContextDescriptorRef(const ParentContextDescriptorRef &o)
: IsResolved(o.IsResolved)
{
Payload.copyConstruct(IsResolved, o.Payload);
}
ParentContextDescriptorRef(ParentContextDescriptorRef &&o)
: IsResolved(o.IsResolved)
{
Payload.moveConstruct(IsResolved, std::move(o.Payload));
}
~ParentContextDescriptorRef() {
Payload.destruct(IsResolved);
}
ParentContextDescriptorRef &operator=(const ParentContextDescriptorRef &o) {
Payload.copyAssign(IsResolved, o.IsResolved, o.Payload);
IsResolved = o.isResolved();
return *this;
}
ParentContextDescriptorRef &operator=(ParentContextDescriptorRef &&o) {
Payload.moveAssign(IsResolved, o.IsResolved, std::move(o.Payload));
IsResolved = o.isResolved();
return *this;
}
bool isResolved() const { return IsResolved; }
StringRef getSymbol() const {
return Payload.template get<std::string>(IsResolved);
}
ContextDescriptorRef getResolved() const {
return Payload.template get<ContextDescriptorRef>(IsResolved);
}
explicit operator bool() const {
return !isResolved() || getResolved();
}
};
/// A cache of read nominal type descriptors, keyed by the address of the
/// nominal type descriptor.
std::unordered_map<StoredPointer, OwnedContextDescriptorRef>
@@ -310,30 +380,34 @@ public:
auto remoteAddress =
mangledName.getAddressData() + offsetInMangledName + offset;
RemoteAbsolutePointer resolved;
if (directness == Directness::Indirect) {
if (auto indirectAddress = readPointerValue(remoteAddress)) {
remoteAddress = *indirectAddress;
if (auto indirectAddress = readPointer(remoteAddress)) {
resolved = *indirectAddress;
} else {
return nullptr;
}
} else {
resolved = RemoteAbsolutePointer("", remoteAddress);
}
switch (kind) {
case Demangle::SymbolicReferenceKind::Context: {
auto context = readContextDescriptor(remoteAddress);
if (!context)
return nullptr;
// Try to preserve a reference to an OpaqueTypeDescriptor
// symbolically, since we'd like to read out and resolve the type ref
// to the underlying type if available.
if (useOpaqueTypeSymbolicReferences
&& context->getKind() == ContextDescriptorKind::OpaqueType) {
return dem.createNode(
Node::Kind::OpaqueTypeDescriptorSymbolicReference,
context.getAddressData());
}
return buildContextMangling(context, dem);
auto context = readContextDescriptor(resolved);
if (!context)
return nullptr;
// Try to preserve a reference to an OpaqueTypeDescriptor
// symbolically, since we'd like to read out and resolve the type ref
// to the underlying type if available.
if (useOpaqueTypeSymbolicReferences
&& context.isResolved()
&& context.getResolved()->getKind() == ContextDescriptorKind::OpaqueType){
return dem.createNode(
Node::Kind::OpaqueTypeDescriptorSymbolicReference,
context.getResolved().getAddressData());
}
return buildContextMangling(context, dem);
}
case Demangle::SymbolicReferenceKind::AccessorFunctionReference: {
// The symbolic reference points at a resolver function, but we can't
@@ -426,14 +500,6 @@ public:
return start;
}
/// Given a pointer to an address, attemp to read the pointer value.
Optional<StoredPointer> readPointerValue(StoredPointer Address) {
StoredPointer PointerVal;
if (!Reader->readInteger(RemoteAddress(Address), &PointerVal))
return None;
return Optional<StoredPointer>(PointerVal);
}
/// Given a pointer to the metadata, attempt to read the value
/// witness table. Note that it's not safe to access any non-mandatory
/// members of the value witness table, like extra inhabitants or enum members.
@@ -821,6 +887,23 @@ public:
Dem.demangleSymbol(StringRef(MangledTypeName, Length));
return decodeMangledType(Demangled);
}
/// Given the address of a context descriptor, attempt to read it, or
/// represent it symbolically.
ParentContextDescriptorRef
readContextDescriptor(const RemoteAbsolutePointer &address) {
// Map an unresolved pointer to an unresolved context ref.
if (!address.isResolved()) {
// We can only handle references to a symbol without an offset currently.
if (address.getOffset() != 0) {
return ParentContextDescriptorRef();
}
return ParentContextDescriptorRef(address.getSymbol());
}
return ParentContextDescriptorRef(
readContextDescriptor(address.getResolvedAddress().getAddressData()));
}
/// Given the address of a context descriptor, attempt to read it.
ContextDescriptorRef
@@ -954,6 +1037,44 @@ public:
return ContextDescriptorRef(address, descriptor);
}
/// Demangle the entity represented by a symbolic reference to a given symbol name.
Demangle::NodePointer
buildContextManglingForSymbol(StringRef symbol, Demangler &dem) {
auto demangledSymbol = dem.demangleSymbol(symbol);
if (demangledSymbol->getKind() == Demangle::Node::Kind::Global) {
demangledSymbol = demangledSymbol->getChild(0);
}
switch (demangledSymbol->getKind()) {
// Pointers to nominal type or protocol descriptors would demangle to
// the type they represent.
case Demangle::Node::Kind::NominalTypeDescriptor:
case Demangle::Node::Kind::ProtocolDescriptor:
demangledSymbol = demangledSymbol->getChild(0);
assert(demangledSymbol->getKind() == Demangle::Node::Kind::Type);
break;
// We don't handle pointers to other symbols yet.
// TODO: Opaque type descriptors could be useful.
default:
return nullptr;
}
return demangledSymbol;
}
/// Given a read context descriptor, attempt to build a demangling tree
/// for it.
Demangle::NodePointer
buildContextMangling(const ParentContextDescriptorRef &descriptor,
Demangler &dem) {
if (descriptor.isResolved()) {
return buildContextMangling(descriptor.getResolved(), dem);
}
// Try to demangle the symbol name to figure out what context it would
// point to.
return buildContextManglingForSymbol(descriptor.getSymbol(), dem);
}
/// Given a read context descriptor, attempt to build a demangling tree
/// for it.
Demangle::NodePointer
@@ -1331,47 +1452,40 @@ public:
auto heapMeta = cast<TargetHeapLocalVariableMetadata<Runtime>>(meta);
return heapMeta->OffsetToFirstCapture;
}
Optional<RemoteAbsolutePointer> readPointer(StoredPointer address) {
return Reader->readPointer(RemoteAddress(address), sizeof(StoredPointer));
}
Optional<StoredPointer> readResolvedPointerValue(StoredPointer address) {
if (auto pointer = readPointer(address)) {
if (!pointer->isResolved())
return None;
return (StoredPointer)pointer->getResolvedAddress().getAddressData();
}
return None;
}
template<typename T, typename U>
RemoteAbsolutePointer resolvePointerField(RemoteRef<T> base,
const U &field) {
auto pointerRef = base.getField(field);
return Reader->resolvePointer(RemoteAddress(getAddress(pointerRef)),
*pointerRef.getLocalBuffer());
}
/// Given a remote pointer to class metadata, attempt to read its superclass.
Optional<StoredPointer>
Optional<RemoteAbsolutePointer>
readCaptureDescriptorFromMetadata(StoredPointer MetadataAddress) {
auto meta = readMetadata(MetadataAddress);
if (!meta || meta->getKind() != MetadataKind::HeapLocalVariable)
return None;
auto heapMeta = cast<TargetHeapLocalVariableMetadata<Runtime>>(meta);
return heapMeta->CaptureDescription;
return resolvePointerField(meta, heapMeta->CaptureDescription);
}
protected:
template<typename Offset>
Optional<StoredPointer>
resolveNullableRelativeIndirectableOffset(StoredPointer targetAddress) {
Offset relative;
if (!Reader->readInteger(RemoteAddress(targetAddress), &relative))
return None;
if (relative == 0)
return 0;
bool indirect = relative & 1;
relative &= ~1u;
using SignedOffset = typename std::make_signed<Offset>::type;
using SignedPointer = typename std::make_signed<StoredPointer>::type;
auto signext = (SignedPointer)(SignedOffset)relative;
StoredPointer resultAddress = targetAddress + signext;
// Low bit set in the offset indicates that the offset leads to the absolute
// address in memory.
if (indirect) {
if (!Reader->readBytes(RemoteAddress(resultAddress),
(uint8_t *)&resultAddress,
sizeof(StoredPointer)))
return None;
}
return resultAddress;
}
template<typename Base>
StoredPointer getAddress(RemoteRef<Base> base) {
return (StoredPointer)base.getAddressData();
@@ -1384,14 +1498,14 @@ protected:
}
template<typename Base, typename Field>
Optional<StoredPointer> resolveRelativeIndirectableField(
Optional<RemoteAbsolutePointer> resolveRelativeIndirectableField(
RemoteRef<Base> base, const Field &field) {
auto fieldRef = base.getField(field);
int32_t offset;
memcpy(&offset, fieldRef.getLocalBuffer(), sizeof(int32_t));
if (offset == 0)
return 0;
return Optional<RemoteAbsolutePointer>(nullptr);
bool indirect = offset & 1;
offset &= ~1u;
@@ -1402,14 +1516,10 @@ protected:
// Low bit set in the offset indicates that the offset leads to the absolute
// address in memory.
if (indirect) {
if (!Reader->readBytes(RemoteAddress(resultAddress),
(uint8_t *)&resultAddress,
sizeof(StoredPointer))) {
return None;
}
return readPointer(resultAddress);
}
return resultAddress;
return RemoteAbsolutePointer("", resultAddress);
}
/// Given a pointer to an Objective-C class, try to read its class name.
@@ -1612,17 +1722,26 @@ private:
}
}
/// Returns Optional(nullptr) if there's no parent descriptor.
/// Returns Optional(ParentContextDescriptorRef()) if there's no parent descriptor.
/// Returns None if there was an error reading the parent descriptor.
Optional<ContextDescriptorRef>
Optional<ParentContextDescriptorRef>
readParentContextDescriptor(ContextDescriptorRef base) {
auto parentAddress = resolveRelativeIndirectableField(base, base->Parent);
if (!parentAddress)
return None;
if (!*parentAddress)
return ContextDescriptorRef(nullptr);
if (auto parentDescriptor = readContextDescriptor(*parentAddress))
return parentDescriptor;
if (!parentAddress->isResolved()) {
// Currently we can only handle references directly to a symbol without
// an offset.
if (parentAddress->getOffset() != 0) {
return None;
}
return ParentContextDescriptorRef(parentAddress->getSymbol());
}
auto addr = parentAddress->getResolvedAddress();
if (!addr)
return ParentContextDescriptorRef();
if (auto parentDescriptor = readContextDescriptor(addr.getAddressData()))
return ParentContextDescriptorRef(parentDescriptor);
return None;
}
@@ -1807,13 +1926,20 @@ private:
/// produce a mangled node describing the name of \c context.
Demangle::NodePointer
adoptAnonymousContextName(ContextDescriptorRef contextRef,
Optional<ContextDescriptorRef> &parentContextRef,
Demangler &dem,
Demangle::NodePointer &outerNode) {
Optional<ParentContextDescriptorRef> &parentContextRef,
Demangler &dem,
Demangle::NodePointer &outerNode) {
outerNode = nullptr;
if (!parentContextRef || !*parentContextRef)
// Bail if there is no parent, or if the parent is in another image.
// (Anonymous contexts should always be emitted in the same image as their
// children.)
if (!parentContextRef
|| !*parentContextRef
|| !parentContextRef->isResolved())
return nullptr;
auto parentContextLocalRef = parentContextRef->getResolved();
auto context = contextRef.getLocalBuffer();
auto typeContext = dyn_cast<TargetTypeContextDescriptor<Runtime>>(context);
@@ -1822,11 +1948,11 @@ private:
return nullptr;
auto anonymousParent = dyn_cast_or_null<TargetAnonymousContextDescriptor<Runtime>>(
parentContextRef->getLocalBuffer());
parentContextLocalRef.getLocalBuffer());
if (!anonymousParent)
return nullptr;
auto mangledNode = demangleAnonymousContextName(*parentContextRef, dem);
auto mangledNode = demangleAnonymousContextName(parentContextLocalRef, dem);
if (!mangledNode)
return nullptr;
@@ -1863,7 +1989,7 @@ private:
// We have a match. Update the parent context to skip the anonymous
// context entirely.
parentContextRef = readParentContextDescriptor(*parentContextRef);
parentContextRef = readParentContextDescriptor(parentContextLocalRef);
// The outer node is the first child.
outerNode = mangledNode->getChild(0);
@@ -1914,6 +2040,26 @@ private:
return resultAddress;
}
Demangle::NodePointer
buildContextDescriptorMangling(const ParentContextDescriptorRef &descriptor,
Demangler &dem) {
if (descriptor.isResolved()) {
return buildContextDescriptorMangling(descriptor.getResolved(), dem);
}
// Try to demangle the symbol name to figure out what context it would
// point to.
auto demangledSymbol = buildContextManglingForSymbol(descriptor.getSymbol(),
dem);
if (!demangledSymbol)
return nullptr;
// Look through Type notes since we're building up a mangling here.
if (demangledSymbol->getKind() == Demangle::Node::Kind::Type){
demangledSymbol = demangledSymbol->getChild(0);
}
return demangledSymbol;
}
Demangle::NodePointer
buildContextDescriptorMangling(ContextDescriptorRef descriptor,
Demangler &dem) {
@@ -2184,17 +2330,13 @@ private:
case ContextDescriptorKind::OpaqueType: {
// The opaque type may have a named anonymous context for us to map
// back to its defining decl.
if (!parentDescriptorResult)
return nullptr;
auto anonymous =
dyn_cast_or_null<TargetAnonymousContextDescriptor<Runtime>>(
parentDescriptorResult->getLocalBuffer());
if (!anonymous)
if (!parentDescriptorResult
|| !*parentDescriptorResult
|| !parentDescriptorResult->isResolved())
return nullptr;
auto mangledNode =
demangleAnonymousContextName(*parentDescriptorResult, dem);
demangleAnonymousContextName(parentDescriptorResult->getResolved(), dem);
if (!mangledNode)
return nullptr;
if (mangledNode->getKind() == Node::Kind::Global)

View File

@@ -19,6 +19,9 @@
#define SWIFT_REMOTE_REMOTEADDRESS_H
#include <cstdint>
#include <string>
#include <llvm/ADT/StringRef.h>
#include <cassert>
namespace swift {
namespace remote {
@@ -46,6 +49,40 @@ public:
}
};
/// A symbolic relocated absolute pointer value.
class RemoteAbsolutePointer {
/// The symbol name that the pointer refers to. Empty if the value is absolute.
std::string Symbol;
/// The offset from the symbol, or the resolved remote address if \c Symbol is empty.
int64_t Offset;
public:
RemoteAbsolutePointer()
: Symbol(), Offset(0)
{}
RemoteAbsolutePointer(std::nullptr_t)
: RemoteAbsolutePointer()
{}
RemoteAbsolutePointer(llvm::StringRef Symbol, int64_t Offset)
: Symbol(Symbol), Offset(Offset)
{}
bool isResolved() const { return Symbol.empty(); }
llvm::StringRef getSymbol() const { return Symbol; }
int64_t getOffset() const { return Offset; }
RemoteAddress getResolvedAddress() const {
assert(isResolved());
return RemoteAddress(Offset);
}
explicit operator bool() const {
return Offset != 0 || !Symbol.empty();
}
};
} // end namespace remote
} // end namespace swift

View File

@@ -491,7 +491,7 @@ public:
Result<OpenedExistential>
getDynamicTypeAndAddressClassExistential(RemoteAddress object) {
auto pointerval = Reader.readPointerValue(object.getAddressData());
auto pointerval = Reader.readResolvedPointerValue(object.getAddressData());
if (!pointerval)
return getFailure<OpenedExistential>();
auto result = Reader.readMetadataFromInstance(*pointerval);
@@ -508,7 +508,7 @@ public:
getDynamicTypeAndAddressErrorExistential(RemoteAddress object,
bool dereference=true) {
if (dereference) {
auto pointerval = Reader.readPointerValue(object.getAddressData());
auto pointerval = Reader.readResolvedPointerValue(object.getAddressData());
if (!pointerval)
return getFailure<OpenedExistential>();
object = RemoteAddress(*pointerval);
@@ -531,7 +531,7 @@ public:
auto payloadAddress = result->PayloadAddress;
if (!result->IsBridgedError &&
typeResult->getClassOrBoundGenericClass()) {
auto pointerval = Reader.readPointerValue(
auto pointerval = Reader.readResolvedPointerValue(
payloadAddress.getAddressData());
if (!pointerval)
return getFailure<OpenedExistential>();
@@ -559,7 +559,7 @@ public:
// of the reference.
auto payloadAddress = result->PayloadAddress;
if (typeResult->getClassOrBoundGenericClass()) {
auto pointerval = Reader.readPointerValue(
auto pointerval = Reader.readResolvedPointerValue(
payloadAddress.getAddressData());
if (!pointerval)
return getFailure<OpenedExistential>();
@@ -578,7 +578,7 @@ public:
// 1) Loading a pointer from the input address
// 2) Reading it as metadata and resolving the type
// 3) Wrapping the resolved type in an existential metatype.
auto pointerval = Reader.readPointerValue(object.getAddressData());
auto pointerval = Reader.readResolvedPointerValue(object.getAddressData());
if (!pointerval)
return getFailure<OpenedExistential>();
auto typeResult = Reader.readTypeFromMetadata(*pointerval);