Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
121 changes: 119 additions & 2 deletions lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -524,8 +524,16 @@ bool CPPLanguageRuntime::GetDynamicTypeAndAddress(
if (!CouldHaveDynamicValue(in_value))
return false;

llvm::Expected<VTableInfoEntry> entry =
GetVTableInfoEntry(in_value, /*check_type=*/false);
if (!entry) {
llvm::consumeError(entry.takeError());

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dropping the error here seems suspicious. But that' not relevant to this PR

return false;
}

return m_itanium_runtime.GetDynamicTypeAndAddress(
in_value, use_dynamic, class_type_or_name, dynamic_address, value_type);
in_value, use_dynamic, entry->info, class_type_or_name, dynamic_address,
value_type);
}

TypeAndOrName
Expand Down Expand Up @@ -588,7 +596,11 @@ void CPPLanguageRuntime::Terminate() {

llvm::Expected<LanguageRuntime::VTableInfo>
CPPLanguageRuntime::GetVTableInfo(ValueObject &in_value, bool check_type) {
return m_itanium_runtime.GetVTableInfo(in_value, check_type);
llvm::Expected<VTableInfoEntry> entry =
GetVTableInfoEntry(in_value, check_type);
if (!entry)
return entry.takeError();
return entry->info;
}

BreakpointResolverSP
Expand Down Expand Up @@ -686,3 +698,108 @@ lldb::ValueObjectSP
CPPLanguageRuntime::GetExceptionObjectForThread(lldb::ThreadSP thread_sp) {
return m_itanium_runtime.GetExceptionObjectForThread(std::move(thread_sp));
}

static llvm::Error TypeHasVTable(CompilerType type) {
// Check to make sure the class has a vtable.
CompilerType original_type = type;
if (type.IsPointerOrReferenceType()) {
CompilerType pointee_type = type.GetPointeeType();
if (pointee_type)
type = pointee_type;
}

// Make sure this is a class or a struct first by checking the type class
// bitfield that gets returned.
if ((type.GetTypeClass() & (eTypeClassStruct | eTypeClassClass)) == 0) {
return llvm::createStringError(
std::errc::invalid_argument,
"type \"%s\" is not a class or struct or a pointer to one",
original_type.GetTypeName().AsCString("<invalid>"));
}

// Check if the type has virtual functions by asking it if it is polymorphic.
if (!type.IsPolymorphicClass()) {
return llvm::createStringError(std::errc::invalid_argument,
"type \"%s\" doesn't have a vtable",
type.GetTypeName().AsCString("<invalid>"));
}
return llvm::Error::success();
}

// This function can accept both pointers or references to classes as well as
// instances of classes. If you are using this function during dynamic type
// detection, only valid ValueObjects that return true to
// CouldHaveDynamicValue(...) should call this function and \a check_type
// should be set to false. This function is also used by ValueObjectVTable
// and is can pass in instances of classes which is not suitable for dynamic
// type detection, these cases should pass true for \a check_type.
llvm::Expected<CPPLanguageRuntime::VTableInfoEntry>
CPPLanguageRuntime::GetVTableInfoEntry(ValueObject &in_value, bool check_type) {

CompilerType type = in_value.GetCompilerType();
if (check_type) {
if (llvm::Error err = TypeHasVTable(type))
return std::move(err);
}
ExecutionContext exe_ctx(in_value.GetExecutionContextRef());
Process *process = exe_ctx.GetProcessPtr();
if (process == nullptr)
return llvm::createStringError(std::errc::invalid_argument,
"invalid process");

auto [original_ptr, address_type] =
type.IsPointerOrReferenceType()
? in_value.GetPointerValue()
: in_value.GetAddressOf(/*scalar_is_load_address=*/true);
if (original_ptr == LLDB_INVALID_ADDRESS || address_type != eAddressTypeLoad)
return llvm::createStringError(std::errc::invalid_argument,
"failed to get the address of the value");

Status error;
lldb::addr_t vtable_load_addr =
process->ReadPointerFromMemory(original_ptr, error);

if (!error.Success() || vtable_load_addr == LLDB_INVALID_ADDRESS)
return llvm::createStringError(
std::errc::invalid_argument,
"failed to read vtable pointer from memory at 0x%" PRIx64,
original_ptr);

// The vtable load address can have authentication bits with
// AArch64 targets on Darwin.
vtable_load_addr = process->FixDataAddress(vtable_load_addr);

// Find the symbol that contains the "vtable_load_addr" address
Address vtable_addr;
if (!process->GetTarget().ResolveLoadAddress(vtable_load_addr, vtable_addr))
return llvm::createStringError(std::errc::invalid_argument,
"failed to resolve vtable pointer 0x%" PRIx64
"to a section",
vtable_load_addr);

// Check our cache first to see if we already have this info
{
std::lock_guard<std::mutex> locker(m_vtable_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 llvm::createStringError(std::errc::invalid_argument,
"no symbol found for 0x%" PRIx64,
vtable_load_addr);
if (m_itanium_runtime.IsVTableSymbol(symbol->GetMangled())) {
VTableInfoEntry entry{
/*info=*/VTableInfo{vtable_addr, symbol},
};
std::lock_guard<std::mutex> locker(m_vtable_mutex);
m_vtable_info_map[vtable_addr] = entry;
return entry;
}
return llvm::createStringError(std::errc::invalid_argument,
"symbol found that contains 0x%" PRIx64
" is not a vtable symbol",
vtable_load_addr);
}
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,16 @@ class CPPLanguageRuntime : public LanguageRuntime {

lldb::BreakpointSP m_cxx_exception_bp_sp;
ItaniumABIRuntime m_itanium_runtime;

struct VTableInfoEntry {
VTableInfo info;
};

llvm::Expected<VTableInfoEntry> GetVTableInfoEntry(ValueObject &in_value,
bool check_type);

std::map<Address, VTableInfoEntry> m_vtable_info_map;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can the corresponding member on the ItaniumABIRuntime be removed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes!

std::mutex m_vtable_mutex;
};

} // namespace lldb_private
Expand Down
118 changes: 6 additions & 112 deletions lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABIRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ static const char *vtable_demangled_prefix = "vtable for ";

ItaniumABIRuntime::ItaniumABIRuntime(Process *process) : m_process(process) {}

bool ItaniumABIRuntime::IsVTableSymbol(Mangled &mangled) const {
return mangled.GetDemangledName().GetStringRef().starts_with(
vtable_demangled_prefix);
}

TypeAndOrName
ItaniumABIRuntime::GetTypeInfo(ValueObject &in_value,
const LanguageRuntime::VTableInfo &vtable_info) {
Expand Down Expand Up @@ -145,112 +150,9 @@ ItaniumABIRuntime::GetTypeInfo(ValueObject &in_value,
return TypeAndOrName();
}

llvm::Error ItaniumABIRuntime::TypeHasVTable(CompilerType type) {
// Check to make sure the class has a vtable.
CompilerType original_type = type;
if (type.IsPointerOrReferenceType()) {
CompilerType pointee_type = type.GetPointeeType();
if (pointee_type)
type = pointee_type;
}

// Make sure this is a class or a struct first by checking the type class
// bitfield that gets returned.
if ((type.GetTypeClass() & (eTypeClassStruct | eTypeClassClass)) == 0) {
return llvm::createStringError(
std::errc::invalid_argument,
"type \"%s\" is not a class or struct or a pointer to one",
original_type.GetTypeName().AsCString("<invalid>"));
}

// Check if the type has virtual functions by asking it if it is polymorphic.
if (!type.IsPolymorphicClass()) {
return llvm::createStringError(std::errc::invalid_argument,
"type \"%s\" doesn't have a vtable",
type.GetTypeName().AsCString("<invalid>"));
}
return llvm::Error::success();
}

// This function can accept both pointers or references to classes as well as
// instances of classes. If you are using this function during dynamic type
// detection, only valid ValueObjects that return true to
// CouldHaveDynamicValue(...) should call this function and \a check_type
// should be set to false. This function is also used by ValueObjectVTable
// and is can pass in instances of classes which is not suitable for dynamic
// type detection, these cases should pass true for \a check_type.
llvm::Expected<LanguageRuntime::VTableInfo>
ItaniumABIRuntime::GetVTableInfo(ValueObject &in_value, bool check_type) {

CompilerType type = in_value.GetCompilerType();
if (check_type) {
if (llvm::Error err = TypeHasVTable(type))
return std::move(err);
}
ExecutionContext exe_ctx(in_value.GetExecutionContextRef());
Process *process = exe_ctx.GetProcessPtr();
if (process == nullptr)
return llvm::createStringError(std::errc::invalid_argument,
"invalid process");

auto [original_ptr, address_type] =
type.IsPointerOrReferenceType()
? in_value.GetPointerValue()
: in_value.GetAddressOf(/*scalar_is_load_address=*/true);
if (original_ptr == LLDB_INVALID_ADDRESS || address_type != eAddressTypeLoad)
return llvm::createStringError(std::errc::invalid_argument,
"failed to get the address of the value");

Status error;
lldb::addr_t vtable_load_addr =
process->ReadPointerFromMemory(original_ptr, error);

if (!error.Success() || vtable_load_addr == LLDB_INVALID_ADDRESS)
return llvm::createStringError(
std::errc::invalid_argument,
"failed to read vtable pointer from memory at 0x%" PRIx64,
original_ptr);

// The vtable load address can have authentication bits with
// AArch64 targets on Darwin.
vtable_load_addr = process->FixDataAddress(vtable_load_addr);

// Find the symbol that contains the "vtable_load_addr" address
Address vtable_addr;
if (!process->GetTarget().ResolveLoadAddress(vtable_load_addr, vtable_addr))
return llvm::createStringError(std::errc::invalid_argument,
"failed to resolve vtable pointer 0x%" PRIx64
"to a section",
vtable_load_addr);

// 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 llvm::createStringError(std::errc::invalid_argument,
"no symbol found for 0x%" PRIx64,
vtable_load_addr);
llvm::StringRef name = symbol->GetMangled().GetDemangledName().GetStringRef();
if (name.starts_with(vtable_demangled_prefix)) {
LanguageRuntime::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 llvm::createStringError(std::errc::invalid_argument,
"symbol found that contains 0x%" PRIx64
" is not a vtable symbol",
vtable_load_addr);
}

bool ItaniumABIRuntime::GetDynamicTypeAndAddress(
ValueObject &in_value, lldb::DynamicValueType use_dynamic,
const LanguageRuntime::VTableInfo &vtable_info,
TypeAndOrName &class_type_or_name, Address &dynamic_address,
Value::ValueType &value_type) {
// For Itanium, if the type has a vtable pointer in the object, it will be at
Expand All @@ -266,14 +168,6 @@ bool ItaniumABIRuntime::GetDynamicTypeAndAddress(
// want GetVTableInfo to check the type since we accept void * as a possible
// dynamic type and that won't pass the type check. We already checked the
// type above in CouldHaveDynamicValue(...).
llvm::Expected<LanguageRuntime::VTableInfo> vtable_info_or_err =
GetVTableInfo(in_value, /*check_type=*/false);
if (!vtable_info_or_err) {
llvm::consumeError(vtable_info_or_err.takeError());
return false;
}

const LanguageRuntime::VTableInfo &vtable_info = vtable_info_or_err.get();
class_type_or_name = GetTypeInfo(in_value, vtable_info);

if (!class_type_or_name)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ class ItaniumABIRuntime {
public:
ItaniumABIRuntime(Process *process);

llvm::Expected<LanguageRuntime::VTableInfo>
GetVTableInfo(ValueObject &in_value, bool check_type);
bool IsVTableSymbol(Mangled &manged) const;

bool GetDynamicTypeAndAddress(ValueObject &in_value,
lldb::DynamicValueType use_dynamic,
const LanguageRuntime::VTableInfo &vtable_info,
TypeAndOrName &class_type_or_name,
Address &dynamic_address,
Value::ValueType &value_type);
Expand All @@ -42,8 +42,6 @@ class ItaniumABIRuntime {
TypeAndOrName GetTypeInfo(ValueObject &in_value,
const LanguageRuntime::VTableInfo &vtable_info);

llvm::Error TypeHasVTable(CompilerType type);

TypeAndOrName GetDynamicTypeInfo(const lldb_private::Address &vtable_addr);

void SetDynamicTypeInfo(const lldb_private::Address &vtable_addr,
Expand Down