Skip to content

[lldb] Adapt class resolution to work without reflection in binaries #8105

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 55 additions & 3 deletions lldb/source/Plugins/LanguageRuntime/Swift/ReflectionContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,14 +129,28 @@ class TargetReflectionContext : public ReflectionContextInterface {
return type_ref_or_err.getType();
}

const swift::reflection::TypeInfo *GetClassInstanceTypeInfo(
const swift::reflection::RecordTypeInfo *GetClassInstanceTypeInfo(
const swift::reflection::TypeRef *type_ref,
swift::remote::TypeInfoProvider *provider,
swift::reflection::DescriptorFinder *descriptor_finder) override {
auto on_exit = SetDescriptorFinderAndClearOnExit(descriptor_finder);
if (!type_ref)
return nullptr;
return m_type_converter.getClassInstanceTypeInfo(type_ref, 0, provider);

auto start =
m_reflection_ctx.computeUnalignedFieldStartOffset(type_ref);
if (!start) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you wrap this in if(GetLog(LLDBLog::Types)), so we don't do the string operations if we don't log?

if (auto *log = GetLog(LLDBLog::Types)) {
std::stringstream ss;
type_ref->dump(ss);
LLDB_LOG(log, "Could not compute start field offset for typeref: ",
ss.str());
}
return nullptr;
}

return m_type_converter.getClassInstanceTypeInfo(type_ref, *start,
provider);
}

const swift::reflection::TypeInfo *
Expand Down Expand Up @@ -195,6 +209,23 @@ class TargetReflectionContext : public ReflectionContextInterface {
return m_reflection_ctx.getBuilder().lookupSuperclass(tr);
}

bool
ForEachSuperClassType(swift::remote::TypeInfoProvider *tip,
swift::reflection::DescriptorFinder *descriptor_finder,
const swift::reflection::TypeRef *tr,
std::function<bool(SuperClassType)> fn) override {
while (tr) {
if (fn({[=]() -> const swift::reflection::RecordTypeInfo * {
return GetRecordTypeInfo(tr, tip, descriptor_finder);
},
[=]() -> const swift::reflection::TypeRef * { return tr; }}))
return true;

tr = LookupSuperclass(tr, descriptor_finder);
}
return false;
}

bool
ForEachSuperClassType(swift::remote::TypeInfoProvider *tip,
swift::reflection::DescriptorFinder *descriptor_finder,
Expand Down Expand Up @@ -299,8 +330,29 @@ class TargetReflectionContext : public ReflectionContextInterface {
StripSignedPointer(swift::remote::RemoteAbsolutePointer pointer) override {
return m_reflection_ctx.stripSignedPointer(pointer);
}
};

private:
/// Return a description of the layout of the record (classes, structs and
/// tuples) type given its typeref.
const swift::reflection::RecordTypeInfo *
GetRecordTypeInfo(const swift::reflection::TypeRef *type_ref,
swift::remote::TypeInfoProvider *tip,
swift::reflection::DescriptorFinder *descriptor_finder) {
auto *type_info = GetTypeInfo(type_ref, tip, descriptor_finder);
if (auto record_type_info =
llvm::dyn_cast_or_null<swift::reflection::RecordTypeInfo>(
type_info))
return record_type_info;
if (llvm::isa_and_nonnull<swift::reflection::ReferenceTypeInfo>(type_info))
return GetClassInstanceTypeInfo(type_ref, tip, descriptor_finder);
if (auto *log = GetLog(LLDBLog::Types)) {
std::stringstream ss;
type_ref->dump(ss);
LLDB_LOG(log, "Could not get record type info for typeref: ", ss.str());
}
return nullptr;
}
};
} // namespace

