Skip to content

Commit 15b873e

Browse files
clayborgMichael137
authored andcommitted
Add the ability to get a C++ vtable ValueObject from another ValueObj… (llvm#67599)
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 ``` (cherry picked from commit 7fbd427)
1 parent c190269 commit 15b873e

File tree

24 files changed

+1013
-178
lines changed

24 files changed

+1013
-178
lines changed

lldb/bindings/interface/SBTypeDocstrings.i

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -139,19 +139,6 @@ SBType supports the eq/ne operator. For example,::
139139
"
140140
) lldb::SBType::IsReferenceType;
141141

142-
%feature("docstring",
143-
"Returns true if this type is a function type.
144-
145-
Language-specific behaviour:
146-
147-
* C: Returns true for types that represent functions. Note that function
148-
pointers are not function types (but their `GetPointeeType()` are function
149-
types).
150-
* C++: Same as in C.
151-
* Objective-C: Returns false for all types.
152-
"
153-
) lldb::SBType::IsPolymorphicClass;
154-
155142
%feature("docstring",
156143
"Returns true if this type is a polymorphic type.
157144

lldb/include/lldb/API/SBValue.h

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,52 @@ class LLDB_API SBValue {
374374
lldb::SBWatchpoint WatchPointee(bool resolve_location, bool read, bool write,
375375
SBError &error);
376376

377+
/// If this value represents a C++ class that has a vtable, return an value
378+
/// that represents the virtual function table.
379+
///
380+
/// SBValue::GetError() will be in the success state if this value represents
381+
/// a C++ class with a vtable, or an appropriate error describing that the
382+
/// object isn't a C++ class with a vtable or not a C++ class.
383+
///
384+
/// SBValue::GetName() will be the demangled symbol name for the virtual
385+
/// function table like "vtable for <classname>".
386+
///
387+
/// SBValue::GetValue() will be the address of the first vtable entry if the
388+
/// current SBValue is a class with a vtable, or nothing the current SBValue
389+
/// is not a C++ class or not a C++ class that has a vtable.
390+
///
391+
/// SBValue::GetValueAtUnsigned(...) will return the address of the first
392+
/// vtable entry.
393+
///
394+
/// SBValue::GetLoadAddress() will return the address of the vtable pointer
395+
/// found in the parent SBValue.
396+
///
397+
/// SBValue::GetNumChildren() will return the number of virtual function
398+
/// pointers in the vtable, or zero on error.
399+
///
400+
/// SBValue::GetChildAtIndex(...) will return each virtual function pointer
401+
/// as a SBValue object.
402+
///
403+
/// The child SBValue objects will have the following values:
404+
///
405+
/// SBValue::GetError() will indicate success if the vtable entry was
406+
/// successfully read from memory, or an error if not.
407+
///
408+
/// SBValue::GetName() will be the vtable function index in the form "[%u]"
409+
/// where %u is the index.
410+
///
411+
/// SBValue::GetValue() will be the virtual function pointer value as a
412+
/// string.
413+
///
414+
/// SBValue::GetValueAtUnsigned(...) will return the virtual function
415+
/// pointer value.
416+
///
417+
/// SBValue::GetLoadAddress() will return the address of the virtual function
418+
/// pointer.
419+
///
420+
/// SBValue::GetNumChildren() returns 0
421+
lldb::SBValue GetVTable();
422+
377423
protected:
378424
friend class SBBlock;
379425
friend class SBFrame;

lldb/include/lldb/Core/ValueObject.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,10 @@ class ValueObject {
633633
virtual lldb::ValueObjectSP CastPointerType(const char *name,
634634
lldb::TypeSP &type_sp);
635635

636+
/// If this object represents a C++ class with a vtable, return an object
637+
/// that represents the virtual function table. If the object isn't a class
638+
/// with a vtable, return a valid ValueObject with the error set correctly.
639+
lldb::ValueObjectSP GetVTable();
636640
// The backing bits of this value object were updated, clear any descriptive
637641
// string, so we know we have to refetch them.
638642
void ValueUpdated() {

lldb/include/lldb/Core/ValueObjectChild.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ class ValueObjectChild : public ValueObject {
7373
friend class ValueObject;
7474
friend class ValueObjectConstResult;
7575
friend class ValueObjectConstResultImpl;
76+
friend class ValueObjectVTable;
7677

7778
ValueObjectChild(ValueObject &parent, const CompilerType &compiler_type,
7879
ConstString name, uint64_t byte_size,
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
//===-- ValueObjectVTable.h -------------------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLDB_CORE_VALUEOBJECTVTABLE_H
10+
#define LLDB_CORE_VALUEOBJECTVTABLE_H
11+
12+
#include "lldb/Core/ValueObject.h"
13+
14+
namespace lldb_private {
15+
16+
/// A class that represents a virtual function table for a C++ class.
17+
///
18+
/// ValueObject::GetError() will be in the success state if this value
19+
/// represents a C++ class with a vtable, or an appropriate error describing
20+
/// that the object isn't a C++ class with a vtable or not a C++ class.
21+
///
22+
/// ValueObject::GetName() will be the demangled symbol name for the virtual
23+
/// function table like "vtable for <classname>".
24+
///
25+
/// ValueObject::GetValueAsCString() will be the address of the first vtable
26+
/// entry if the current ValueObject is a class with a vtable, or nothing the
27+
/// current ValueObject is not a C++ class or not a C++ class that has a
28+
/// vtable.
29+
///
30+
/// ValueObject::GetValueAtUnsigned(...) will return the address of the first
31+
/// vtable entry.
32+
///
33+
/// ValueObject::GetAddressOf() will return the address of the vtable pointer
34+
/// found in the parent ValueObject.
35+
///
36+
/// ValueObject::GetNumChildren() will return the number of virtual function
37+
/// pointers in the vtable, or zero on error.
38+
///
39+
/// ValueObject::GetChildAtIndex(...) will return each virtual function pointer
40+
/// as a ValueObject object.
41+
///
42+
/// The child ValueObjects will have the following values:
43+
///
44+
/// ValueObject::GetError() will indicate success if the vtable entry was
45+
/// successfully read from memory, or an error if not.
46+
///
47+
/// ValueObject::GetName() will be the vtable function index in the form "[%u]"
48+
/// where %u is the index.
49+
///
50+
/// ValueObject::GetValueAsCString() will be the virtual function pointer value
51+
///
52+
/// ValueObject::GetValueAtUnsigned(...) will return the virtual function
53+
/// pointer value.
54+
///
55+
/// ValueObject::GetAddressOf() will return the address of the virtual function
56+
/// pointer.
57+
///
58+
/// ValueObject::GetNumChildren() returns 0
59+
class ValueObjectVTable : public ValueObject {
60+
public:
61+
~ValueObjectVTable() override;
62+
63+
static lldb::ValueObjectSP Create(ValueObject &parent);
64+
65+
std::optional<uint64_t> GetByteSize() override;
66+
67+
size_t CalculateNumChildren(uint32_t max) override;
68+
69+
ValueObject *CreateChildAtIndex(size_t idx, bool synthetic_array_member,
70+
int32_t synthetic_index) override;
71+
72+
lldb::ValueType GetValueType() const override;
73+
74+
ConstString GetTypeName() override;
75+
76+
ConstString GetQualifiedTypeName() override;
77+
78+
ConstString GetDisplayTypeName() override;
79+
80+
bool IsInScope() override;
81+
82+
protected:
83+
bool UpdateValue() override;
84+
85+
CompilerType GetCompilerTypeImpl() override;
86+
87+
/// The symbol for the C++ virtual function table.
88+
const Symbol *m_vtable_symbol = nullptr;
89+
/// Cache the number of vtable children when we update the value.
90+
uint32_t m_num_vtable_entries = 0;
91+
/// Cache the address size in bytes to avoid checking with the process to
92+
/// many times.
93+
uint32_t m_addr_size = 0;
94+
95+
private:
96+
ValueObjectVTable(ValueObject &parent);
97+
98+
// For ValueObject only
99+
ValueObjectVTable(const ValueObjectVTable &) = delete;
100+
const ValueObjectVTable &operator=(const ValueObjectVTable &) = delete;
101+
};
102+
103+
} // namespace lldb_private
104+
105+
#endif // LLDB_CORE_VALUEOBJECTVTABLE_H

lldb/include/lldb/Symbol/Type.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,8 @@ class TypeAndOrName {
429429

430430
void SetName(const char *type_name_cstr);
431431

432+
void SetName(llvm::StringRef name);
433+
432434
void SetTypeSP(lldb::TypeSP type_sp);
433435

434436
void SetCompilerType(CompilerType compiler_type);

lldb/include/lldb/Symbol/TypeSystem.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,10 @@ class TypeSystem : public PluginInterface,
485485

486486
virtual CompilerType GetBasicTypeFromAST(lldb::BasicType basic_type) = 0;
487487

488+
virtual CompilerType CreateGenericFunctionPrototype() {
489+
return CompilerType();
490+
}
491+
488492
virtual CompilerType
489493
GetBuiltinTypeForEncodingAndBitSize(lldb::Encoding encoding,
490494
size_t bit_size) = 0;

lldb/include/lldb/Target/LanguageRuntime.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,32 @@ class LanguageRuntime : public Runtime, public PluginInterface {
7979
virtual bool GetObjectDescription(Stream &str, Value &value,
8080
ExecutionContextScope *exe_scope) = 0;
8181

82+
83+
struct VTableInfo {
84+
Address addr; /// Address of the vtable's virtual function table
85+
Symbol *symbol; /// The vtable symbol from the symbol table
86+
};
87+
/// Get the vtable information for a given value.
88+
///
89+
/// \param[in] in_value
90+
/// The value object to try and extract the VTableInfo from.
91+
///
92+
/// \param[in] check_type
93+
/// If true, the compiler type of \a in_value will be checked to see if
94+
/// it is an instance to, or pointer or reference to a class or struct
95+
/// that has a vtable. If the type doesn't meet the requirements, an
96+
/// error will be returned explaining why the type isn't suitable.
97+
///
98+
/// \return
99+
/// An error if anything goes wrong while trying to extract the vtable
100+
/// or if \a check_type is true and the type doesn't have a vtable.
101+
virtual llvm::Expected<VTableInfo> GetVTableInfo(ValueObject &in_value,
102+
bool check_type) {
103+
return llvm::createStringError(
104+
std::errc::invalid_argument,
105+
"language doesn't support getting vtable information");
106+
}
107+
82108
// this call should return true if it could set the name and/or the type
83109
virtual bool GetDynamicTypeAndAddress(ValueObject &in_value,
84110
lldb::DynamicValueType use_dynamic,

lldb/include/lldb/lldb-enumerations.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,9 @@ enum ValueType {
322322
eValueTypeRegister = 5, ///< stack frame register value
323323
eValueTypeRegisterSet = 6, ///< A collection of stack frame register values
324324
eValueTypeConstResult = 7, ///< constant result variables
325-
eValueTypeVariableThreadLocal = 8 ///< thread local storage variable
325+
eValueTypeVariableThreadLocal = 8, ///< thread local storage variable
326+
eValueTypeVTable = 9, ///< virtual function table
327+
eValueTypeVTableEntry = 10, ///< function pointer in virtual function table
326328
};
327329

328330
/// Token size/granularities for Input Readers.

lldb/source/API/SBValue.cpp

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ class ValueImpl {
115115

116116
Target *target = value_sp->GetTargetSP().get();
117117
// If this ValueObject holds an error, then it is valuable for that.
118-
if (value_sp->GetError().Fail())
118+
if (value_sp->GetError().Fail())
119119
return value_sp;
120120

121121
if (!target)
@@ -1039,8 +1039,8 @@ lldb::ValueObjectSP SBValue::GetSP(ValueLocker &locker) const {
10391039
// IsValid means that the SBValue has a value in it. But that's not the
10401040
// only time that ValueObjects are useful. We also want to return the value
10411041
// if there's an error state in it.
1042-
if (!m_opaque_sp || (!m_opaque_sp->IsValid()
1043-
&& (m_opaque_sp->GetRootSP()
1042+
if (!m_opaque_sp || (!m_opaque_sp->IsValid()
1043+
&& (m_opaque_sp->GetRootSP()
10441044
&& !m_opaque_sp->GetRootSP()->GetError().Fail()))) {
10451045
locker.GetError().SetErrorString("No value");
10461046
return ValueObjectSP();
@@ -1506,3 +1506,14 @@ lldb::SBValue SBValue::Persist() {
15061506
}
15071507
return persisted_sb;
15081508
}
1509+
1510+
lldb::SBValue SBValue::GetVTable() {
1511+
SBValue vtable_sb;
1512+
ValueLocker locker;
1513+
lldb::ValueObjectSP value_sp(GetSP(locker));
1514+
if (!value_sp)
1515+
return vtable_sb;
1516+
1517+
vtable_sb.SetSP(value_sp->GetVTable());
1518+
return vtable_sb;
1519+
}

lldb/source/Commands/CommandObjectFrame.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,8 @@ may even involve JITing and running code in the target program.)");
495495
case eValueTypeRegisterSet:
496496
case eValueTypeConstResult:
497497
case eValueTypeVariableThreadLocal:
498+
case eValueTypeVTable:
499+
case eValueTypeVTableEntry:
498500
return false;
499501
}
500502
}

lldb/source/Core/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ add_lldb_library(lldbCore
7373
ValueObjectSyntheticFilter.cpp
7474
ValueObjectUpdater.cpp
7575
ValueObjectVariable.cpp
76+
ValueObjectVTable.cpp
7677

7778
DEPENDS
7879
clang-tablegen-targets

lldb/source/Core/ValueObject.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "lldb/Core/ValueObjectDynamicValue.h"
2020
#include "lldb/Core/ValueObjectMemory.h"
2121
#include "lldb/Core/ValueObjectSyntheticFilter.h"
22+
#include "lldb/Core/ValueObjectVTable.h"
2223
#include "lldb/DataFormatters/DataVisualization.h"
2324
#include "lldb/DataFormatters/DumpValueObjectOptions.h"
2425
#include "lldb/DataFormatters/FormatManager.h"
@@ -3283,3 +3284,7 @@ ValueObjectSP ValueObject::Persist() {
32833284

32843285
return persistent_var_sp->GetValueObject();
32853286
}
3287+
3288+
lldb::ValueObjectSP ValueObject::GetVTable() {
3289+
return ValueObjectVTable::Create(*this);
3290+
}

0 commit comments

Comments
 (0)