[Lldb-commits] [lldb] Add the ability to get a C++ vtable ValueObject from another ValueObj… (PR #67599)
Greg Clayton via lldb-commits
lldb-commits at lists.llvm.org
Thu Sep 28 12:00:02 PDT 2023
https://github.com/clayborg updated https://github.com/llvm/llvm-project/pull/67599
>From 8fa9aae354ac455f4ea443de1bb5f753fe93fb51 Mon Sep 17 00:00:00 2001
From: Greg Clayton <gclayton at fb.com>
Date: Wed, 27 Sep 2023 13:03:40 -0700
Subject: [PATCH 1/3] 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
```
---
lldb/include/lldb/API/SBValue.h | 28 ++
lldb/include/lldb/Core/ValueObject.h | 4 +
lldb/include/lldb/Core/ValueObjectChild.h | 1 +
lldb/include/lldb/Core/ValueObjectVTable.h | 65 ++++
lldb/include/lldb/Symbol/TypeSystem.h | 4 +
lldb/include/lldb/lldb-enumerations.h | 4 +-
lldb/source/API/SBValue.cpp | 17 +-
lldb/source/Commands/CommandObjectFrame.cpp | 2 +
lldb/source/Core/CMakeLists.txt | 1 +
lldb/source/Core/ValueObject.cpp | 5 +
lldb/source/Core/ValueObjectVTable.cpp | 325 ++++++++++++++++++
.../DataFormatters/CXXFunctionPointer.cpp | 6 +-
.../Language/CPlusPlus/CPlusPlusLanguage.cpp | 4 +-
.../TypeSystem/Clang/TypeSystemClang.cpp | 26 +-
.../TypeSystem/Clang/TypeSystemClang.h | 4 +
lldb/test/API/functionalities/vtable/Makefile | 3 +
.../functionalities/vtable/TestVTableValue.py | 135 ++++++++
lldb/test/API/functionalities/vtable/main.cpp | 37 ++
18 files changed, 664 insertions(+), 7 deletions(-)
create mode 100644 lldb/include/lldb/Core/ValueObjectVTable.h
create mode 100644 lldb/source/Core/ValueObjectVTable.cpp
create mode 100644 lldb/test/API/functionalities/vtable/Makefile
create mode 100644 lldb/test/API/functionalities/vtable/TestVTableValue.py
create mode 100644 lldb/test/API/functionalities/vtable/main.cpp
diff --git a/lldb/include/lldb/API/SBValue.h b/lldb/include/lldb/API/SBValue.h
index b66c2d5642b6f95..333bdf1738eecaf 100644
--- a/lldb/include/lldb/API/SBValue.h
+++ b/lldb/include/lldb/API/SBValue.h
@@ -374,6 +374,34 @@ class LLDB_API SBValue {
lldb::SBWatchpoint WatchPointee(bool resolve_location, bool read, bool write,
SBError &error);
+ /// If this value represents a C++ class that has a vtable, return an value
+ /// that represents the virtual function table.
+ ///
+ /// SBValue::GetError() will be in the success state if this value represents
+ /// a C++ class with a vtable, or an appropriate error describing that the
+ /// object isn't a C++ class with a vtable or not a C++ class.
+ ///
+ /// SBValue::GetName() will be the demangled symbol name for the virtual
+ /// function table like "vtable for Baseclass".
+ ///
+ /// SBValue::GetValue() will be the address of the first vtable entry if the
+ /// current SBValue is a class with a vtable, or nothing the current SBValue
+ /// is not a C++ class or not a C++ class that has a vtable.
+ ///
+ /// SBValue::GetSummary() will contain the number of virtual function pointers
+ /// in the vtable like is done for arrays.
+ ///
+ /// SBValue::GetNumChildren() will return the number of virtual function
+ /// pointers in the vtable, or zero on error.
+ ///
+ /// SBValue::GetChildAtIndex(...) will return each virtual function pointer
+ /// as a SBValue object. The child SBValue objects name will be the array
+ /// index, value will be the virtual function pointer, summary will be the
+ /// symbolicated address description, and if the the adress resolves to a
+ /// function in debug info, the child type will be the function prototype as
+ /// a SBType object.
+ lldb::SBValue GetVTable();
+
protected:
friend class SBBlock;
friend class SBFrame;
diff --git a/lldb/include/lldb/Core/ValueObject.h b/lldb/include/lldb/Core/ValueObject.h
index 3af94f0a86e2fcc..20b3086138457f7 100644
--- a/lldb/include/lldb/Core/ValueObject.h
+++ b/lldb/include/lldb/Core/ValueObject.h
@@ -620,6 +620,10 @@ class ValueObject {
virtual lldb::ValueObjectSP CastPointerType(const char *name,
lldb::TypeSP &type_sp);
+ /// If this object represents a C++ class with a vtable, return an object
+ /// that represents the virtual function table. If the object isn't a class
+ /// with a vtable, return a valid ValueObject with the error set correctly.
+ lldb::ValueObjectSP GetVTable();
// The backing bits of this value object were updated, clear any descriptive
// string, so we know we have to refetch them.
void ValueUpdated() {
diff --git a/lldb/include/lldb/Core/ValueObjectChild.h b/lldb/include/lldb/Core/ValueObjectChild.h
index 07b37aa8a405f7e..46b14e6840f0dc3 100644
--- a/lldb/include/lldb/Core/ValueObjectChild.h
+++ b/lldb/include/lldb/Core/ValueObjectChild.h
@@ -73,6 +73,7 @@ class ValueObjectChild : public ValueObject {
friend class ValueObject;
friend class ValueObjectConstResult;
friend class ValueObjectConstResultImpl;
+ friend class ValueObjectVTable;
ValueObjectChild(ValueObject &parent, const CompilerType &compiler_type,
ConstString name, uint64_t byte_size,
diff --git a/lldb/include/lldb/Core/ValueObjectVTable.h b/lldb/include/lldb/Core/ValueObjectVTable.h
new file mode 100644
index 000000000000000..faa0af238e764f4
--- /dev/null
+++ b/lldb/include/lldb/Core/ValueObjectVTable.h
@@ -0,0 +1,65 @@
+//===-- ValueObjectVTable.h -------------------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_CORE_VALUEOBJECTVTABLE_H
+#define LLDB_CORE_VALUEOBJECTVTABLE_H
+
+#include "lldb/Core/ValueObject.h"
+
+namespace lldb_private {
+
+/// A class that represents a virtual function table for a C++ class.
+///
+///
+class ValueObjectVTable : public ValueObject {
+public:
+ ~ValueObjectVTable() override;
+
+ static lldb::ValueObjectSP Create(ValueObject &parent);
+
+ std::optional<uint64_t> GetByteSize() override;
+
+ size_t CalculateNumChildren(uint32_t max) override;
+
+ ValueObject *CreateChildAtIndex(size_t idx, bool synthetic_array_member,
+ int32_t synthetic_index) override;
+
+ lldb::ValueType GetValueType() const override;
+
+ ConstString GetTypeName() override;
+
+ ConstString GetQualifiedTypeName() override;
+
+ ConstString GetDisplayTypeName() override;
+
+ bool IsInScope() override;
+
+protected:
+ bool UpdateValue() override;
+
+ CompilerType GetCompilerTypeImpl() override;
+
+ /// The symbol for the C++ virtual function table.
+ Symbol *m_vtable_symbol = nullptr;
+ /// Cache the number of vtable children when we update the value.
+ uint32_t m_num_vtable_entries = 0;
+ /// Cache the address size in bytes to avoid checking with the process to
+ /// many times.
+ uint32_t m_addr_size = 0;
+
+private:
+ ValueObjectVTable(ValueObject &parent);
+
+ // For ValueObject only
+ ValueObjectVTable(const ValueObjectVTable &) = delete;
+ const ValueObjectVTable &operator=(const ValueObjectVTable &) = delete;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_CORE_VALUEOBJECTVTABLE_H
diff --git a/lldb/include/lldb/Symbol/TypeSystem.h b/lldb/include/lldb/Symbol/TypeSystem.h
index eb6e453e1aec0d0..16b912c8113f969 100644
--- a/lldb/include/lldb/Symbol/TypeSystem.h
+++ b/lldb/include/lldb/Symbol/TypeSystem.h
@@ -444,6 +444,10 @@ class TypeSystem : public PluginInterface,
virtual CompilerType GetBasicTypeFromAST(lldb::BasicType basic_type) = 0;
+ virtual CompilerType CreateGenericFunctionPrototype() {
+ return CompilerType();
+ }
+
virtual CompilerType
GetBuiltinTypeForEncodingAndBitSize(lldb::Encoding encoding,
size_t bit_size) = 0;
diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h
index 21e098481ce8022..b9dae9ca5573d5b 100644
--- a/lldb/include/lldb/lldb-enumerations.h
+++ b/lldb/include/lldb/lldb-enumerations.h
@@ -322,7 +322,9 @@ enum ValueType {
eValueTypeRegister = 5, ///< stack frame register value
eValueTypeRegisterSet = 6, ///< A collection of stack frame register values
eValueTypeConstResult = 7, ///< constant result variables
- eValueTypeVariableThreadLocal = 8 ///< thread local storage variable
+ eValueTypeVariableThreadLocal = 8, ///< thread local storage variable
+ eValueTypeVTable = 9, ///< virtual function table
+ eValueTypeVTableEntry = 10, ///< function pointer in virtual function table
};
/// Token size/granularities for Input Readers.
diff --git a/lldb/source/API/SBValue.cpp b/lldb/source/API/SBValue.cpp
index 738773c93c49b03..cf2d862fa504844 100644
--- a/lldb/source/API/SBValue.cpp
+++ b/lldb/source/API/SBValue.cpp
@@ -114,7 +114,7 @@ class ValueImpl {
Target *target = value_sp->GetTargetSP().get();
// If this ValueObject holds an error, then it is valuable for that.
- if (value_sp->GetError().Fail())
+ if (value_sp->GetError().Fail())
return value_sp;
if (!target)
@@ -1038,8 +1038,8 @@ lldb::ValueObjectSP SBValue::GetSP(ValueLocker &locker) const {
// IsValid means that the SBValue has a value in it. But that's not the
// only time that ValueObjects are useful. We also want to return the value
// if there's an error state in it.
- if (!m_opaque_sp || (!m_opaque_sp->IsValid()
- && (m_opaque_sp->GetRootSP()
+ if (!m_opaque_sp || (!m_opaque_sp->IsValid()
+ && (m_opaque_sp->GetRootSP()
&& !m_opaque_sp->GetRootSP()->GetError().Fail()))) {
locker.GetError().SetErrorString("No value");
return ValueObjectSP();
@@ -1498,3 +1498,14 @@ lldb::SBValue SBValue::Persist() {
}
return persisted_sb;
}
+
+lldb::SBValue SBValue::GetVTable() {
+ SBValue vtable_sb;
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (!value_sp)
+ return vtable_sb;
+
+ vtable_sb.SetSP(value_sp->GetVTable());
+ return vtable_sb;
+}
diff --git a/lldb/source/Commands/CommandObjectFrame.cpp b/lldb/source/Commands/CommandObjectFrame.cpp
index 1390fd8748dfaf6..2149d0f2f98da3d 100644
--- a/lldb/source/Commands/CommandObjectFrame.cpp
+++ b/lldb/source/Commands/CommandObjectFrame.cpp
@@ -495,6 +495,8 @@ may even involve JITing and running code in the target program.)");
case eValueTypeRegisterSet:
case eValueTypeConstResult:
case eValueTypeVariableThreadLocal:
+ case eValueTypeVTable:
+ case eValueTypeVTableEntry:
return false;
}
}
diff --git a/lldb/source/Core/CMakeLists.txt b/lldb/source/Core/CMakeLists.txt
index d7b4f2587a98bf9..b23cc5d2eaf3156 100644
--- a/lldb/source/Core/CMakeLists.txt
+++ b/lldb/source/Core/CMakeLists.txt
@@ -71,6 +71,7 @@ add_lldb_library(lldbCore
ValueObjectSyntheticFilter.cpp
ValueObjectUpdater.cpp
ValueObjectVariable.cpp
+ ValueObjectVTable.cpp
DEPENDS
clang-tablegen-targets
diff --git a/lldb/source/Core/ValueObject.cpp b/lldb/source/Core/ValueObject.cpp
index 3e9116f2d922933..aa2a35bf2b2dc5f 100644
--- a/lldb/source/Core/ValueObject.cpp
+++ b/lldb/source/Core/ValueObject.cpp
@@ -17,6 +17,7 @@
#include "lldb/Core/ValueObjectDynamicValue.h"
#include "lldb/Core/ValueObjectMemory.h"
#include "lldb/Core/ValueObjectSyntheticFilter.h"
+#include "lldb/Core/ValueObjectVTable.h"
#include "lldb/DataFormatters/DataVisualization.h"
#include "lldb/DataFormatters/DumpValueObjectOptions.h"
#include "lldb/DataFormatters/FormatManager.h"
@@ -3155,3 +3156,7 @@ ValueObjectSP ValueObject::Persist() {
return persistent_var_sp->GetValueObject();
}
+
+lldb::ValueObjectSP ValueObject::GetVTable() {
+ return ValueObjectVTable::Create(*this);
+}
diff --git a/lldb/source/Core/ValueObjectVTable.cpp b/lldb/source/Core/ValueObjectVTable.cpp
new file mode 100644
index 000000000000000..31c619986b825f1
--- /dev/null
+++ b/lldb/source/Core/ValueObjectVTable.cpp
@@ -0,0 +1,325 @@
+//===-- 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/lldb-defines.h"
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-forward.h"
+#include "lldb/lldb-private-enumerations.h"
+#include "clang/Tooling/Transformer/RangeSelector.h"
+#include "llvm/Support/MathExtras.h"
+#include <cstdint>
+
+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 {
+ ValueObject *parent = GetParent();
+ if (parent)
+ return parent->IsInScope();
+ return false;
+ };
+
+protected:
+ bool UpdateValue() override {
+ SetValueIsValid(false);
+ m_value.Clear();
+ ValueObject *parent = GetParent();
+ if (!parent) {
+ m_error.SetErrorString("no parent object");
+ return false;
+ }
+
+ addr_t parent_addr = parent->GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
+ if (parent_addr == LLDB_INVALID_ADDRESS) {
+ m_error.SetErrorString("parent has invalid 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;
+ }
+
+ Address resolved_vfunc_ptr_address;
+ target_sp->ResolveLoadAddress(vfunc_ptr, resolved_vfunc_ptr_address);
+ if (!resolved_vfunc_ptr_address.IsValid()) {
+ m_error.SetErrorStringWithFormat(
+ "unable to resolve func ptr address: 0x%16.16" PRIx64, vfunc_ptr);
+ 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 =
+ resolved_vfunc_ptr_address.CalculateSymbolContextFunction();
+ if (function) {
+ m_value.SetCompilerType(function->GetCompilerType());
+ } 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.
+ auto type_system_or_err =
+ target_sp->GetScratchTypeSystemForLanguage(eLanguageTypeC_plus_plus);
+ if (type_system_or_err) {
+ CompilerType proto = (*type_system_or_err)->CreateGenericFunctionPrototype();
+ if (proto.IsFunctionType())
+ m_value.SetCompilerType(proto);
+ } else {
+ consumeError(type_system_or_err.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();
+ else
+ 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;
+ }
+
+ // Check to make sure the class has a vtable.
+
+ // If we have a pointer or reference type, get the pointee type from this
+ // so we can ask if it has virtual functions below.
+ CompilerType value_type = parent->GetCompilerType();
+ if (value_type.IsPointerOrReferenceType()) {
+ CompilerType pointee_type = value_type.GetPointeeType();
+ if (pointee_type)
+ value_type = pointee_type;
+ }
+
+ // Make sure this is a class or a struct first by checking the type class
+ // bitfield that gets returned.
+ if ((value_type.GetTypeClass() & (eTypeClassStruct | eTypeClassClass)) == 0) {
+ m_error.SetErrorStringWithFormat("type \"%s\" is not a class or struct",
+ value_type.GetTypeName().AsCString("<invalid>"));
+ return false;
+ }
+
+ // Check if the type has virtual functions by asking it if it is polymorphic.
+ if (!value_type.IsPolymorphicClass()) {
+ m_error.SetErrorStringWithFormat("type \"%s\" doesn't have a vtable",
+ value_type.GetTypeName().AsCString("<invalid>"));
+ return false;
+ }
+
+ TargetSP target_sp = GetTargetSP();
+ if (!target_sp) {
+ m_error.SetErrorString("no target");
+ return false;
+ }
+
+ // Get the address of our parent. This will update the parent value object
+ // if needed and fetch the address of the parent.
+ AddressType addr_type;
+ addr_t parent_load_addr =
+ parent->GetAddressOf(/*scalar_is_load_address=*/true, &addr_type);
+ if (addr_type == eAddressTypeFile) {
+ ModuleSP module_sp(parent->GetModule());
+ if (!module_sp) {
+ parent_load_addr = LLDB_INVALID_ADDRESS;
+ } else {
+ Address addr;
+ module_sp->ResolveFileAddress(parent_load_addr, addr);
+ parent_load_addr = addr.GetLoadAddress(target_sp.get());
+ }
+ } else if (addr_type == eAddressTypeHost ||
+ addr_type == eAddressTypeInvalid) {
+ parent_load_addr = LLDB_INVALID_ADDRESS;
+ }
+
+ if (parent_load_addr == LLDB_INVALID_ADDRESS) {
+ m_error.SetErrorString("parent is not in memory");
+ return false;
+ }
+
+ m_value.Clear();
+
+ ProcessSP process_sp = GetProcessSP();
+ if (!process_sp) {
+ m_error.SetErrorString("no process");
+ return false;
+ }
+
+ // We expect to find the vtable at the first block of memory.
+ addr_t possible_vtable_ptr =
+ process_sp->ReadPointerFromMemory(parent_load_addr, m_error);
+ if (m_error.Fail())
+ return false;
+
+ Address resolved_possible_vtable_address;
+ target_sp->ResolveLoadAddress(possible_vtable_ptr,
+ resolved_possible_vtable_address);
+ if (!resolved_possible_vtable_address.IsValid()) {
+ m_error.SetErrorStringWithFormat(
+ "unable to resolve 0x%" PRIx64 " to a section for vtable "
+ "symbol search", possible_vtable_ptr);
+ return false;
+ }
+
+ m_vtable_symbol =
+ resolved_possible_vtable_address.CalculateSymbolContextSymbol();
+ if (!(m_vtable_symbol &&
+ m_vtable_symbol->GetName().GetStringRef().startswith("vtable for "))) {
+ m_error.SetErrorStringWithFormat(
+ "no vtable symbol found containing 0x%" PRIx64, possible_vtable_ptr);
+ return false;
+ }
+
+ // Now that we know it's a vtable, we update the object's state.
+ SetName(GetTypeName());
+
+ // Calculate the number of entries
+ assert(m_vtable_symbol->GetByteSizeIsValid());
+ m_addr_size = process_sp->GetAddressByteSize();
+ addr_t symbol_end_addr = m_vtable_symbol->GetLoadAddress(target_sp.get()) +
+ m_vtable_symbol->GetByteSize();
+ m_num_vtable_entries = (symbol_end_addr - possible_vtable_ptr) / m_addr_size;
+
+ m_value.SetValueType(Value::ValueType::LoadAddress);
+ m_value.GetScalar() = parent_load_addr;
+ 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;
diff --git a/lldb/source/DataFormatters/CXXFunctionPointer.cpp b/lldb/source/DataFormatters/CXXFunctionPointer.cpp
index d7df280e56efb0d..6543433d17ff45f 100644
--- a/lldb/source/DataFormatters/CXXFunctionPointer.cpp
+++ b/lldb/source/DataFormatters/CXXFunctionPointer.cpp
@@ -13,6 +13,7 @@
#include "lldb/Target/SectionLoadList.h"
#include "lldb/Target/Target.h"
#include "lldb/Utility/Stream.h"
+#include "lldb/lldb-enumerations.h"
#include <string>
@@ -76,7 +77,10 @@ bool lldb_private::formatters::CXXFunctionPointerSummaryProvider(
}
}
if (sstr.GetSize() > 0) {
- stream.Printf("(%s)", sstr.GetData());
+ if (valobj.GetValueType() == lldb::eValueTypeVTableEntry)
+ stream.PutCString(sstr.GetData());
+ else
+ stream.Printf("(%s)", sstr.GetData());
return true;
} else
return false;
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
index 3d709e3d6759556..147088edeb15b8d 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
@@ -45,6 +45,7 @@
#include "LibCxxVariant.h"
#include "LibStdcpp.h"
#include "MSVCUndecoratedNameParser.h"
+#include "lldb/lldb-enumerations.h"
using namespace lldb;
using namespace lldb_private;
@@ -1356,7 +1357,8 @@ CPlusPlusLanguage::GetHardcodedSummaries() {
lldb_private::formatters::CXXFunctionPointerSummaryProvider,
"Function pointer summary provider"));
if (CompilerType CT = valobj.GetCompilerType();
- CT.IsFunctionPointerType() || CT.IsMemberFunctionPointerType()) {
+ CT.IsFunctionPointerType() || CT.IsMemberFunctionPointerType() ||
+ valobj.GetValueType() == lldb::eValueTypeVTableEntry) {
return formatter_sp;
}
return nullptr;
diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
index 69cff0f35ae4ab2..37ba8b0f6e2afcf 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
@@ -4700,6 +4700,21 @@ TypeSystemClang::GetTypedefedType(lldb::opaque_compiler_type_t type) {
CompilerType TypeSystemClang::GetBasicTypeFromAST(lldb::BasicType basic_type) {
return TypeSystemClang::GetBasicType(basic_type);
}
+
+CompilerType TypeSystemClang::CreateGenericFunctionPrototype() {
+ clang::ASTContext &ast = getASTContext();
+ const FunctionType::ExtInfo generic_ext_info(
+ /*noReturn=*/false,
+ /*hasRegParm=*/false,
+ /*regParm=*/0,
+ CallingConv::CC_C,
+ /*producesResult=*/false,
+ /*noCallerSavedRegs=*/false,
+ /*NoCfCheck=*/false,
+ /*cmseNSCall=*/false);
+ QualType func_type = ast.getFunctionNoProtoType(ast.VoidTy, generic_ext_info);
+ return GetType(func_type);
+}
// Exploring the type
const llvm::fltSemantics &
@@ -4733,6 +4748,15 @@ TypeSystemClang::GetBitSize(lldb::opaque_compiler_type_t type,
return std::nullopt;
break;
+ case clang::Type::FunctionProto:
+ case clang::Type::FunctionNoProto: {
+ ExecutionContext exe_ctx(exe_scope);
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process)
+ return process->GetAddressByteSize() * 8;
+ break;
+ }
+
case clang::Type::ObjCInterface:
case clang::Type::ObjCObject: {
ExecutionContext exe_ctx(exe_scope);
@@ -4816,7 +4840,7 @@ lldb::Encoding TypeSystemClang::GetEncoding(lldb::opaque_compiler_type_t type,
case clang::Type::FunctionNoProto:
case clang::Type::FunctionProto:
- break;
+ return lldb::eEncodingUint;
case clang::Type::IncompleteArray:
case clang::Type::VariableArray:
diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
index 0544de3cd33befb..baf19156e41bba5 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
@@ -800,6 +800,10 @@ class TypeSystemClang : public TypeSystem {
// Create related types using the current type's AST
CompilerType GetBasicTypeFromAST(lldb::BasicType basic_type) override;
+ // Create a generic function prototype that can be used in ValuObject types
+ // to correctly display a function pointer with the right value and summary.
+ CompilerType CreateGenericFunctionPrototype() override;
+
// Exploring the type
const llvm::fltSemantics &GetFloatTypeSemantics(size_t byte_size) override;
diff --git a/lldb/test/API/functionalities/vtable/Makefile b/lldb/test/API/functionalities/vtable/Makefile
new file mode 100644
index 000000000000000..99998b20bcb0502
--- /dev/null
+++ b/lldb/test/API/functionalities/vtable/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
diff --git a/lldb/test/API/functionalities/vtable/TestVTableValue.py b/lldb/test/API/functionalities/vtable/TestVTableValue.py
new file mode 100644
index 000000000000000..ce3da0a2185f501
--- /dev/null
+++ b/lldb/test/API/functionalities/vtable/TestVTableValue.py
@@ -0,0 +1,135 @@
+"""
+Make sure the getting a variable path works and doesn't crash.
+"""
+
+
+import lldb
+import lldbsuite.test.lldbutil as lldbutil
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+
+class TestVTableValue(TestBase):
+ # If your test case doesn't stress debug info, then
+ # set this to true. That way it won't be run once for
+ # each debug info format.
+ NO_DEBUG_INFO_TESTCASE = True
+
+ @skipUnlessPlatform(["linux", "macosx"])
+ def test_vtable(self):
+ self.build()
+ lldbutil.run_to_source_breakpoint(
+ self, "At the end", lldb.SBFileSpec("main.cpp")
+ )
+
+ shape = self.frame().FindVariable("shape")
+ vtable = shape.GetVTable()
+ self.assertEquals(vtable.GetName(), "vtable for Shape")
+ self.assertEquals(vtable.GetTypeName(), "vtable for Shape")
+ # Make sure we have the right number of virtual functions in our vtable
+ # for the shape class.
+ self.assertEquals(vtable.GetNumChildren(), 4)
+
+ # Verify vtable address
+ vtable_addr = vtable.GetValueAsUnsigned(0)
+ expected_addr = self.expected_vtable_addr(shape)
+ self.assertEquals(vtable_addr, expected_addr)
+
+ for (idx, vtable_entry) in enumerate(vtable.children):
+ self.verify_vtable_entry(vtable_entry, vtable_addr, idx)
+
+ rect = self.frame().FindVariable("rect")
+ vtable = rect.GetVTable()
+ self.assertEquals(vtable.GetName(), "vtable for Rectangle")
+ self.assertEquals(vtable.GetTypeName(), "vtable for Rectangle")
+
+ # Make sure we have the right number of virtual functions in our vtable
+ # with the extra virtual function added by the Rectangle class
+ self.assertEquals(vtable.GetNumChildren(), 5)
+
+ # Verify vtable address
+ vtable_addr = vtable.GetValueAsUnsigned()
+ expected_addr = self.expected_vtable_addr(rect)
+ self.assertEquals(vtable_addr, expected_addr)
+
+ for (idx, vtable_entry) in enumerate(vtable.children):
+ self.verify_vtable_entry(vtable_entry, vtable_addr, idx)
+
+ @skipUnlessPlatform(["linux", "macosx"])
+ def test_base_class_ptr(self):
+ self.build()
+ (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
+ self, "Shape is Rectangle", lldb.SBFileSpec("main.cpp")
+ )
+
+ shape = self.frame().FindVariable("shape")
+ rect = self.frame().FindVariable("rect")
+
+ shape_ptr = self.frame().FindVariable("shape_ptr")
+ shape_ptr_vtable = shape_ptr.GetVTable()
+ self.assertEquals(shape_ptr_vtable.GetName(), "vtable for Rectangle")
+ self.assertEquals(shape_ptr_vtable.GetNumChildren(), 5)
+ self.assertEquals(shape_ptr.GetValueAsUnsigned(0),
+ rect.GetLoadAddress())
+ lldbutil.continue_to_source_breakpoint(
+ self, process, "Shape is Shape", lldb.SBFileSpec("main.cpp")
+ )
+ self.assertEquals(shape_ptr.GetValueAsUnsigned(0),
+ shape.GetLoadAddress())
+ self.assertEquals(shape_ptr_vtable.GetNumChildren(), 4)
+ self.assertEquals(shape_ptr_vtable.GetName(), "vtable for Shape")
+
+ @skipUnlessPlatform(["linux", "macosx"])
+ def test_no_vtable(self):
+ self.build()
+ lldbutil.run_to_source_breakpoint(
+ self, "At the end", lldb.SBFileSpec("main.cpp")
+ )
+
+ var = self.frame().FindVariable("not_virtual")
+ self.assertEqual(var.GetVTable().GetError().GetCString(),
+ 'type "NotVirtual" doesn\'t have a vtable')
+
+ var = self.frame().FindVariable("argc")
+ self.assertEqual(var.GetVTable().GetError().GetCString(),
+ 'type "int" is not a class or struct')
+
+ def expected_vtable_addr(self, var: lldb.SBValue) -> int:
+ load_addr = var.GetLoadAddress()
+ read_from_memory_error = lldb.SBError()
+ vtable_addr = self.process().ReadPointerFromMemory(
+ load_addr, read_from_memory_error
+ )
+ self.assertTrue(read_from_memory_error.Success())
+ return vtable_addr
+
+ def expected_vtable_entry_func_ptr(self, vtable_addr: int, idx: int):
+ vtable_entry_addr = vtable_addr + idx * self.process().GetAddressByteSize()
+ read_func_ptr_error = lldb.SBError()
+ func_ptr = self.process().ReadPointerFromMemory(vtable_entry_addr,
+ read_func_ptr_error)
+ self.assertTrue(read_func_ptr_error.Success())
+ return func_ptr
+
+ def verify_vtable_entry(self, vtable_entry: lldb.SBValue, vtable_addr: int,
+ idx: int):
+ """Verify the vtable entry looks something like:
+
+ (double ()) [0] = 0x0000000100003a10 a.out`Rectangle::Area() at main.cpp:14
+
+ """
+ # Check function ptr
+ vtable_entry_func_ptr = vtable_entry.GetValueAsUnsigned(0)
+ self.assertEquals(
+ vtable_entry_func_ptr,
+ self.expected_vtable_entry_func_ptr(vtable_addr, idx),
+ )
+
+ sb_addr = self.target().ResolveLoadAddress(vtable_entry_func_ptr)
+ sym_ctx = sb_addr.GetSymbolContext(lldb.eSymbolContextEverything)
+
+ # Make sure the type is the same as the function type
+ self.assertEquals(vtable_entry.GetType(), sym_ctx.GetFunction().GetType())
+
+ # The summary should be the address description of the function pointer
+ summary = vtable_entry.GetSummary()
+ self.assertEquals(str(sb_addr), summary)
diff --git a/lldb/test/API/functionalities/vtable/main.cpp b/lldb/test/API/functionalities/vtable/main.cpp
new file mode 100644
index 000000000000000..7113eff343d43f8
--- /dev/null
+++ b/lldb/test/API/functionalities/vtable/main.cpp
@@ -0,0 +1,37 @@
+class Shape {
+public:
+ virtual double Area() { return 1.0; }
+ virtual double Perimeter() { return 1.0; }
+ // Note that destructors generate two entries in the vtable: base object
+ // destructor and deleting destructor.
+ virtual ~Shape() = default;
+};
+
+class Rectangle : public Shape {
+public:
+ ~Rectangle() override = default;
+ double Area() override { return 2.0; }
+ double Perimeter() override { return 2.0; }
+ virtual void RectangleOnly() {}
+ // This *shouldn't* show up in the vtable.
+ void RectangleSpecific() { return; }
+};
+
+// Make a class that looks like it would be virtual because the first ivar is
+// a virtual class and if we inspect memory at the address of this class it
+// would appear to be a virtual class. We need to make sure we don't get a
+// valid vtable from this object.
+class NotVirtual {
+ Rectangle m_rect;
+public:
+ NotVirtual() = default;
+};
+
+int main(int argc, const char **argv) {
+ Shape shape;
+ Rectangle rect;
+ Shape *shape_ptr = ▭
+ shape_ptr = &shape; // Shape is Rectangle
+ NotVirtual not_virtual; // Shape is Shape
+ return 0; // At the end
+}
>From 9d3e2750b7ee8df38055f2f251d8558d9570f1bc Mon Sep 17 00:00:00 2001
From: Greg Clayton <gclayton at fb.com>
Date: Thu, 28 Sep 2023 11:14:28 -0700
Subject: [PATCH 2/3] Use LanguageRuntime to extract the vtable information by
language.
Also revert changes that allowed a bit size for function prototypes as it was affecting the test suite. We now create function prototype pointers as the type of each vtable child instead of just a function prototype (no pointer).
---
lldb/include/lldb/API/SBValue.h | 12 +-
lldb/include/lldb/Core/ValueObjectVTable.h | 2 +-
lldb/include/lldb/Symbol/Type.h | 2 +
lldb/include/lldb/Target/LanguageRuntime.h | 12 +
lldb/source/Core/ValueObjectVTable.cpp | 123 +++----
.../ItaniumABI/ItaniumABILanguageRuntime.cpp | 335 ++++++++++--------
.../ItaniumABI/ItaniumABILanguageRuntime.h | 17 +-
.../TypeSystem/Clang/TypeSystemClang.cpp | 9 -
lldb/source/Symbol/Type.cpp | 4 +
.../functionalities/vtable/TestVTableValue.py | 25 +-
lldb/test/API/functionalities/vtable/main.cpp | 1 +
11 files changed, 298 insertions(+), 244 deletions(-)
diff --git a/lldb/include/lldb/API/SBValue.h b/lldb/include/lldb/API/SBValue.h
index 333bdf1738eecaf..5e6e95e67a796b9 100644
--- a/lldb/include/lldb/API/SBValue.h
+++ b/lldb/include/lldb/API/SBValue.h
@@ -382,12 +382,18 @@ class LLDB_API SBValue {
/// object isn't a C++ class with a vtable or not a C++ class.
///
/// SBValue::GetName() will be the demangled symbol name for the virtual
- /// function table like "vtable for Baseclass".
+ /// function table like "vtable for <classname>".
///
/// SBValue::GetValue() will be the address of the first vtable entry if the
/// current SBValue is a class with a vtable, or nothing the current SBValue
/// is not a C++ class or not a C++ class that has a vtable.
///
+ /// SBValue::GetValueAtUnsigned(...) will return the address of the first
+ /// vtable entry.
+ ///
+ /// SBValue::GetLoadAddress() will return the address of the vtable pointer
+ /// found in the parent SBValue.
+ ///
/// SBValue::GetSummary() will contain the number of virtual function pointers
/// in the vtable like is done for arrays.
///
@@ -398,8 +404,8 @@ class LLDB_API SBValue {
/// as a SBValue object. The child SBValue objects name will be the array
/// index, value will be the virtual function pointer, summary will be the
/// symbolicated address description, and if the the adress resolves to a
- /// function in debug info, the child type will be the function prototype as
- /// a SBType object.
+ /// function in debug info, the child type will be a pointer to the function
+ /// prototype.
lldb::SBValue GetVTable();
protected:
diff --git a/lldb/include/lldb/Core/ValueObjectVTable.h b/lldb/include/lldb/Core/ValueObjectVTable.h
index faa0af238e764f4..6aaa40134240bdb 100644
--- a/lldb/include/lldb/Core/ValueObjectVTable.h
+++ b/lldb/include/lldb/Core/ValueObjectVTable.h
@@ -45,7 +45,7 @@ class ValueObjectVTable : public ValueObject {
CompilerType GetCompilerTypeImpl() override;
/// The symbol for the C++ virtual function table.
- Symbol *m_vtable_symbol = nullptr;
+ const Symbol *m_vtable_symbol = nullptr;
/// Cache the number of vtable children when we update the value.
uint32_t m_num_vtable_entries = 0;
/// Cache the address size in bytes to avoid checking with the process to
diff --git a/lldb/include/lldb/Symbol/Type.h b/lldb/include/lldb/Symbol/Type.h
index 046501931d211a7..c52eb5d6af0b662 100644
--- a/lldb/include/lldb/Symbol/Type.h
+++ b/lldb/include/lldb/Symbol/Type.h
@@ -427,6 +427,8 @@ class TypeAndOrName {
void SetName(const char *type_name_cstr);
+ void SetName(llvm::StringRef name);
+
void SetTypeSP(lldb::TypeSP type_sp);
void SetCompilerType(CompilerType compiler_type);
diff --git a/lldb/include/lldb/Target/LanguageRuntime.h b/lldb/include/lldb/Target/LanguageRuntime.h
index eff79a0bf0d0622..fd25476036640db 100644
--- a/lldb/include/lldb/Target/LanguageRuntime.h
+++ b/lldb/include/lldb/Target/LanguageRuntime.h
@@ -78,6 +78,18 @@ class LanguageRuntime : public Runtime, public PluginInterface {
virtual bool GetObjectDescription(Stream &str, Value &value,
ExecutionContextScope *exe_scope) = 0;
+ struct VTableInfo {
+ Address addr; /// Address of the vtable's virtual function table
+ Symbol *symbol; /// The vtable symbol from the symbol table
+ };
+ // Get the vtable information for a given value.
+ //
+ // If the value doesn't represent something that has a vtable, then return
+ // std::nullopt. Else return the a valid VTableInfo structure.
+ virtual std::optional<VTableInfo> GetVTableInfo(ValueObject &in_value) {
+ return std::nullopt;
+ }
+
// this call should return true if it could set the name and/or the type
virtual bool GetDynamicTypeAndAddress(ValueObject &in_value,
lldb::DynamicValueType use_dynamic,
diff --git a/lldb/source/Core/ValueObjectVTable.cpp b/lldb/source/Core/ValueObjectVTable.cpp
index 31c619986b825f1..6d14571dcfb57f1 100644
--- a/lldb/source/Core/ValueObjectVTable.cpp
+++ b/lldb/source/Core/ValueObjectVTable.cpp
@@ -10,13 +10,12 @@
#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"
-#include "clang/Tooling/Transformer/RangeSelector.h"
-#include "llvm/Support/MathExtras.h"
-#include <cstdint>
using namespace lldb;
using namespace lldb_private;
@@ -84,13 +83,6 @@ class ValueObjectVTableChild : public ValueObject {
return false;
}
- Address resolved_vfunc_ptr_address;
- target_sp->ResolveLoadAddress(vfunc_ptr, resolved_vfunc_ptr_address);
- if (!resolved_vfunc_ptr_address.IsValid()) {
- m_error.SetErrorStringWithFormat(
- "unable to resolve func ptr address: 0x%16.16" PRIx64, vfunc_ptr);
- 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.
@@ -100,22 +92,28 @@ class ValueObjectVTableChild : public ValueObject {
// 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 =
- resolved_vfunc_ptr_address.CalculateSymbolContextFunction();
+ 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());
+ 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.
- auto type_system_or_err =
- target_sp->GetScratchTypeSystemForLanguage(eLanguageTypeC_plus_plus);
- if (type_system_or_err) {
- CompilerType proto = (*type_system_or_err)->CreateGenericFunctionPrototype();
- if (proto.IsFunctionType())
- m_value.SetCompilerType(proto);
+
+ // 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_or_err.takeError());
+ consumeError(type_system.takeError());
}
}
@@ -206,7 +204,7 @@ bool ValueObjectVTable::UpdateValue() {
return false;
}
- // Check to make sure the class has a vtable.
+ // Check to make sure the class has a vtable.
// If we have a pointer or reference type, get the pointee type from this
// so we can ask if it has virtual functions below.
@@ -232,66 +230,36 @@ bool ValueObjectVTable::UpdateValue() {
return false;
}
- TargetSP target_sp = GetTargetSP();
- if (!target_sp) {
- m_error.SetErrorString("no target");
- return false;
- }
-
- // Get the address of our parent. This will update the parent value object
- // if needed and fetch the address of the parent.
- AddressType addr_type;
- addr_t parent_load_addr =
- parent->GetAddressOf(/*scalar_is_load_address=*/true, &addr_type);
- if (addr_type == eAddressTypeFile) {
- ModuleSP module_sp(parent->GetModule());
- if (!module_sp) {
- parent_load_addr = LLDB_INVALID_ADDRESS;
- } else {
- Address addr;
- module_sp->ResolveFileAddress(parent_load_addr, addr);
- parent_load_addr = addr.GetLoadAddress(target_sp.get());
- }
- } else if (addr_type == eAddressTypeHost ||
- addr_type == eAddressTypeInvalid) {
- parent_load_addr = LLDB_INVALID_ADDRESS;
- }
-
- if (parent_load_addr == LLDB_INVALID_ADDRESS) {
- m_error.SetErrorString("parent is not in memory");
- return false;
- }
-
- m_value.Clear();
-
ProcessSP process_sp = GetProcessSP();
if (!process_sp) {
m_error.SetErrorString("no process");
return false;
}
- // We expect to find the vtable at the first block of memory.
- addr_t possible_vtable_ptr =
- process_sp->ReadPointerFromMemory(parent_load_addr, m_error);
- if (m_error.Fail())
+ const LanguageType language = parent->GetObjectRuntimeLanguage();
+ LanguageRuntime *language_runtime = process_sp->GetLanguageRuntime(language);
+
+ if (language_runtime == nullptr) {
+ m_error.SetErrorString("value doesn't have a vtable");
return false;
+ }
- Address resolved_possible_vtable_address;
- target_sp->ResolveLoadAddress(possible_vtable_ptr,
- resolved_possible_vtable_address);
- if (!resolved_possible_vtable_address.IsValid()) {
- m_error.SetErrorStringWithFormat(
- "unable to resolve 0x%" PRIx64 " to a section for vtable "
- "symbol search", possible_vtable_ptr);
+ // Get the vtable information from the language runtime.
+ std::optional<LanguageRuntime::VTableInfo> opt_vtable_info =
+ language_runtime->GetVTableInfo(*parent);
+ if (!opt_vtable_info) {
+ m_error.SetErrorString("value doesn't have a vtable");
return false;
}
- m_vtable_symbol =
- resolved_possible_vtable_address.CalculateSymbolContextSymbol();
- if (!(m_vtable_symbol &&
- m_vtable_symbol->GetName().GetStringRef().startswith("vtable for "))) {
+ TargetSP target_sp = GetTargetSP();
+ const addr_t vtable_start_addr =
+ opt_vtable_info->addr.GetLoadAddress(target_sp.get());
+
+ m_vtable_symbol = opt_vtable_info->symbol;
+ if (!m_vtable_symbol) {
m_error.SetErrorStringWithFormat(
- "no vtable symbol found containing 0x%" PRIx64, possible_vtable_ptr);
+ "no vtable symbol found containing 0x%" PRIx64, vtable_start_addr);
return false;
}
@@ -299,14 +267,21 @@ bool ValueObjectVTable::UpdateValue() {
SetName(GetTypeName());
// Calculate the number of entries
- assert(m_vtable_symbol->GetByteSizeIsValid());
+ 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();
- addr_t symbol_end_addr = m_vtable_symbol->GetLoadAddress(target_sp.get()) +
- m_vtable_symbol->GetByteSize();
- m_num_vtable_entries = (symbol_end_addr - possible_vtable_ptr) / m_addr_size;
+ 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_load_addr;
+ m_value.GetScalar() = parent->GetAddressOf();
auto type_system_or_err =
target_sp->GetScratchTypeSystemForLanguage(eLanguageTypeC_plus_plus);
if (type_system_or_err) {
diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp
index 711a696ff9b4d67..a9ab789703ac155 100644
--- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp
+++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp
@@ -54,134 +54,182 @@ bool ItaniumABILanguageRuntime::CouldHaveDynamicValue(ValueObject &in_value) {
check_objc);
}
-TypeAndOrName ItaniumABILanguageRuntime::GetTypeInfoFromVTableAddress(
- ValueObject &in_value, lldb::addr_t original_ptr,
- lldb::addr_t vtable_load_addr) {
- if (m_process && vtable_load_addr != LLDB_INVALID_ADDRESS) {
- // Find the symbol that contains the "vtable_load_addr" address
- Address vtable_addr;
- Target &target = m_process->GetTarget();
- if (!target.GetSectionLoadList().IsEmpty()) {
- if (target.GetSectionLoadList().ResolveLoadAddress(vtable_load_addr,
- vtable_addr)) {
- // See if we have cached info for this type already
- TypeAndOrName type_info = GetDynamicTypeInfo(vtable_addr);
- if (type_info)
- return type_info;
-
- SymbolContext sc;
- target.GetImages().ResolveSymbolContextForAddress(
- vtable_addr, eSymbolContextSymbol, sc);
- Symbol *symbol = sc.symbol;
- if (symbol != nullptr) {
- const char *name =
- symbol->GetMangled().GetDemangledName().AsCString();
- if (name && strstr(name, vtable_demangled_prefix) == name) {
- Log *log = GetLog(LLDBLog::Object);
- LLDB_LOGF(log,
- "0x%16.16" PRIx64
- ": static-type = '%s' has vtable symbol '%s'\n",
- original_ptr, in_value.GetTypeName().GetCString(), name);
- // We are a C++ class, that's good. Get the class name and look it
- // up:
- const char *class_name = name + strlen(vtable_demangled_prefix);
- // We know the class name is absolute, so tell FindTypes that by
- // prefixing it with the root namespace:
- std::string lookup_name("::");
- lookup_name.append(class_name);
-
- type_info.SetName(class_name);
- const bool exact_match = true;
- TypeList class_types;
-
- // First look in the module that the vtable symbol came from and
- // look for a single exact match.
- llvm::DenseSet<SymbolFile *> searched_symbol_files;
- if (sc.module_sp)
- sc.module_sp->FindTypes(ConstString(lookup_name), exact_match, 1,
+TypeAndOrName ItaniumABILanguageRuntime::GetTypeInfo(
+ ValueObject &in_value, const VTableInfo &vtable_info) {
+ if (vtable_info.addr.IsSectionOffset()) {
+ // See if we have cached info for this type already
+ TypeAndOrName type_info = GetDynamicTypeInfo(vtable_info.addr);
+ if (type_info)
+ return type_info;
+
+ if (vtable_info.symbol) {
+ Log *log = GetLog(LLDBLog::Object);
+ llvm::StringRef symbol_name =
+ vtable_info.symbol->GetMangled().GetDemangledName().GetStringRef();
+ LLDB_LOGF(log,
+ "0x%16.16" PRIx64
+ ": static-type = '%s' has vtable symbol '%s'\n",
+ in_value.GetPointerValue(),
+ in_value.GetTypeName().GetCString(),
+ symbol_name.str().c_str());
+ // We are a C++ class, that's good. Get the class name and look it
+ // up:
+ llvm::StringRef class_name = symbol_name;
+ class_name.consume_front(vtable_demangled_prefix);
+ // We know the class name is absolute, so tell FindTypes that by
+ // prefixing it with the root namespace:
+ std::string lookup_name("::");
+ lookup_name.append(class_name.data(), class_name.size());
+
+ type_info.SetName(class_name);
+ const bool exact_match = true;
+ TypeList class_types;
+
+ // First look in the module that the vtable symbol came from and
+ // look for a single exact match.
+ llvm::DenseSet<SymbolFile *> searched_symbol_files;
+ ModuleSP module_sp = vtable_info.symbol->CalculateSymbolContextModule();
+ if (module_sp)
+ module_sp->FindTypes(ConstString(lookup_name), exact_match, 1,
+ searched_symbol_files, class_types);
+
+ // If we didn't find a symbol, then move on to the entire module
+ // list in the target and get as many unique matches as possible
+ Target &target = m_process->GetTarget();
+ if (class_types.Empty())
+ target.GetImages().FindTypes(nullptr, ConstString(lookup_name),
+ exact_match, UINT32_MAX,
searched_symbol_files, class_types);
- // If we didn't find a symbol, then move on to the entire module
- // list in the target and get as many unique matches as possible
- if (class_types.Empty())
- target.GetImages().FindTypes(nullptr, ConstString(lookup_name),
- exact_match, UINT32_MAX,
- searched_symbol_files, class_types);
-
- lldb::TypeSP type_sp;
- if (class_types.Empty()) {
- LLDB_LOGF(log, "0x%16.16" PRIx64 ": is not dynamic\n",
- original_ptr);
- return TypeAndOrName();
+ lldb::TypeSP type_sp;
+ if (class_types.Empty()) {
+ LLDB_LOGF(log, "0x%16.16" PRIx64 ": is not dynamic\n",
+ in_value.GetPointerValue());
+ return TypeAndOrName();
+ }
+ if (class_types.GetSize() == 1) {
+ type_sp = class_types.GetTypeAtIndex(0);
+ if (type_sp) {
+ if (TypeSystemClang::IsCXXClassType(
+ type_sp->GetForwardCompilerType())) {
+ LLDB_LOGF(
+ log,
+ "0x%16.16" PRIx64
+ ": static-type = '%s' has dynamic type: uid={0x%" PRIx64
+ "}, type-name='%s'\n",
+ in_value.GetPointerValue(), in_value.GetTypeName().AsCString(),
+ type_sp->GetID(), type_sp->GetName().GetCString());
+ type_info.SetTypeSP(type_sp);
+ }
+ }
+ } else {
+ size_t i;
+ if (log) {
+ for (i = 0; i < class_types.GetSize(); i++) {
+ type_sp = class_types.GetTypeAtIndex(i);
+ if (type_sp) {
+ LLDB_LOGF(
+ log,
+ "0x%16.16" PRIx64
+ ": static-type = '%s' has multiple matching dynamic "
+ "types: uid={0x%" PRIx64 "}, type-name='%s'\n",
+ in_value.GetPointerValue(),
+ in_value.GetTypeName().AsCString(),
+ type_sp->GetID(), type_sp->GetName().GetCString());
}
- if (class_types.GetSize() == 1) {
- type_sp = class_types.GetTypeAtIndex(0);
- if (type_sp) {
- if (TypeSystemClang::IsCXXClassType(
- type_sp->GetForwardCompilerType())) {
- LLDB_LOGF(
- log,
- "0x%16.16" PRIx64
- ": static-type = '%s' has dynamic type: uid={0x%" PRIx64
- "}, type-name='%s'\n",
- original_ptr, in_value.GetTypeName().AsCString(),
- type_sp->GetID(), type_sp->GetName().GetCString());
- type_info.SetTypeSP(type_sp);
- }
- }
- } else {
- size_t i;
- if (log) {
- for (i = 0; i < class_types.GetSize(); i++) {
- type_sp = class_types.GetTypeAtIndex(i);
- if (type_sp) {
- LLDB_LOGF(
- log,
- "0x%16.16" PRIx64
- ": static-type = '%s' has multiple matching dynamic "
- "types: uid={0x%" PRIx64 "}, type-name='%s'\n",
- original_ptr, in_value.GetTypeName().AsCString(),
- type_sp->GetID(), type_sp->GetName().GetCString());
- }
- }
- }
-
- for (i = 0; i < class_types.GetSize(); i++) {
- type_sp = class_types.GetTypeAtIndex(i);
- if (type_sp) {
- if (TypeSystemClang::IsCXXClassType(
- type_sp->GetForwardCompilerType())) {
- LLDB_LOGF(
- log,
- "0x%16.16" PRIx64 ": static-type = '%s' has multiple "
- "matching dynamic types, picking "
- "this one: uid={0x%" PRIx64 "}, type-name='%s'\n",
- original_ptr, in_value.GetTypeName().AsCString(),
- type_sp->GetID(), type_sp->GetName().GetCString());
- type_info.SetTypeSP(type_sp);
- }
- }
- }
-
- if (log) {
- LLDB_LOGF(log,
- "0x%16.16" PRIx64
- ": static-type = '%s' has multiple matching dynamic "
- "types, didn't find a C++ match\n",
- original_ptr, in_value.GetTypeName().AsCString());
- }
+ }
+ }
+
+ for (i = 0; i < class_types.GetSize(); i++) {
+ type_sp = class_types.GetTypeAtIndex(i);
+ if (type_sp) {
+ if (TypeSystemClang::IsCXXClassType(
+ type_sp->GetForwardCompilerType())) {
+ LLDB_LOGF(
+ log,
+ "0x%16.16" PRIx64 ": static-type = '%s' has multiple "
+ "matching dynamic types, picking "
+ "this one: uid={0x%" PRIx64 "}, type-name='%s'\n",
+ in_value.GetPointerValue(),
+ in_value.GetTypeName().AsCString(),
+ type_sp->GetID(), type_sp->GetName().GetCString());
+ type_info.SetTypeSP(type_sp);
}
- if (type_info)
- SetDynamicTypeInfo(vtable_addr, type_info);
- return type_info;
}
}
+
+ if (log) {
+ LLDB_LOGF(log,
+ "0x%16.16" PRIx64
+ ": static-type = '%s' has multiple matching dynamic "
+ "types, didn't find a C++ match\n",
+ in_value.GetPointerValue(),
+ in_value.GetTypeName().AsCString());
+ }
}
+ if (type_info)
+ SetDynamicTypeInfo(vtable_info.addr, type_info);
+ return type_info;
}
}
return TypeAndOrName();
}
+// This function can accept both pointers or references to classes as well
+// as instances of classes. For dynamic type detection, only valid ValueObjects
+// that return true to CouldHaveDynamicValue(...) should call this function.
+// This function is also used by ValueObjectVTable and is can pass in
+// instances of classes.
+ std::optional<LanguageRuntime::VTableInfo>
+ ItaniumABILanguageRuntime::GetVTableInfo(ValueObject &in_value) {
+ ExecutionContext exe_ctx(in_value.GetExecutionContextRef());
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process == nullptr)
+ return std::nullopt;
+
+ AddressType address_type;
+ lldb::addr_t original_ptr = LLDB_INVALID_ADDRESS;
+ if (in_value.GetCompilerType().IsPointerOrReferenceType())
+ original_ptr = in_value.GetPointerValue(&address_type);
+ else
+ original_ptr = in_value.GetAddressOf(/*scalar_is_load_address=*/true,
+ &address_type);
+ if (original_ptr == LLDB_INVALID_ADDRESS || address_type != eAddressTypeLoad)
+ return std::nullopt;
+
+ Status error;
+ const lldb::addr_t vtable_load_addr =
+ process->ReadPointerFromMemory(original_ptr, error);
+
+ if (!error.Success() || vtable_load_addr == LLDB_INVALID_ADDRESS)
+ return std::nullopt;
+
+ // Find the symbol that contains the "vtable_load_addr" address
+ Address vtable_addr;
+ if (!process->GetTarget().ResolveLoadAddress(vtable_load_addr, vtable_addr))
+ return std::nullopt;
+
+ // Check our cache first to see if we already have this info
+ {
+ std::lock_guard<std::mutex> locker(m_mutex);
+ auto pos = m_vtable_info_map.find(vtable_addr);
+ if (pos != m_vtable_info_map.end())
+ return pos->second;
+ }
+
+ Symbol *symbol = vtable_addr.CalculateSymbolContextSymbol();
+ if (symbol == nullptr)
+ return std::nullopt;
+ llvm::StringRef name = symbol->GetMangled().GetDemangledName().GetStringRef();
+ if (name.startswith(vtable_demangled_prefix)) {
+ VTableInfo info = {vtable_addr, symbol};
+ std::lock_guard<std::mutex> locker(m_mutex);
+ auto pos = m_vtable_info_map[vtable_addr] = info;
+ return info;
+ }
+ return std::nullopt;
+}
+
bool ItaniumABILanguageRuntime::GetDynamicTypeAndAddress(
ValueObject &in_value, lldb::DynamicValueType use_dynamic,
TypeAndOrName &class_type_or_name, Address &dynamic_address,
@@ -198,33 +246,17 @@ bool ItaniumABILanguageRuntime::GetDynamicTypeAndAddress(
class_type_or_name.Clear();
value_type = Value::ValueType::Scalar;
- // Only a pointer or reference type can have a different dynamic and static
- // type:
if (!CouldHaveDynamicValue(in_value))
return false;
- // First job, pull out the address at 0 offset from the object.
- AddressType address_type;
- lldb::addr_t original_ptr = in_value.GetPointerValue(&address_type);
- if (original_ptr == LLDB_INVALID_ADDRESS)
+ // Check if we have a vtable pointer in this value. If we don't it will
+ // return std::nullopt, else it will return a valid resolved address.
+ std::optional<VTableInfo> opt_vtable_info = GetVTableInfo(in_value);
+ if (!opt_vtable_info)
return false;
- ExecutionContext exe_ctx(in_value.GetExecutionContextRef());
-
- Process *process = exe_ctx.GetProcessPtr();
-
- if (process == nullptr)
- return false;
-
- Status error;
- const lldb::addr_t vtable_address_point =
- process->ReadPointerFromMemory(original_ptr, error);
-
- if (!error.Success() || vtable_address_point == LLDB_INVALID_ADDRESS)
- return false;
-
- class_type_or_name = GetTypeInfoFromVTableAddress(in_value, original_ptr,
- vtable_address_point);
+ const VTableInfo &vtable_info = opt_vtable_info.value();
+ class_type_or_name = GetTypeInfo(in_value, vtable_info);
if (!class_type_or_name)
return false;
@@ -244,22 +276,27 @@ bool ItaniumABILanguageRuntime::GetDynamicTypeAndAddress(
}
// The offset_to_top is two pointers above the vtable pointer.
- const uint32_t addr_byte_size = process->GetAddressByteSize();
+ Target &target = m_process->GetTarget();
+ const addr_t vtable_load_addr = vtable_info.addr.GetLoadAddress(&target);
+ if (vtable_load_addr == LLDB_INVALID_ADDRESS)
+ return false;
+ const uint32_t addr_byte_size = m_process->GetAddressByteSize();
const lldb::addr_t offset_to_top_location =
- vtable_address_point - 2 * addr_byte_size;
+ vtable_load_addr - 2 * addr_byte_size;
// Watch for underflow, offset_to_top_location should be less than
- // vtable_address_point
- if (offset_to_top_location >= vtable_address_point)
+ // vtable_load_addr
+ if (offset_to_top_location >= vtable_load_addr)
return false;
- const int64_t offset_to_top = process->ReadSignedIntegerFromMemory(
+ Status error;
+ const int64_t offset_to_top = m_process->ReadSignedIntegerFromMemory(
offset_to_top_location, addr_byte_size, INT64_MIN, error);
if (offset_to_top == INT64_MIN)
return false;
// So the dynamic type is a value that starts at offset_to_top above
// the original address.
- lldb::addr_t dynamic_addr = original_ptr + offset_to_top;
- if (!process->GetTarget().GetSectionLoadList().ResolveLoadAddress(
+ lldb::addr_t dynamic_addr = in_value.GetPointerValue() + offset_to_top;
+ if (!m_process->GetTarget().ResolveLoadAddress(
dynamic_addr, dynamic_address)) {
dynamic_address.SetRawAddress(dynamic_addr);
}
@@ -583,10 +620,10 @@ ValueObjectSP ItaniumABILanguageRuntime::GetExceptionObjectForThread(
ValueObjectSP exception = ValueObject::CreateValueObjectFromData(
"exception", exception_isw.GetAsData(m_process->GetByteOrder()), exe_ctx,
voidstar);
- ValueObjectSP dyn_exception
+ ValueObjectSP dyn_exception
= exception->GetDynamicValue(eDynamicDontRunTarget);
// If we succeed in making a dynamic value, return that:
- if (dyn_exception)
+ if (dyn_exception)
return dyn_exception;
return exception;
@@ -594,7 +631,7 @@ ValueObjectSP ItaniumABILanguageRuntime::GetExceptionObjectForThread(
TypeAndOrName ItaniumABILanguageRuntime::GetDynamicTypeInfo(
const lldb_private::Address &vtable_addr) {
- std::lock_guard<std::mutex> locker(m_dynamic_type_map_mutex);
+ std::lock_guard<std::mutex> locker(m_mutex);
DynamicTypeCache::const_iterator pos = m_dynamic_type_map.find(vtable_addr);
if (pos == m_dynamic_type_map.end())
return TypeAndOrName();
@@ -604,6 +641,6 @@ TypeAndOrName ItaniumABILanguageRuntime::GetDynamicTypeInfo(
void ItaniumABILanguageRuntime::SetDynamicTypeInfo(
const lldb_private::Address &vtable_addr, const TypeAndOrName &type_info) {
- std::lock_guard<std::mutex> locker(m_dynamic_type_map_mutex);
+ std::lock_guard<std::mutex> locker(m_mutex);
m_dynamic_type_map[vtable_addr] = type_info;
}
diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h
index ca8d5ab1a93a1b9..f6afc7a08a32db9 100644
--- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h
+++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h
@@ -47,6 +47,9 @@ class ItaniumABILanguageRuntime : public lldb_private::CPPLanguageRuntime {
return runtime->isA(&ID);
}
+ std::optional<LanguageRuntime::VTableInfo>
+ GetVTableInfo(ValueObject &in_value) override;
+
bool GetDynamicTypeAndAddress(ValueObject &in_value,
lldb::DynamicValueType use_dynamic,
TypeAndOrName &class_type_or_name,
@@ -71,7 +74,7 @@ class ItaniumABILanguageRuntime : public lldb_private::CPPLanguageRuntime {
bool catch_bp, bool throw_bp) override;
lldb::SearchFilterSP CreateExceptionSearchFilter() override;
-
+
lldb::ValueObjectSP GetExceptionObjectForThread(
lldb::ThreadSP thread_sp) override;
@@ -89,19 +92,19 @@ class ItaniumABILanguageRuntime : public lldb_private::CPPLanguageRuntime {
private:
typedef std::map<lldb_private::Address, TypeAndOrName> DynamicTypeCache;
+ typedef std::map<lldb_private::Address, VTableInfo> VTableInfoCache;
ItaniumABILanguageRuntime(Process *process)
: // Call CreateInstance instead.
- lldb_private::CPPLanguageRuntime(process), m_cxx_exception_bp_sp(),
- m_dynamic_type_map(), m_dynamic_type_map_mutex() {}
+ lldb_private::CPPLanguageRuntime(process) {}
lldb::BreakpointSP m_cxx_exception_bp_sp;
DynamicTypeCache m_dynamic_type_map;
- std::mutex m_dynamic_type_map_mutex;
+ VTableInfoCache m_vtable_info_map;
+ std::mutex m_mutex;
- TypeAndOrName GetTypeInfoFromVTableAddress(ValueObject &in_value,
- lldb::addr_t original_ptr,
- lldb::addr_t vtable_addr);
+ TypeAndOrName GetTypeInfo(ValueObject &in_value,
+ const VTableInfo &vtable_info);
TypeAndOrName GetDynamicTypeInfo(const lldb_private::Address &vtable_addr);
diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
index 37ba8b0f6e2afcf..27807e4ccf681da 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
@@ -4748,15 +4748,6 @@ TypeSystemClang::GetBitSize(lldb::opaque_compiler_type_t type,
return std::nullopt;
break;
- case clang::Type::FunctionProto:
- case clang::Type::FunctionNoProto: {
- ExecutionContext exe_ctx(exe_scope);
- Process *process = exe_ctx.GetProcessPtr();
- if (process)
- return process->GetAddressByteSize() * 8;
- break;
- }
-
case clang::Type::ObjCInterface:
case clang::Type::ObjCObject: {
ExecutionContext exe_ctx(exe_scope);
diff --git a/lldb/source/Symbol/Type.cpp b/lldb/source/Symbol/Type.cpp
index 66284eb73cad038..2ed4717467587ea 100644
--- a/lldb/source/Symbol/Type.cpp
+++ b/lldb/source/Symbol/Type.cpp
@@ -790,6 +790,10 @@ void TypeAndOrName::SetName(const char *type_name_cstr) {
m_type_name.SetCString(type_name_cstr);
}
+void TypeAndOrName::SetName(llvm::StringRef type_name) {
+ m_type_name.SetString(type_name);
+}
+
void TypeAndOrName::SetTypeSP(lldb::TypeSP type_sp) {
if (type_sp) {
m_compiler_type = type_sp->GetForwardCompilerType();
diff --git a/lldb/test/API/functionalities/vtable/TestVTableValue.py b/lldb/test/API/functionalities/vtable/TestVTableValue.py
index ce3da0a2185f501..c1d207371aa6b29 100644
--- a/lldb/test/API/functionalities/vtable/TestVTableValue.py
+++ b/lldb/test/API/functionalities/vtable/TestVTableValue.py
@@ -21,6 +21,7 @@ def test_vtable(self):
self, "At the end", lldb.SBFileSpec("main.cpp")
)
+ # Test a shape instance to make sure we get the vtable correctly.
shape = self.frame().FindVariable("shape")
vtable = shape.GetVTable()
self.assertEquals(vtable.GetName(), "vtable for Shape")
@@ -37,6 +38,25 @@ def test_vtable(self):
for (idx, vtable_entry) in enumerate(vtable.children):
self.verify_vtable_entry(vtable_entry, vtable_addr, idx)
+ # Test a shape reference to make sure we get the vtable correctly.
+ shape = self.frame().FindVariable("shape_ref")
+ vtable = shape.GetVTable()
+ self.assertEquals(vtable.GetName(), "vtable for Shape")
+ self.assertEquals(vtable.GetTypeName(), "vtable for Shape")
+ # Make sure we have the right number of virtual functions in our vtable
+ # for the shape class.
+ self.assertEquals(vtable.GetNumChildren(), 4)
+
+ # Verify vtable address
+ vtable_addr = vtable.GetValueAsUnsigned(0)
+ expected_addr = self.expected_vtable_addr(shape)
+ self.assertEquals(vtable_addr, expected_addr)
+
+ for (idx, vtable_entry) in enumerate(vtable.children):
+ self.verify_vtable_entry(vtable_entry, vtable_addr, idx)
+
+
+ # Test we get the right vtable for the Rectangle instance.
rect = self.frame().FindVariable("rect")
vtable = rect.GetVTable()
self.assertEquals(vtable.GetName(), "vtable for Rectangle")
@@ -128,7 +148,10 @@ def verify_vtable_entry(self, vtable_entry: lldb.SBValue, vtable_addr: int,
sym_ctx = sb_addr.GetSymbolContext(lldb.eSymbolContextEverything)
# Make sure the type is the same as the function type
- self.assertEquals(vtable_entry.GetType(), sym_ctx.GetFunction().GetType())
+ func_type = sym_ctx.GetFunction().GetType()
+ if func_type.IsValid():
+ self.assertEquals(vtable_entry.GetType(),
+ func_type.GetPointerType())
# The summary should be the address description of the function pointer
summary = vtable_entry.GetSummary()
diff --git a/lldb/test/API/functionalities/vtable/main.cpp b/lldb/test/API/functionalities/vtable/main.cpp
index 7113eff343d43f8..498a5765a3f6ff7 100644
--- a/lldb/test/API/functionalities/vtable/main.cpp
+++ b/lldb/test/API/functionalities/vtable/main.cpp
@@ -31,6 +31,7 @@ int main(int argc, const char **argv) {
Shape shape;
Rectangle rect;
Shape *shape_ptr = ▭
+ Shape &shape_ref = shape;
shape_ptr = &shape; // Shape is Rectangle
NotVirtual not_virtual; // Shape is Shape
return 0; // At the end
>From cbb0915b7d267a7ed9d45030daf5031bc5007b3c Mon Sep 17 00:00:00 2001
From: Greg Clayton <gclayton at fb.com>
Date: Thu, 28 Sep 2023 11:59:28 -0700
Subject: [PATCH 3/3] Add better header documentation, and fix review comments.
---
lldb/include/lldb/API/SBValue.h | 28 ++++++++++-----
lldb/include/lldb/Core/ValueObjectVTable.h | 40 ++++++++++++++++++++++
lldb/source/Core/ValueObjectVTable.cpp | 6 ++--
3 files changed, 62 insertions(+), 12 deletions(-)
diff --git a/lldb/include/lldb/API/SBValue.h b/lldb/include/lldb/API/SBValue.h
index 5e6e95e67a796b9..bbcccaab51aaee1 100644
--- a/lldb/include/lldb/API/SBValue.h
+++ b/lldb/include/lldb/API/SBValue.h
@@ -394,18 +394,30 @@ class LLDB_API SBValue {
/// SBValue::GetLoadAddress() will return the address of the vtable pointer
/// found in the parent SBValue.
///
- /// SBValue::GetSummary() will contain the number of virtual function pointers
- /// in the vtable like is done for arrays.
- ///
/// SBValue::GetNumChildren() will return the number of virtual function
/// pointers in the vtable, or zero on error.
///
/// SBValue::GetChildAtIndex(...) will return each virtual function pointer
- /// as a SBValue object. The child SBValue objects name will be the array
- /// index, value will be the virtual function pointer, summary will be the
- /// symbolicated address description, and if the the adress resolves to a
- /// function in debug info, the child type will be a pointer to the function
- /// prototype.
+ /// as a SBValue object.
+ ///
+ /// The child SBValue objects will have the following values:
+ ///
+ /// SBValue::GetError() will indicate success if the vtable entry was
+ /// successfully read from memory, or an error if not.
+ ///
+ /// SBValue::GetName() will be the vtable function index in the form "[%u]"
+ /// where %u is the index.
+ ///
+ /// SBValue::GetValue() will be the virtual function pointer value as a
+ /// string.
+ ///
+ /// SBValue::GetValueAtUnsigned(...) will return the virtual function
+ /// pointer value.
+ ///
+ /// SBValue::GetLoadAddress() will return the address of the virtual function
+ /// pointer.
+ ///
+ /// SBValue::GetNumChildren() returns 0
lldb::SBValue GetVTable();
protected:
diff --git a/lldb/include/lldb/Core/ValueObjectVTable.h b/lldb/include/lldb/Core/ValueObjectVTable.h
index 6aaa40134240bdb..217ff8d0d334ce6 100644
--- a/lldb/include/lldb/Core/ValueObjectVTable.h
+++ b/lldb/include/lldb/Core/ValueObjectVTable.h
@@ -15,7 +15,47 @@ namespace lldb_private {
/// A class that represents a virtual function table for a C++ class.
///
+/// ValueObject::GetError() will be in the success state if this value
+/// represents a C++ class with a vtable, or an appropriate error describing
+/// that the object isn't a C++ class with a vtable or not a C++ class.
///
+/// ValueObject::GetName() will be the demangled symbol name for the virtual
+/// function table like "vtable for <classname>".
+///
+/// ValueObject::GetValueAsCString() will be the address of the first vtable
+/// entry if the current ValueObject is a class with a vtable, or nothing the
+/// current ValueObject is not a C++ class or not a C++ class that has a
+/// vtable.
+///
+/// ValueObject::GetValueAtUnsigned(...) will return the address of the first
+/// vtable entry.
+///
+/// ValueObject::GetAddressOf() will return the address of the vtable pointer
+/// found in the parent ValueObject.
+///
+/// ValueObject::GetNumChildren() will return the number of virtual function
+/// pointers in the vtable, or zero on error.
+///
+/// ValueObject::GetChildAtIndex(...) will return each virtual function pointer
+/// as a ValueObject object.
+///
+/// The child ValueObjects will have the following values:
+///
+/// ValueObject::GetError() will indicate success if the vtable entry was
+/// successfully read from memory, or an error if not.
+///
+/// ValueObject::GetName() will be the vtable function index in the form "[%u]"
+/// where %u is the index.
+///
+/// ValueObject::GetValueAsCString() will be the virtual function pointer value
+///
+/// ValueObject::GetValueAtUnsigned(...) will return the virtual function
+/// pointer value.
+///
+/// ValueObject::GetAddressOf() will return the address of the virtual function
+/// pointer.
+///
+/// ValueObject::GetNumChildren() returns 0
class ValueObjectVTable : public ValueObject {
public:
~ValueObjectVTable() override;
diff --git a/lldb/source/Core/ValueObjectVTable.cpp b/lldb/source/Core/ValueObjectVTable.cpp
index 6d14571dcfb57f1..04fcce5ebd3fe3a 100644
--- a/lldb/source/Core/ValueObjectVTable.cpp
+++ b/lldb/source/Core/ValueObjectVTable.cpp
@@ -38,8 +38,7 @@ class ValueObjectVTableChild : public ValueObject {
ValueType GetValueType() const override { return eValueTypeVTableEntry; };
bool IsInScope() override {
- ValueObject *parent = GetParent();
- if (parent)
+ if (ValueObject *parent = GetParent())
return parent->IsInScope();
return false;
};
@@ -157,8 +156,7 @@ ValueObjectVTable::ValueObjectVTable(ValueObject &parent)
std::optional<uint64_t> ValueObjectVTable::GetByteSize() {
if (m_vtable_symbol)
return m_vtable_symbol->GetByteSize();
- else
- return std::nullopt;
+ return std::nullopt;
}
size_t ValueObjectVTable::CalculateNumChildren(uint32_t max) {
More information about the lldb-commits
mailing list