https://github.com/Nerixyz created https://github.com/llvm/llvm-project/pull/207010
The original PR was reverted in https://github.com/llvm/llvm-project/pull/206816 due to a test failure on lldb-aarch64-ubuntu. Since I couldn't reproduce the failure, I decided to split the PR into smaller chunks. This is part 1/4 (the final state is on https://github.com/Nerixyz/llvm-project/tree/refactor/common-abi-runtime-take2-4-of-4). It moves `GetVTableInfo` and `TypeHasVTable` from the Itanium ABI runtime to the C++ language runtime. Eventually, this will be used to select the ABI runtime that's able to handle a vtable. For now, we always ask and use Itanium. >From e51e40fc51ddcbf97212e4e1317ff5a46a5eb116 Mon Sep 17 00:00:00 2001 From: Nerixyz <[email protected]> Date: Wed, 1 Jul 2026 16:17:43 +0200 Subject: [PATCH] [lldb] Move GetVTableInfo to C++ language runtime --- .../CPlusPlus/CPPLanguageRuntime.cpp | 121 +++++++++++++++++- .../CPlusPlus/CPPLanguageRuntime.h | 10 ++ .../CPlusPlus/ItaniumABIRuntime.cpp | 118 +---------------- .../CPlusPlus/ItaniumABIRuntime.h | 6 +- 4 files changed, 137 insertions(+), 118 deletions(-) diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp index c517ec8611932..95830675a2b06 100644 --- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp +++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp @@ -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()); + 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 @@ -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 @@ -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); +} diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h index 7c3dade76d703..0ed1a71b976be 100644 --- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h +++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h @@ -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; + std::mutex m_vtable_mutex; }; } // namespace lldb_private diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABIRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABIRuntime.cpp index 4d9cf31c8904d..f62b4b3b4e695 100644 --- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABIRuntime.cpp +++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABIRuntime.cpp @@ -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) { @@ -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 @@ -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) diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABIRuntime.h b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABIRuntime.h index 75a8142b26d93..b276e7df60951 100644 --- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABIRuntime.h +++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABIRuntime.h @@ -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); @@ -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, _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
