| //===-- ValueObjectVTable.cpp ---------------------------------------------===// | |
| // | |
| // 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 | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "lldb/ValueObject/ValueObjectVTable.h" | |
| #include "lldb/Core/Module.h" | |
| #include "lldb/Symbol/Function.h" | |
| #include "lldb/Target/Language.h" | |
| #include "lldb/Target/LanguageRuntime.h" | |
| #include "lldb/ValueObject/ValueObjectChild.h" | |
| #include "lldb/lldb-defines.h" | |
| #include "lldb/lldb-enumerations.h" | |
| #include "lldb/lldb-forward.h" | |
| #include "lldb/lldb-private-enumerations.h" | |
| #include "llvm/Support/Error.h" | |
| using namespace lldb; | |
| using namespace lldb_private; | |
| class ValueObjectVTableChild : public ValueObject { | |
| public: | |
| ValueObjectVTableChild(ValueObject &parent, uint32_t func_idx, | |
| uint64_t addr_size) | |
| : ValueObject(parent), m_func_idx(func_idx), m_addr_size(addr_size) { | |
| SetFormat(eFormatPointer); | |
| SetName(llvm::formatv("[{0}]", func_idx).str()); | |
| } | |
| ~ValueObjectVTableChild() override = default; | |
| llvm::Expected<uint64_t> GetByteSize() override { return m_addr_size; }; | |
| llvm::Expected<uint32_t> CalculateNumChildren(uint32_t max) override { | |
| return 0; | |
| }; | |
| ValueType GetValueType() const override { return eValueTypeVTableEntry; }; | |
| bool IsInScope() override { | |
| if (ValueObject *parent = GetParent()) | |
| return parent->IsInScope(); | |
| return false; | |
| }; | |
| protected: | |
| bool UpdateValue() override { | |
| SetValueIsValid(false); | |
| m_value.Clear(); | |
| ValueObject *parent = GetParent(); | |
| if (!parent) { | |
| m_error = Status::FromErrorString("owning vtable object not valid"); | |
| return false; | |
| } | |
| addr_t parent_addr = parent->GetValueAsUnsigned(LLDB_INVALID_ADDRESS); | |
| if (parent_addr == LLDB_INVALID_ADDRESS) { | |
| m_error = Status::FromErrorString("invalid vtable address"); | |
| return false; | |
| } | |
| ProcessSP process_sp = GetProcessSP(); | |
| if (!process_sp) { | |
| m_error = Status::FromErrorString("no process"); | |
| return false; | |
| } | |
| TargetSP target_sp = GetTargetSP(); | |
| if (!target_sp) { | |
| m_error = Status::FromErrorString("no target"); | |
| return false; | |
| } | |
| parent_addr = process_sp->FixCodeAddress(parent_addr); | |
| // Each `vtable_entry_addr` points to the function pointer. | |
| addr_t vtable_entry_addr = parent_addr + m_func_idx * m_addr_size; | |
| llvm::Expected<lldb::addr_t> vfunc_ptr_or_err = | |
| process_sp->ReadPointerFromMemory(vtable_entry_addr); | |
| if (!vfunc_ptr_or_err) { | |
| llvm::consumeError(vfunc_ptr_or_err.takeError()); | |
| m_error = Status::FromErrorStringWithFormat( | |
| "failed to read virtual function entry 0x%16.16" PRIx64, | |
| vtable_entry_addr); | |
| return false; | |
| } | |
| addr_t vfunc_ptr = *vfunc_ptr_or_err; | |
| vfunc_ptr = process_sp->FixCodeAddress(vfunc_ptr); | |
| // Set our value to be the load address of the function pointer in memory | |
| // and our type to be the function pointer type. | |
| m_value.SetValueType(Value::ValueType::LoadAddress); | |
| m_value.GetScalar() = vtable_entry_addr; | |
| // See if our resolved address points to a function in the debug info. If | |
| // it does, then we can report the type as a function prototype for this | |
| // function. | |
| Function *function = nullptr; | |
| Address resolved_vfunc_ptr_address; | |
| target_sp->ResolveLoadAddress(vfunc_ptr, resolved_vfunc_ptr_address); | |
| if (resolved_vfunc_ptr_address.IsValid()) | |
| function = resolved_vfunc_ptr_address.CalculateSymbolContextFunction(); | |
| if (function) { | |
| m_value.SetCompilerType(function->GetCompilerType().GetPointerType()); | |
| } else { | |
| // Set our value's compiler type to a generic function protoype so that | |
| // it displays as a hex function pointer for the value and the summary | |
| // will display the address description. | |
| // Get the original type that this vtable is based off of so we can get | |
| // the language from it correctly. | |
| ValueObject *val = parent->GetParent(); | |
| auto type_system = target_sp->GetScratchTypeSystemForLanguage( | |
| val ? val->GetObjectRuntimeLanguage() : eLanguageTypeC_plus_plus); | |
| if (type_system) { | |
| m_value.SetCompilerType( | |
| (*type_system)->CreateGenericFunctionPrototype().GetPointerType()); | |
| } else { | |
| consumeError(type_system.takeError()); | |
| } | |
| } | |
| // Now read our value into m_data so that our we can use the default | |
| // summary provider for C++ for function pointers which will get the | |
| // address description for our function pointer. | |
| if (m_error.Success()) { | |
| const bool thread_and_frame_only_if_stopped = true; | |
| ExecutionContext exe_ctx( | |
| GetExecutionContextRef().Lock(thread_and_frame_only_if_stopped)); | |
| m_error = m_value.GetValueAsData(&exe_ctx, m_data, GetModule().get()); | |
| } | |
| SetValueDidChange(true); | |
| SetValueIsValid(true); | |
| return true; | |
| }; | |
| CompilerType GetCompilerTypeImpl() override { | |
| return m_value.GetCompilerType(); | |
| }; | |
| const uint32_t m_func_idx; | |
| const uint64_t m_addr_size; | |
| private: | |
| // For ValueObject only | |
| ValueObjectVTableChild(const ValueObjectVTableChild &) = delete; | |
| const ValueObjectVTableChild & | |
| operator=(const ValueObjectVTableChild &) = delete; | |
| }; | |
| ValueObjectSP ValueObjectVTable::Create(ValueObject &parent) { | |
| return (new ValueObjectVTable(parent))->GetSP(); | |
| } | |
| ValueObjectVTable::ValueObjectVTable(ValueObject &parent) | |
| : ValueObject(parent) { | |
| SetFormat(eFormatPointer); | |
| } | |
| llvm::Expected<uint64_t> ValueObjectVTable::GetByteSize() { | |
| if (m_vtable_symbol) | |
| return m_vtable_symbol->GetByteSize(); | |
| return llvm::createStringError("no symbol for vtable"); | |
| } | |
| llvm::Expected<uint32_t> ValueObjectVTable::CalculateNumChildren(uint32_t max) { | |
| if (UpdateValueIfNeeded(false)) | |
| return m_num_vtable_entries <= max ? m_num_vtable_entries : max; | |
| return 0; | |
| } | |
| ValueType ValueObjectVTable::GetValueType() const { return eValueTypeVTable; } | |
| ConstString ValueObjectVTable::GetTypeName() { | |
| if (m_vtable_symbol) | |
| return m_vtable_symbol->GetName(); | |
| return ConstString(); | |
| } | |
| ConstString ValueObjectVTable::GetQualifiedTypeName() { return GetTypeName(); } | |
| ConstString ValueObjectVTable::GetDisplayTypeName() { | |
| if (m_vtable_symbol) | |
| return m_vtable_symbol->GetDisplayName(); | |
| return ConstString(); | |
| } | |
| bool ValueObjectVTable::IsInScope() { return GetParent()->IsInScope(); } | |
| ValueObject *ValueObjectVTable::CreateChildAtIndex(size_t idx) { | |
| return new ValueObjectVTableChild(*this, idx, m_addr_size); | |
| } | |
| bool ValueObjectVTable::UpdateValue() { | |
| m_error.Clear(); | |
| m_flags.m_children_count_valid = false; | |
| SetValueIsValid(false); | |
| m_num_vtable_entries = 0; | |
| ValueObject *parent = GetParent(); | |
| if (!parent) { | |
| m_error = Status::FromErrorString("no parent object"); | |
| return false; | |
| } | |
| ProcessSP process_sp = GetProcessSP(); | |
| if (!process_sp) { | |
| m_error = Status::FromErrorString("no process"); | |
| return false; | |
| } | |
| const LanguageType language = parent->GetObjectRuntimeLanguage(); | |
| LanguageRuntime *language_runtime = process_sp->GetLanguageRuntime(language); | |
| if (language_runtime == nullptr) { | |
| m_error = Status::FromErrorStringWithFormat( | |
| "no language runtime support for the language \"%s\"", | |
| Language::GetNameForLanguageType(language)); | |
| return false; | |
| } | |
| // Get the vtable information from the language runtime. | |
| llvm::Expected<LanguageRuntime::VTableInfo> vtable_info_or_err = | |
| language_runtime->GetVTableInfo(*parent, /*check_type=*/true); | |
| if (!vtable_info_or_err) { | |
| m_error = Status::FromError(vtable_info_or_err.takeError()); | |
| return false; | |
| } | |
| TargetSP target_sp = GetTargetSP(); | |
| const addr_t vtable_start_addr = | |
| vtable_info_or_err->addr.GetLoadAddress(target_sp.get()); | |
| m_vtable_symbol = vtable_info_or_err->symbol; | |
| if (!m_vtable_symbol) { | |
| m_error = Status::FromErrorStringWithFormat( | |
| "no vtable symbol found containing 0x%" PRIx64, vtable_start_addr); | |
| return false; | |
| } | |
| // Now that we know it's a vtable, we update the object's state. | |
| SetName(GetTypeName()); | |
| // Calculate the number of entries | |
| if (!m_vtable_symbol->GetByteSizeIsValid()) { | |
| m_error = Status::FromErrorStringWithFormat( | |
| "vtable symbol \"%s\" doesn't have a valid size", | |
| m_vtable_symbol->GetMangled().GetDemangledName().GetCString()); | |
| return false; | |
| } | |
| m_addr_size = process_sp->GetAddressByteSize(); | |
| const addr_t vtable_end_addr = | |
| m_vtable_symbol->GetLoadAddress(target_sp.get()) + | |
| m_vtable_symbol->GetByteSize(); | |
| m_num_vtable_entries = (vtable_end_addr - vtable_start_addr) / m_addr_size; | |
| m_value.SetValueType(Value::ValueType::LoadAddress); | |
| m_value.GetScalar() = parent->GetAddressOf().address; | |
| auto type_system_or_err = | |
| target_sp->GetScratchTypeSystemForLanguage(eLanguageTypeC_plus_plus); | |
| if (type_system_or_err) { | |
| m_value.SetCompilerType( | |
| (*type_system_or_err)->GetBasicTypeFromAST(eBasicTypeUnsignedLong)); | |
| } else { | |
| consumeError(type_system_or_err.takeError()); | |
| } | |
| SetValueDidChange(true); | |
| SetValueIsValid(true); | |
| return true; | |
| } | |
| CompilerType ValueObjectVTable::GetCompilerTypeImpl() { return CompilerType(); } | |
| ValueObjectVTable::~ValueObjectVTable() = default; |