[ClangASTContext] Extract VTable pointers from C++ objects

This patch processes the case of retrieving a virtual base when the object is
already read from the debuggee memory.

To achieve that ValueObject::GetCPPVTableAddress was removed and was
reimplemented in ClangASTContext (because access to the process is needed to
retrieve the VTable pointer in general, and because this is the only place that
used old version of ValueObject::GetCPPVTableAddress).

This patch allows to use real object's VTable instead of searching virtual bases
by offsets restored by MicrosoftRecordLayoutBuilder. PDB has no enough info to
restore VBase offsets properly, so we have to read real VTable instead.

Differential revision: https://reviews.llvm.org/D53506

llvm-svn: 346669
This commit is contained in:
Aleksandr Urakov 2018-11-12 16:23:50 +00:00
parent de62b76c51
commit 1dc51db757
6 changed files with 160 additions and 102 deletions

View File

@ -635,9 +635,6 @@ public:
virtual void SetLiveAddress(lldb::addr_t addr = LLDB_INVALID_ADDRESS,
AddressType address_type = eAddressTypeLoad) {}
// Find the address of the C++ vtable pointer
virtual lldb::addr_t GetCPPVTableAddress(AddressType &address_type);
virtual lldb::ValueObjectSP Cast(const CompilerType &compiler_type);
virtual lldb::ValueObjectSP CastPointerType(const char *name,

View File

@ -0,0 +1,16 @@
struct A {
char a = 1;
};
struct B {
int b = 2;
};
struct C : virtual A, virtual B {
short c = 3;
};
int main() {
C c{};
return 0;
}

View File

@ -0,0 +1,7 @@
breakpoint set --file VBases.cpp --line 15
run
print c
frame variable c

View File

@ -0,0 +1,15 @@
REQUIRES: windows
RUN: clang-cl /Zi %S/Inputs/VBases.cpp /o %t.exe
RUN: %lldb -b -s %S/Inputs/VBases.script -- %t.exe | FileCheck %s
CHECK: {
CHECK: A = (a = '\x01')
CHECK: B = (b = 2)
CHECK: c = 3
CHECK: }
CHECK: {
CHECK: A = (a = '\x01')
CHECK: B = (b = 2)
CHECK: c = 3
CHECK: }

View File

@ -2804,31 +2804,6 @@ ValueObjectSP ValueObject::GetQualifiedRepresentationIfAvailable(
return result_sp;
}
lldb::addr_t ValueObject::GetCPPVTableAddress(AddressType &address_type) {
CompilerType pointee_type;
CompilerType this_type(GetCompilerType());
uint32_t type_info = this_type.GetTypeInfo(&pointee_type);
if (type_info) {
bool ptr_or_ref = false;
if (type_info & (eTypeIsPointer | eTypeIsReference)) {
ptr_or_ref = true;
type_info = pointee_type.GetTypeInfo();
}
const uint32_t cpp_class = eTypeIsClass | eTypeIsCPlusPlus;
if ((type_info & cpp_class) == cpp_class) {
if (ptr_or_ref) {
address_type = GetAddressTypeOfChildren();
return GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
} else
return GetAddressOf(false, &address_type);
}
}
address_type = eAddressTypeInvalid;
return LLDB_INVALID_ADDRESS;
}
ValueObjectSP ValueObject::Dereference(Status &error) {
if (m_deref_valobj)
return m_deref_valobj->GetSP();

View File

@ -201,6 +201,122 @@ void addOverridesForMethod(clang::CXXMethodDecl *decl) {
}
}
static lldb::addr_t GetVTableAddress(Process &process,
VTableContextBase &vtable_ctx,
ValueObject &valobj,
const ASTRecordLayout &record_layout) {
// Retrieve type info
CompilerType pointee_type;
CompilerType this_type(valobj.GetCompilerType());
uint32_t type_info = this_type.GetTypeInfo(&pointee_type);
if (!type_info)
return LLDB_INVALID_ADDRESS;
// Check if it's a pointer or reference
bool ptr_or_ref = false;
if (type_info & (eTypeIsPointer | eTypeIsReference)) {
ptr_or_ref = true;
type_info = pointee_type.GetTypeInfo();
}
// We process only C++ classes
const uint32_t cpp_class = eTypeIsClass | eTypeIsCPlusPlus;
if ((type_info & cpp_class) != cpp_class)
return LLDB_INVALID_ADDRESS;
// Calculate offset to VTable pointer
lldb::offset_t vbtable_ptr_offset =
vtable_ctx.isMicrosoft() ? record_layout.getVBPtrOffset().getQuantity()
: 0;
if (ptr_or_ref) {
// We have a pointer / ref to object, so read
// VTable pointer from process memory
if (valobj.GetAddressTypeOfChildren() != eAddressTypeLoad)
return LLDB_INVALID_ADDRESS;
auto vbtable_ptr_addr = valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
if (vbtable_ptr_addr == LLDB_INVALID_ADDRESS)
return LLDB_INVALID_ADDRESS;
vbtable_ptr_addr += vbtable_ptr_offset;
Status err;
return process.ReadPointerFromMemory(vbtable_ptr_addr, err);
}
// We have an object already read from process memory,
// so just extract VTable pointer from it
DataExtractor data;
Status err;
auto size = valobj.GetData(data, err);
if (err.Fail() || vbtable_ptr_offset + data.GetAddressByteSize() > size)
return LLDB_INVALID_ADDRESS;
return data.GetPointer(&vbtable_ptr_offset);
}
static int64_t ReadVBaseOffsetFromVTable(Process &process,
VTableContextBase &vtable_ctx,
lldb::addr_t vtable_ptr,
const CXXRecordDecl *cxx_record_decl,
const CXXRecordDecl *base_class_decl) {
if (vtable_ctx.isMicrosoft()) {
clang::MicrosoftVTableContext &msoft_vtable_ctx =
static_cast<clang::MicrosoftVTableContext &>(vtable_ctx);
// Get the index into the virtual base table. The
// index is the index in uint32_t from vbtable_ptr
const unsigned vbtable_index =
msoft_vtable_ctx.getVBTableIndex(cxx_record_decl, base_class_decl);
const lldb::addr_t base_offset_addr = vtable_ptr + vbtable_index * 4;
Status err;
return process.ReadSignedIntegerFromMemory(base_offset_addr, 4, INT64_MAX,
err);
}
clang::ItaniumVTableContext &itanium_vtable_ctx =
static_cast<clang::ItaniumVTableContext &>(vtable_ctx);
clang::CharUnits base_offset_offset =
itanium_vtable_ctx.getVirtualBaseOffsetOffset(cxx_record_decl,
base_class_decl);
const lldb::addr_t base_offset_addr =
vtable_ptr + base_offset_offset.getQuantity();
const uint32_t base_offset_size = process.GetAddressByteSize();
Status err;
return process.ReadSignedIntegerFromMemory(base_offset_addr, base_offset_size,
INT64_MAX, err);
}
static bool GetVBaseBitOffset(VTableContextBase &vtable_ctx,
ValueObject &valobj,
const ASTRecordLayout &record_layout,
const CXXRecordDecl *cxx_record_decl,
const CXXRecordDecl *base_class_decl,
int32_t &bit_offset) {
ExecutionContext exe_ctx(valobj.GetExecutionContextRef());
Process *process = exe_ctx.GetProcessPtr();
if (!process)
return false;
lldb::addr_t vtable_ptr =
GetVTableAddress(*process, vtable_ctx, valobj, record_layout);
if (vtable_ptr == LLDB_INVALID_ADDRESS)
return false;
auto base_offset = ReadVBaseOffsetFromVTable(
*process, vtable_ctx, vtable_ptr, cxx_record_decl, base_class_decl);
if (base_offset == INT64_MAX)
return false;
bit_offset = base_offset * 8;
return true;
}
typedef lldb_private::ThreadSafeDenseMap<clang::ASTContext *, ClangASTContext *>
ClangASTMap;
@ -6545,80 +6661,12 @@ CompilerType ClangASTContext::GetChildCompilerTypeAtIndex(
if (base_class->isVirtual()) {
bool handled = false;
if (valobj) {
Status err;
AddressType addr_type = eAddressTypeInvalid;
lldb::addr_t vtable_ptr_addr =
valobj->GetCPPVTableAddress(addr_type);
if (vtable_ptr_addr != LLDB_INVALID_ADDRESS &&
addr_type == eAddressTypeLoad) {
ExecutionContext exe_ctx(valobj->GetExecutionContextRef());
Process *process = exe_ctx.GetProcessPtr();
if (process) {
clang::VTableContextBase *vtable_ctx =
getASTContext()->getVTableContext();
if (vtable_ctx) {
if (vtable_ctx->isMicrosoft()) {
clang::MicrosoftVTableContext *msoft_vtable_ctx =
static_cast<clang::MicrosoftVTableContext *>(
vtable_ctx);
if (vtable_ptr_addr) {
const lldb::addr_t vbtable_ptr_addr =
vtable_ptr_addr +
record_layout.getVBPtrOffset().getQuantity();
const lldb::addr_t vbtable_ptr =
process->ReadPointerFromMemory(vbtable_ptr_addr,
err);
if (vbtable_ptr != LLDB_INVALID_ADDRESS) {
// Get the index into the virtual base table. The
// index is the index in uint32_t from vbtable_ptr
const unsigned vbtable_index =
msoft_vtable_ctx->getVBTableIndex(
cxx_record_decl, base_class_decl);
const lldb::addr_t base_offset_addr =
vbtable_ptr + vbtable_index * 4;
const int32_t base_offset =
process->ReadSignedIntegerFromMemory(
base_offset_addr, 4, INT32_MAX, err);
if (base_offset != INT32_MAX) {
handled = true;
bit_offset = base_offset * 8;
}
}
}
} else {
clang::ItaniumVTableContext *itanium_vtable_ctx =
static_cast<clang::ItaniumVTableContext *>(
vtable_ctx);
if (vtable_ptr_addr) {
const lldb::addr_t vtable_ptr =
process->ReadPointerFromMemory(vtable_ptr_addr,
err);
if (vtable_ptr != LLDB_INVALID_ADDRESS) {
clang::CharUnits base_offset_offset =
itanium_vtable_ctx->getVirtualBaseOffsetOffset(
cxx_record_decl, base_class_decl);
const lldb::addr_t base_offset_addr =
vtable_ptr + base_offset_offset.getQuantity();
const uint32_t base_offset_size =
process->GetAddressByteSize();
const int64_t base_offset =
process->ReadSignedIntegerFromMemory(
base_offset_addr, base_offset_size,
UINT32_MAX, err);
if (base_offset < UINT32_MAX) {
handled = true;
bit_offset = base_offset * 8;
}
}
}
}
}
}
}
clang::VTableContextBase *vtable_ctx =
getASTContext()->getVTableContext();
if (vtable_ctx)
handled = GetVBaseBitOffset(*vtable_ctx, *valobj,
record_layout, cxx_record_decl,
base_class_decl, bit_offset);
}
if (!handled)
bit_offset = record_layout.getVBaseClassOffset(base_class_decl)