namespace lldb_private {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,21 +90,33 @@ class ReflectionContextInterface {
const swift::reflection::TypeRef *type_ref,
swift::remote::TypeInfoProvider *provider,
swift::reflection::DescriptorFinder *descriptor_finder) = 0;
virtual const swift::reflection::TypeInfo *GetTypeInfo(
const swift::reflection::TypeRef *type_ref,
swift::remote::TypeInfoProvider *provider,
swift::reflection::DescriptorFinder *descriptor_finder) = 0;
virtual const swift::reflection::TypeInfo *
GetTypeInfo(const swift::reflection::TypeRef *type_ref,
swift::remote::TypeInfoProvider *provider,
swift::reflection::DescriptorFinder *descriptor_finder) = 0;
virtual const swift::reflection::TypeInfo *GetTypeInfoFromInstance(
lldb::addr_t instance, swift::remote::TypeInfoProvider *provider,
swift::reflection::DescriptorFinder *descriptor_finder) = 0;
virtual swift::remote::MemoryReader &GetReader() = 0;
virtual const swift::reflection::TypeRef *LookupSuperclass(
const swift::reflection::TypeRef *tr,
swift::reflection::DescriptorFinder *descriptor_finder) = 0;
virtual bool ForEachSuperClassType(
swift::remote::TypeInfoProvider *tip,
swift::reflection::DescriptorFinder *descriptor_finder,
lldb::addr_t pointer, std::function<bool(SuperClassType)> fn) = 0;
virtual bool
ForEachSuperClassType(swift::remote::TypeInfoProvider *tip,
swift::reflection::DescriptorFinder *descriptor_finder,
lldb::addr_t pointer,
std::function<bool(SuperClassType)> fn) = 0;

/// Traverses the superclass hierarchy using the typeref, as opposed to the
/// other version of the function that uses the instance's pointer. This
/// version is useful when reflection metadata has been stripped from the
/// binary (for example, when debugging embedded Swift programs).
virtual bool
ForEachSuperClassType(swift::remote::TypeInfoProvider *tip,
swift::reflection::DescriptorFinder *descriptor_finder,
const swift::reflection::TypeRef *tr,
std::function<bool(SuperClassType)> fn) = 0;

virtual llvm::Optional<std::pair<const swift::reflection::TypeRef *,
swift::remote::RemoteAddress>>
ProjectExistentialAndUnwrapClass(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -999,8 +999,9 @@ CompilerType SwiftLanguageRuntimeImpl::GetChildCompilerTypeAtIndex(
};

// Try the static type metadata.
auto *ti =
GetSwiftRuntimeTypeInfo(type, exe_ctx.GetBestExecutionContextScope());
const swift::reflection::TypeRef *tr = nullptr;
auto *ti = GetSwiftRuntimeTypeInfo(
type, exe_ctx.GetBestExecutionContextScope(), &tr);
if (!ti)
return {};
// Structs and Tuples.
Expand Down Expand Up @@ -1066,6 +1067,29 @@ CompilerType SwiftLanguageRuntimeImpl::GetChildCompilerTypeAtIndex(
if (auto *rti =
llvm::dyn_cast_or_null<swift::reflection::ReferenceTypeInfo>(ti)) {
// Objects.
switch (rti->getReferenceKind()) {
case swift::reflection::ReferenceKind::Weak:
case swift::reflection::ReferenceKind::Unowned:
case swift::reflection::ReferenceKind::Unmanaged:
// Weak references are implicitly Optionals, so report the one
// child of Optional here.
if (idx != 0)
break; // Maybe assert that type is not an Optional?
child_name = "some";
child_byte_size = ts->GetPointerByteSize();
child_byte_offset = 0;
child_bitfield_bit_size = 0;
child_bitfield_bit_offset = 0;
child_is_base_class = false;
child_is_deref_of_parent = false;
language_flags = 0;
if (CompilerType optional = GetWeakReferent(*ts, type))
return optional;
break;
default:
break;
}

// Try the instance type metadata.
if (!valobj)
return {};
Expand All @@ -1085,61 +1109,58 @@ CompilerType SwiftLanguageRuntimeImpl::GetChildCompilerTypeAtIndex(
instance_type.GetTypeSystem().dyn_cast_or_null<TypeSystemSwift>();
if (!instance_ts)
return {};

// LLDBTypeInfoProvider needs to be kept alive while supers gets accessed.
llvm::SmallVector<SuperClassType, 2> supers;
LLDBTypeInfoProvider tip(*this, *instance_ts);
reflection_ctx->ForEachSuperClassType(
&tip, ts->GetDescriptorFinder(), pointer,
[&](SuperClassType sc) -> bool {
// If the typeref is invalid, we don't want to process it (for
// example, this could be an artifical ObjC class).
if (!sc.get_typeref())
return false;
auto superclass_finder = [&](SuperClassType sc) -> bool {
// If the typeref is invalid, we don't want to process it (for
// example, this could be an artifical ObjC class).
if (!sc.get_typeref())
return false;

if (!found_start) {
// The ValueObject always points to the same class instance,
// even when querying base classes. Drop base classes until we
// reach the requested type.
if (auto *tr = sc.get_typeref()) {
NodePointer base_class = tr->getDemangling(dem);
if (TypeSystemSwiftTypeRef::GetBaseName(base_class) != type_name)
return false;
found_start = true;
}
}
supers.push_back(sc);
return supers.size() >= 2;
});
if (!found_start) {
// The ValueObject always points to the same class instance,
// even when querying base classes. Drop base classes until we
// reach the requested type.
if (auto *tr = sc.get_typeref()) {
NodePointer base_class = tr->getDemangling(dem);
if (TypeSystemSwiftTypeRef::GetBaseName(base_class) != type_name)
return false;
found_start = true;
}
}
supers.push_back(sc);
return supers.size() >= 2;
};

if (supers.size() == 0) {
LLDBTypeInfoProvider tip(*this, *instance_ts);
// Try out the instance pointer based super class traversal first, as its
// usually faster.
reflection_ctx->ForEachSuperClassType(&tip, ts->GetDescriptorFinder(),
pointer, superclass_finder);

if (supers.empty())
// If the pointer based super class traversal failed (this may happen
// when metadata is not present in the binary, for example: embedded
// Swift), try the typeref based one next.
reflection_ctx->ForEachSuperClassType(&tip, ts->GetDescriptorFinder(), tr,
superclass_finder);

if (supers.empty()) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why twice the same condition in a row?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dynamic type resolution first tries the pointer-based ForEachSuperClassType, if that fails, it tries the typeref-based ForEachSuperClassType, if that also fails then we're out of luck.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think my question was aiming at: does ForEachSuperClassType modify supers?
Otherwise why isn't this

if (supers.empty()) {
   ForEachSuperClassType ...;
  LLDB_LOG
}

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it's being captured by reference by the superclass_finder lambda

LLDB_LOG(GetLog(LLDBLog::Types),
"Couldn't find the type metadata for {0} in instance",
type.GetTypeName());
return {};
}

switch (rti->getReferenceKind()) {
case swift::reflection::ReferenceKind::Weak:
case swift::reflection::ReferenceKind::Unowned:
case swift::reflection::ReferenceKind::Unmanaged:
// Weak references are implicitly Optionals, so report the one
// child of Optional here.
if (idx != 0)
break; // Maybe assert that type is not an Optional?
child_name = "some";
child_byte_size = ts->GetPointerByteSize();
child_byte_offset = 0;
child_bitfield_bit_size = 0;
child_bitfield_bit_offset = 0;
child_is_base_class = false;
child_is_deref_of_parent = false;
language_flags = 0;
if (CompilerType optional = GetWeakReferent(*ts, type))
return optional;
break;
default:
break;
auto *cti = reflection_ctx->GetClassInstanceTypeInfo(
tr, &tip, ts->GetDescriptorFinder());
if (auto *rti =
llvm::dyn_cast_or_null<swift::reflection::RecordTypeInfo>(cti)) {
auto fields = rti->getFields();
if (idx < fields.size()) {
llvm::Optional<TypeSystemSwift::TupleElement> tuple;
return get_from_field_info(fields[idx], tuple, true);
}
}
return {};
}

// Handle the artificial base class fields.
Expand Down Expand Up @@ -1587,6 +1608,14 @@ bool SwiftLanguageRuntimeImpl::GetDynamicTypeAndAddress_Class(

const auto *typeref = reflection_ctx->ReadTypeFromInstance(
instance_ptr, ts.GetDescriptorFinder(), true);

// If we couldn't find the typeref from the instance, the best we can do is
// use the static type. This is a valid use case when the binary doesn't
// contain any metadata (for example, embedded Swift).
if (!typeref)
typeref = reflection_ctx->GetTypeRefOrNull(class_type.GetMangledTypeName(),
ts.GetDescriptorFinder());

if (!typeref) {
LLDB_LOGF(log,
"could not read typeref for type: %s (instance_ptr = 0x%" PRIx64
Expand Down