| 1 | //===-- ValueObjectVTable.cpp ---------------------------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #include "lldb/ValueObject/ValueObjectVTable.h" |
| 10 | #include "lldb/Core/Module.h" |
| 11 | #include "lldb/Symbol/Function.h" |
| 12 | #include "lldb/Target/Language.h" |
| 13 | #include "lldb/Target/LanguageRuntime.h" |
| 14 | #include "lldb/ValueObject/ValueObjectChild.h" |
| 15 | #include "lldb/lldb-defines.h" |
| 16 | #include "lldb/lldb-enumerations.h" |
| 17 | #include "lldb/lldb-forward.h" |
| 18 | #include "lldb/lldb-private-enumerations.h" |
| 19 | |
| 20 | using namespace lldb; |
| 21 | using namespace lldb_private; |
| 22 | |
| 23 | class ValueObjectVTableChild : public ValueObject { |
| 24 | public: |
| 25 | ValueObjectVTableChild(ValueObject &parent, uint32_t func_idx, |
| 26 | uint64_t addr_size) |
| 27 | : ValueObject(parent), m_func_idx(func_idx), m_addr_size(addr_size) { |
| 28 | SetFormat(eFormatPointer); |
| 29 | SetName(ConstString(llvm::formatv(Fmt: "[{0}]" , Vals&: func_idx).str())); |
| 30 | } |
| 31 | |
| 32 | ~ValueObjectVTableChild() override = default; |
| 33 | |
| 34 | llvm::Expected<uint64_t> GetByteSize() override { return m_addr_size; }; |
| 35 | |
| 36 | llvm::Expected<uint32_t> CalculateNumChildren(uint32_t max) override { |
| 37 | return 0; |
| 38 | }; |
| 39 | |
| 40 | ValueType GetValueType() const override { return eValueTypeVTableEntry; }; |
| 41 | |
| 42 | bool IsInScope() override { |
| 43 | if (ValueObject *parent = GetParent()) |
| 44 | return parent->IsInScope(); |
| 45 | return false; |
| 46 | }; |
| 47 | |
| 48 | protected: |
| 49 | bool UpdateValue() override { |
| 50 | SetValueIsValid(false); |
| 51 | m_value.Clear(); |
| 52 | ValueObject *parent = GetParent(); |
| 53 | if (!parent) { |
| 54 | m_error = Status::FromErrorString(str: "owning vtable object not valid" ); |
| 55 | return false; |
| 56 | } |
| 57 | |
| 58 | addr_t parent_addr = parent->GetValueAsUnsigned(LLDB_INVALID_ADDRESS); |
| 59 | if (parent_addr == LLDB_INVALID_ADDRESS) { |
| 60 | m_error = Status::FromErrorString(str: "invalid vtable address" ); |
| 61 | return false; |
| 62 | } |
| 63 | |
| 64 | ProcessSP process_sp = GetProcessSP(); |
| 65 | if (!process_sp) { |
| 66 | m_error = Status::FromErrorString(str: "no process" ); |
| 67 | return false; |
| 68 | } |
| 69 | |
| 70 | TargetSP target_sp = GetTargetSP(); |
| 71 | if (!target_sp) { |
| 72 | m_error = Status::FromErrorString(str: "no target" ); |
| 73 | return false; |
| 74 | } |
| 75 | |
| 76 | // Each `vtable_entry_addr` points to the function pointer. |
| 77 | addr_t vtable_entry_addr = parent_addr + m_func_idx * m_addr_size; |
| 78 | addr_t vfunc_ptr = |
| 79 | process_sp->ReadPointerFromMemory(vm_addr: vtable_entry_addr, error&: m_error); |
| 80 | if (m_error.Fail()) { |
| 81 | m_error = Status::FromErrorStringWithFormat( |
| 82 | format: "failed to read virtual function entry 0x%16.16" PRIx64, |
| 83 | vtable_entry_addr); |
| 84 | return false; |
| 85 | } |
| 86 | |
| 87 | // Set our value to be the load address of the function pointer in memory |
| 88 | // and our type to be the function pointer type. |
| 89 | m_value.SetValueType(Value::ValueType::LoadAddress); |
| 90 | m_value.GetScalar() = vtable_entry_addr; |
| 91 | |
| 92 | // See if our resolved address points to a function in the debug info. If |
| 93 | // it does, then we can report the type as a function prototype for this |
| 94 | // function. |
| 95 | Function *function = nullptr; |
| 96 | Address resolved_vfunc_ptr_address; |
| 97 | target_sp->ResolveLoadAddress(load_addr: vfunc_ptr, so_addr&: resolved_vfunc_ptr_address); |
| 98 | if (resolved_vfunc_ptr_address.IsValid()) |
| 99 | function = resolved_vfunc_ptr_address.CalculateSymbolContextFunction(); |
| 100 | if (function) { |
| 101 | m_value.SetCompilerType(function->GetCompilerType().GetPointerType()); |
| 102 | } else { |
| 103 | // Set our value's compiler type to a generic function protoype so that |
| 104 | // it displays as a hex function pointer for the value and the summary |
| 105 | // will display the address description. |
| 106 | |
| 107 | // Get the original type that this vtable is based off of so we can get |
| 108 | // the language from it correctly. |
| 109 | ValueObject *val = parent->GetParent(); |
| 110 | auto type_system = target_sp->GetScratchTypeSystemForLanguage( |
| 111 | language: val ? val->GetObjectRuntimeLanguage() : eLanguageTypeC_plus_plus); |
| 112 | if (type_system) { |
| 113 | m_value.SetCompilerType( |
| 114 | (*type_system)->CreateGenericFunctionPrototype().GetPointerType()); |
| 115 | } else { |
| 116 | consumeError(Err: type_system.takeError()); |
| 117 | } |
| 118 | } |
| 119 | |
| 120 | // Now read our value into m_data so that our we can use the default |
| 121 | // summary provider for C++ for function pointers which will get the |
| 122 | // address description for our function pointer. |
| 123 | if (m_error.Success()) { |
| 124 | const bool thread_and_frame_only_if_stopped = true; |
| 125 | ExecutionContext exe_ctx( |
| 126 | GetExecutionContextRef().Lock(thread_and_frame_only_if_stopped)); |
| 127 | m_error = m_value.GetValueAsData(exe_ctx: &exe_ctx, data&: m_data, module: GetModule().get()); |
| 128 | } |
| 129 | SetValueDidChange(true); |
| 130 | SetValueIsValid(true); |
| 131 | return true; |
| 132 | }; |
| 133 | |
| 134 | CompilerType GetCompilerTypeImpl() override { |
| 135 | return m_value.GetCompilerType(); |
| 136 | }; |
| 137 | |
| 138 | const uint32_t m_func_idx; |
| 139 | const uint64_t m_addr_size; |
| 140 | |
| 141 | private: |
| 142 | // For ValueObject only |
| 143 | ValueObjectVTableChild(const ValueObjectVTableChild &) = delete; |
| 144 | const ValueObjectVTableChild & |
| 145 | operator=(const ValueObjectVTableChild &) = delete; |
| 146 | }; |
| 147 | |
| 148 | ValueObjectSP ValueObjectVTable::Create(ValueObject &parent) { |
| 149 | return (new ValueObjectVTable(parent))->GetSP(); |
| 150 | } |
| 151 | |
| 152 | ValueObjectVTable::ValueObjectVTable(ValueObject &parent) |
| 153 | : ValueObject(parent) { |
| 154 | SetFormat(eFormatPointer); |
| 155 | } |
| 156 | |
| 157 | llvm::Expected<uint64_t> ValueObjectVTable::GetByteSize() { |
| 158 | if (m_vtable_symbol) |
| 159 | return m_vtable_symbol->GetByteSize(); |
| 160 | return llvm::createStringError(Fmt: "no symbol for vtable" ); |
| 161 | } |
| 162 | |
| 163 | llvm::Expected<uint32_t> ValueObjectVTable::CalculateNumChildren(uint32_t max) { |
| 164 | if (UpdateValueIfNeeded(update_format: false)) |
| 165 | return m_num_vtable_entries <= max ? m_num_vtable_entries : max; |
| 166 | return 0; |
| 167 | } |
| 168 | |
| 169 | ValueType ValueObjectVTable::GetValueType() const { return eValueTypeVTable; } |
| 170 | |
| 171 | ConstString ValueObjectVTable::GetTypeName() { |
| 172 | if (m_vtable_symbol) |
| 173 | return m_vtable_symbol->GetName(); |
| 174 | return ConstString(); |
| 175 | } |
| 176 | |
| 177 | ConstString ValueObjectVTable::GetQualifiedTypeName() { return GetTypeName(); } |
| 178 | |
| 179 | ConstString ValueObjectVTable::GetDisplayTypeName() { |
| 180 | if (m_vtable_symbol) |
| 181 | return m_vtable_symbol->GetDisplayName(); |
| 182 | return ConstString(); |
| 183 | } |
| 184 | |
| 185 | bool ValueObjectVTable::IsInScope() { return GetParent()->IsInScope(); } |
| 186 | |
| 187 | ValueObject *ValueObjectVTable::CreateChildAtIndex(size_t idx) { |
| 188 | return new ValueObjectVTableChild(*this, idx, m_addr_size); |
| 189 | } |
| 190 | |
| 191 | bool ValueObjectVTable::UpdateValue() { |
| 192 | m_error.Clear(); |
| 193 | m_flags.m_children_count_valid = false; |
| 194 | SetValueIsValid(false); |
| 195 | m_num_vtable_entries = 0; |
| 196 | ValueObject *parent = GetParent(); |
| 197 | if (!parent) { |
| 198 | m_error = Status::FromErrorString(str: "no parent object" ); |
| 199 | return false; |
| 200 | } |
| 201 | |
| 202 | ProcessSP process_sp = GetProcessSP(); |
| 203 | if (!process_sp) { |
| 204 | m_error = Status::FromErrorString(str: "no process" ); |
| 205 | return false; |
| 206 | } |
| 207 | |
| 208 | const LanguageType language = parent->GetObjectRuntimeLanguage(); |
| 209 | LanguageRuntime *language_runtime = process_sp->GetLanguageRuntime(language); |
| 210 | |
| 211 | if (language_runtime == nullptr) { |
| 212 | m_error = Status::FromErrorStringWithFormat( |
| 213 | format: "no language runtime support for the language \"%s\"" , |
| 214 | Language::GetNameForLanguageType(language)); |
| 215 | return false; |
| 216 | } |
| 217 | |
| 218 | // Get the vtable information from the language runtime. |
| 219 | llvm::Expected<LanguageRuntime::VTableInfo> vtable_info_or_err = |
| 220 | language_runtime->GetVTableInfo(in_value&: *parent, /*check_type=*/true); |
| 221 | if (!vtable_info_or_err) { |
| 222 | m_error = Status::FromError(error: vtable_info_or_err.takeError()); |
| 223 | return false; |
| 224 | } |
| 225 | |
| 226 | TargetSP target_sp = GetTargetSP(); |
| 227 | const addr_t vtable_start_addr = |
| 228 | vtable_info_or_err->addr.GetLoadAddress(target: target_sp.get()); |
| 229 | |
| 230 | m_vtable_symbol = vtable_info_or_err->symbol; |
| 231 | if (!m_vtable_symbol) { |
| 232 | m_error = Status::FromErrorStringWithFormat( |
| 233 | format: "no vtable symbol found containing 0x%" PRIx64, vtable_start_addr); |
| 234 | return false; |
| 235 | } |
| 236 | |
| 237 | // Now that we know it's a vtable, we update the object's state. |
| 238 | SetName(GetTypeName()); |
| 239 | |
| 240 | // Calculate the number of entries |
| 241 | if (!m_vtable_symbol->GetByteSizeIsValid()) { |
| 242 | m_error = Status::FromErrorStringWithFormat( |
| 243 | format: "vtable symbol \"%s\" doesn't have a valid size" , |
| 244 | m_vtable_symbol->GetMangled().GetDemangledName().GetCString()); |
| 245 | return false; |
| 246 | } |
| 247 | |
| 248 | m_addr_size = process_sp->GetAddressByteSize(); |
| 249 | const addr_t vtable_end_addr = |
| 250 | m_vtable_symbol->GetLoadAddress(target: target_sp.get()) + |
| 251 | m_vtable_symbol->GetByteSize(); |
| 252 | m_num_vtable_entries = (vtable_end_addr - vtable_start_addr) / m_addr_size; |
| 253 | |
| 254 | m_value.SetValueType(Value::ValueType::LoadAddress); |
| 255 | m_value.GetScalar() = parent->GetAddressOf().address; |
| 256 | auto type_system_or_err = |
| 257 | target_sp->GetScratchTypeSystemForLanguage(language: eLanguageTypeC_plus_plus); |
| 258 | if (type_system_or_err) { |
| 259 | m_value.SetCompilerType( |
| 260 | (*type_system_or_err)->GetBasicTypeFromAST(basic_type: eBasicTypeUnsignedLong)); |
| 261 | } else { |
| 262 | consumeError(Err: type_system_or_err.takeError()); |
| 263 | } |
| 264 | SetValueDidChange(true); |
| 265 | SetValueIsValid(true); |
| 266 | return true; |
| 267 | } |
| 268 | |
| 269 | CompilerType ValueObjectVTable::GetCompilerTypeImpl() { return CompilerType(); } |
| 270 | |
| 271 | ValueObjectVTable::~ValueObjectVTable() = default; |
| 272 | |