mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-25 10:36:07 +00:00

Add the ability to get a C++ vtable ValueObject from another ValueObject. This patch adds the ability to ask a ValueObject for a ValueObject that represents the virtual function table for a C++ class. If the ValueObject is not a C++ class with a vtable, a valid ValueObject value will be returned that contains an appropriate error. If it is successful a valid ValueObject that represents vtable will be returned. The ValueObject that is returned will have a name that matches the demangled value for a C++ vtable mangled name like "vtable for <class-name>". It will have N children, one for each virtual function pointer. Each child's value is the function pointer itself, the summary is the symbolication of this function pointer, and the type will be a valid function pointer from the debug info if there is debug information corresponding to the virtual function pointer. The vtable SBValue will have the following: - SBValue::GetName() returns "vtable for <class>" - SBValue::GetValue() returns a string representation of the vtable address - SBValue::GetSummary() returns NULL - SBValue::GetType() returns a type appropriate for a uintptr_t type for the current process - SBValue::GetLoadAddress() returns the address of the vtable adderess - SBValue::GetValueAsUnsigned(...) returns the vtable address - SBValue::GetNumChildren() returns the number of virtual function pointers in the vtable - SBValue::GetChildAtIndex(...) returns a SBValue that represents a virtual function pointer The child SBValue objects that represent a virtual function pointer has the following values: - SBValue::GetName() returns "[%u]" where %u is the vtable function pointer index - SBValue::GetValue() returns a string representation of the virtual function pointer - SBValue::GetSummary() returns a symbolicated respresentation of the virtual function pointer - SBValue::GetType() returns the function prototype type if there is debug info, or a generic funtion prototype if there is no debug info - SBValue::GetLoadAddress() returns the address of the virtual function pointer - SBValue::GetValueAsUnsigned(...) returns the virtual function pointer - SBValue::GetNumChildren() returns 0 - SBValue::GetChildAtIndex(...) returns invalid SBValue for any index Examples of using this API via python: ``` (lldb) script vtable = lldb.frame.FindVariable("shape_ptr").GetVTable() (lldb) script vtable vtable for Shape = 0x0000000100004088 { [0] = 0x0000000100003d20 a.out`Shape::~Shape() at main.cpp:3 [1] = 0x0000000100003e4c a.out`Shape::~Shape() at main.cpp:3 [2] = 0x0000000100003e7c a.out`Shape::area() at main.cpp:4 [3] = 0x0000000100003e3c a.out`Shape::optional() at main.cpp:7 } (lldb) script c = vtable.GetChildAtIndex(0) (lldb) script c (void ()) [0] = 0x0000000100003d20 a.out`Shape::~Shape() at main.cpp:3 ```
275 lines
8.9 KiB
C++
275 lines
8.9 KiB
C++
//===-- ValueObjectVTable.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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "lldb/Core/ValueObjectVTable.h"
|
|
#include "lldb/Core/Module.h"
|
|
#include "lldb/Core/ValueObjectChild.h"
|
|
#include "lldb/Symbol/Function.h"
|
|
#include "lldb/Target/Language.h"
|
|
#include "lldb/Target/LanguageRuntime.h"
|
|
#include "lldb/lldb-defines.h"
|
|
#include "lldb/lldb-enumerations.h"
|
|
#include "lldb/lldb-forward.h"
|
|
#include "lldb/lldb-private-enumerations.h"
|
|
|
|
using namespace lldb;
|
|
using namespace lldb_private;
|
|
|
|
class ValueObjectVTableChild : public ValueObject {
|
|
public:
|
|
ValueObjectVTableChild(ValueObject &parent, uint32_t func_idx,
|
|
uint64_t addr_size)
|
|
: ValueObject(parent), m_func_idx(func_idx), m_addr_size(addr_size) {
|
|
SetFormat(eFormatPointer);
|
|
SetName(ConstString(llvm::formatv("[{0}]", func_idx).str()));
|
|
}
|
|
|
|
~ValueObjectVTableChild() override = default;
|
|
|
|
std::optional<uint64_t> GetByteSize() override { return m_addr_size; };
|
|
|
|
size_t CalculateNumChildren(uint32_t max) override { return 0; };
|
|
|
|
ValueType GetValueType() const override { return eValueTypeVTableEntry; };
|
|
|
|
bool IsInScope() override {
|
|
if (ValueObject *parent = GetParent())
|
|
return parent->IsInScope();
|
|
return false;
|
|
};
|
|
|
|
protected:
|
|
bool UpdateValue() override {
|
|
SetValueIsValid(false);
|
|
m_value.Clear();
|
|
ValueObject *parent = GetParent();
|
|
if (!parent) {
|
|
m_error.SetErrorString("owning vtable object not valid");
|
|
return false;
|
|
}
|
|
|
|
addr_t parent_addr = parent->GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
|
|
if (parent_addr == LLDB_INVALID_ADDRESS) {
|
|
m_error.SetErrorString("invalid vtable address");
|
|
return false;
|
|
}
|
|
|
|
ProcessSP process_sp = GetProcessSP();
|
|
if (!process_sp) {
|
|
m_error.SetErrorString("no process");
|
|
return false;
|
|
}
|
|
|
|
TargetSP target_sp = GetTargetSP();
|
|
if (!target_sp) {
|
|
m_error.SetErrorString("no target");
|
|
return false;
|
|
}
|
|
|
|
// Each `vtable_entry_addr` points to the function pointer.
|
|
addr_t vtable_entry_addr = parent_addr + m_func_idx * m_addr_size;
|
|
addr_t vfunc_ptr =
|
|
process_sp->ReadPointerFromMemory(vtable_entry_addr, m_error);
|
|
if (m_error.Fail()) {
|
|
m_error.SetErrorStringWithFormat(
|
|
"failed to read virtual function entry 0x%16.16" PRIx64,
|
|
vtable_entry_addr);
|
|
return false;
|
|
}
|
|
|
|
|
|
// Set our value to be the load address of the function pointer in memory
|
|
// and our type to be the function pointer type.
|
|
m_value.SetValueType(Value::ValueType::LoadAddress);
|
|
m_value.GetScalar() = vtable_entry_addr;
|
|
|
|
// See if our resolved address points to a function in the debug info. If
|
|
// it does, then we can report the type as a function prototype for this
|
|
// function.
|
|
Function *function = nullptr;
|
|
Address resolved_vfunc_ptr_address;
|
|
target_sp->ResolveLoadAddress(vfunc_ptr, resolved_vfunc_ptr_address);
|
|
if (resolved_vfunc_ptr_address.IsValid())
|
|
function = resolved_vfunc_ptr_address.CalculateSymbolContextFunction();
|
|
if (function) {
|
|
m_value.SetCompilerType(function->GetCompilerType().GetPointerType());
|
|
} else {
|
|
// Set our value's compiler type to a generic function protoype so that
|
|
// it displays as a hex function pointer for the value and the summary
|
|
// will display the address description.
|
|
|
|
// Get the original type that this vtable is based off of so we can get
|
|
// the language from it correctly.
|
|
ValueObject *val = parent->GetParent();
|
|
auto type_system = target_sp->GetScratchTypeSystemForLanguage(
|
|
val ? val->GetObjectRuntimeLanguage() : eLanguageTypeC_plus_plus);
|
|
if (type_system) {
|
|
m_value.SetCompilerType(
|
|
(*type_system)->CreateGenericFunctionPrototype().GetPointerType());
|
|
} else {
|
|
consumeError(type_system.takeError());
|
|
}
|
|
}
|
|
|
|
// Now read our value into m_data so that our we can use the default
|
|
// summary provider for C++ for function pointers which will get the
|
|
// address description for our function pointer.
|
|
if (m_error.Success()) {
|
|
const bool thread_and_frame_only_if_stopped = true;
|
|
ExecutionContext exe_ctx(
|
|
GetExecutionContextRef().Lock(thread_and_frame_only_if_stopped));
|
|
m_error = m_value.GetValueAsData(&exe_ctx, m_data, GetModule().get());
|
|
}
|
|
SetValueDidChange(true);
|
|
SetValueIsValid(true);
|
|
return true;
|
|
};
|
|
|
|
CompilerType GetCompilerTypeImpl() override {
|
|
return m_value.GetCompilerType();
|
|
};
|
|
|
|
const uint32_t m_func_idx;
|
|
const uint64_t m_addr_size;
|
|
|
|
private:
|
|
// For ValueObject only
|
|
ValueObjectVTableChild(const ValueObjectVTableChild &) = delete;
|
|
const ValueObjectVTableChild &
|
|
operator=(const ValueObjectVTableChild &) = delete;
|
|
};
|
|
|
|
ValueObjectSP ValueObjectVTable::Create(ValueObject &parent) {
|
|
return (new ValueObjectVTable(parent))->GetSP();
|
|
}
|
|
|
|
ValueObjectVTable::ValueObjectVTable(ValueObject &parent)
|
|
: ValueObject(parent) {
|
|
SetFormat(eFormatPointer);
|
|
}
|
|
|
|
std::optional<uint64_t> ValueObjectVTable::GetByteSize() {
|
|
if (m_vtable_symbol)
|
|
return m_vtable_symbol->GetByteSize();
|
|
return std::nullopt;
|
|
}
|
|
|
|
size_t ValueObjectVTable::CalculateNumChildren(uint32_t max) {
|
|
if (UpdateValueIfNeeded(false))
|
|
return m_num_vtable_entries <= max ? m_num_vtable_entries : max;
|
|
return 0;
|
|
}
|
|
|
|
ValueType ValueObjectVTable::GetValueType() const { return eValueTypeVTable; }
|
|
|
|
ConstString ValueObjectVTable::GetTypeName() {
|
|
if (m_vtable_symbol)
|
|
return m_vtable_symbol->GetName();
|
|
return ConstString();
|
|
}
|
|
|
|
ConstString ValueObjectVTable::GetQualifiedTypeName() { return GetTypeName(); }
|
|
|
|
ConstString ValueObjectVTable::GetDisplayTypeName() {
|
|
if (m_vtable_symbol)
|
|
return m_vtable_symbol->GetDisplayName();
|
|
return ConstString();
|
|
}
|
|
|
|
bool ValueObjectVTable::IsInScope() { return GetParent()->IsInScope(); }
|
|
|
|
ValueObject *ValueObjectVTable::CreateChildAtIndex(size_t idx,
|
|
bool synthetic_array_member,
|
|
int32_t synthetic_index) {
|
|
if (synthetic_array_member)
|
|
return nullptr;
|
|
return new ValueObjectVTableChild(*this, idx, m_addr_size);
|
|
}
|
|
|
|
bool ValueObjectVTable::UpdateValue() {
|
|
m_error.Clear();
|
|
m_flags.m_children_count_valid = false;
|
|
SetValueIsValid(false);
|
|
m_num_vtable_entries = 0;
|
|
ValueObject *parent = GetParent();
|
|
if (!parent) {
|
|
m_error.SetErrorString("no parent object");
|
|
return false;
|
|
}
|
|
|
|
ProcessSP process_sp = GetProcessSP();
|
|
if (!process_sp) {
|
|
m_error.SetErrorString("no process");
|
|
return false;
|
|
}
|
|
|
|
const LanguageType language = parent->GetObjectRuntimeLanguage();
|
|
LanguageRuntime *language_runtime = process_sp->GetLanguageRuntime(language);
|
|
|
|
if (language_runtime == nullptr) {
|
|
m_error.SetErrorStringWithFormat(
|
|
"no language runtime support for the language \"%s\"",
|
|
Language::GetNameForLanguageType(language));
|
|
return false;
|
|
}
|
|
|
|
// Get the vtable information from the language runtime.
|
|
llvm::Expected<LanguageRuntime::VTableInfo> vtable_info_or_err =
|
|
language_runtime->GetVTableInfo(*parent, /*check_type=*/true);
|
|
if (!vtable_info_or_err) {
|
|
m_error = vtable_info_or_err.takeError();
|
|
return false;
|
|
}
|
|
|
|
TargetSP target_sp = GetTargetSP();
|
|
const addr_t vtable_start_addr =
|
|
vtable_info_or_err->addr.GetLoadAddress(target_sp.get());
|
|
|
|
m_vtable_symbol = vtable_info_or_err->symbol;
|
|
if (!m_vtable_symbol) {
|
|
m_error.SetErrorStringWithFormat(
|
|
"no vtable symbol found containing 0x%" PRIx64, vtable_start_addr);
|
|
return false;
|
|
}
|
|
|
|
// Now that we know it's a vtable, we update the object's state.
|
|
SetName(GetTypeName());
|
|
|
|
// Calculate the number of entries
|
|
if (!m_vtable_symbol->GetByteSizeIsValid()) {
|
|
m_error.SetErrorStringWithFormat(
|
|
"vtable symbol \"%s\" doesn't have a valid size",
|
|
m_vtable_symbol->GetMangled().GetDemangledName().GetCString());
|
|
return false;
|
|
}
|
|
|
|
m_addr_size = process_sp->GetAddressByteSize();
|
|
const addr_t vtable_end_addr =
|
|
m_vtable_symbol->GetLoadAddress(target_sp.get()) +
|
|
m_vtable_symbol->GetByteSize();
|
|
m_num_vtable_entries = (vtable_end_addr - vtable_start_addr) / m_addr_size;
|
|
|
|
m_value.SetValueType(Value::ValueType::LoadAddress);
|
|
m_value.GetScalar() = parent->GetAddressOf();
|
|
auto type_system_or_err =
|
|
target_sp->GetScratchTypeSystemForLanguage(eLanguageTypeC_plus_plus);
|
|
if (type_system_or_err) {
|
|
m_value.SetCompilerType(
|
|
(*type_system_or_err)->GetBasicTypeFromAST(eBasicTypeUnsignedLong));
|
|
} else {
|
|
consumeError(type_system_or_err.takeError());
|
|
}
|
|
SetValueDidChange(true);
|
|
SetValueIsValid(true);
|
|
return true;
|
|
}
|
|
|
|
CompilerType ValueObjectVTable::GetCompilerTypeImpl() { return CompilerType(); }
|
|
|
|
ValueObjectVTable::~ValueObjectVTable() = default;
|