Skip to content

Add the ability to get a C++ vtable ValueObject from another ValueObj… #67599

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 0 additions & 13 deletions lldb/bindings/interface/SBTypeDocstrings.i
Original file line number Diff line number Diff line change
Expand Up @@ -139,19 +139,6 @@ SBType supports the eq/ne operator. For example,::
"
) lldb::SBType::IsReferenceType;

%feature("docstring",
"Returns true if this type is a function type.

Language-specific behaviour:

* C: Returns true for types that represent functions. Note that function
pointers are not function types (but their `GetPointeeType()` are function
types).
* C++: Same as in C.
* Objective-C: Returns false for all types.
"
) lldb::SBType::IsPolymorphicClass;

%feature("docstring",
"Returns true if this type is a polymorphic type.

Expand Down
46 changes: 46 additions & 0 deletions lldb/include/lldb/API/SBValue.h
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,52 @@ 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 <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::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 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:
friend class SBBlock;
friend class SBFrame;
Expand Down
4 changes: 4 additions & 0 deletions lldb/include/lldb/Core/ValueObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
1 change: 1 addition & 0 deletions lldb/include/lldb/Core/ValueObjectChild.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
105 changes: 105 additions & 0 deletions lldb/include/lldb/Core/ValueObjectVTable.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
//===-- 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.
///
/// 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;

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.
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
/// 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
2 changes: 2 additions & 0 deletions lldb/include/lldb/Symbol/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,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);
Expand Down
4 changes: 4 additions & 0 deletions lldb/include/lldb/Symbol/TypeSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,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;
Expand Down
26 changes: 26 additions & 0 deletions lldb/include/lldb/Target/LanguageRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,32 @@ 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.
///
/// \param[in] in_value
/// The value object to try and extract the VTableInfo from.
///
/// \param[in] check_type
/// If true, the compiler type of \a in_value will be checked to see if
/// it is an instance to, or pointer or reference to a class or struct
/// that has a vtable. If the type doesn't meet the requirements, an
/// error will be returned explaining why the type isn't suitable.
///
/// \return
/// An error if anything goes wrong while trying to extract the vtable
/// or if \a check_type is true and the type doesn't have a vtable.
virtual llvm::Expected<VTableInfo> GetVTableInfo(ValueObject &in_value,
bool check_type) {
return llvm::createStringError(
std::errc::invalid_argument,
"language doesn't support getting vtable information");
}

// 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,
Expand Down
4 changes: 3 additions & 1 deletion lldb/include/lldb/lldb-enumerations.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
17 changes: 14 additions & 3 deletions lldb/source/API/SBValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -1505,3 +1505,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;
}
2 changes: 2 additions & 0 deletions lldb/source/Commands/CommandObjectFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,8 @@ may even involve JITing and running code in the target program.)");
case eValueTypeRegisterSet:
case eValueTypeConstResult:
case eValueTypeVariableThreadLocal:
case eValueTypeVTable:
case eValueTypeVTableEntry:
return false;
}
}
Expand Down
1 change: 1 addition & 0 deletions lldb/source/Core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ add_lldb_library(lldbCore
ValueObjectSyntheticFilter.cpp
ValueObjectUpdater.cpp
ValueObjectVariable.cpp
ValueObjectVTable.cpp

DEPENDS
clang-tablegen-targets
Expand Down
5 changes: 5 additions & 0 deletions lldb/source/Core/ValueObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -3155,3 +3156,7 @@ ValueObjectSP ValueObject::Persist() {

return persistent_var_sp->GetValueObject();
}

lldb::ValueObjectSP ValueObject::GetVTable() {
return ValueObjectVTable::Create(*this);
}
Loading