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