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: |
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 | std::optional<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.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(vm_addr: vtable_entry_addr, error&: 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(load_addr: vfunc_ptr, so_addr&: 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 | language: val ? val->GetObjectRuntimeLanguage() : eLanguageTypeC_plus_plus); |
113 | if (type_system) { |
114 | m_value.SetCompilerType( |
115 | (*type_system)->CreateGenericFunctionPrototype().GetPointerType()); |
116 | } else { |
117 | consumeError(Err: 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: &exe_ctx, data&: m_data, module: GetModule().get()); |
129 | } |
130 | SetValueDidChange(true); |
131 | SetValueIsValid(true); |
132 | return true; |
133 | }; |
134 | |
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 | |
149 | ValueObjectSP ValueObjectVTable::Create(ValueObject &parent) { |
150 | return (new ValueObjectVTable(parent))->GetSP(); |
151 | } |
152 | |
153 | ValueObjectVTable::ValueObjectVTable(ValueObject &parent) |
154 | : ValueObject(parent) { |
155 | SetFormat(eFormatPointer); |
156 | } |
157 | |
158 | std::optional<uint64_t> ValueObjectVTable::GetByteSize() { |
159 | if (m_vtable_symbol) |
160 | return m_vtable_symbol->GetByteSize(); |
161 | return std::nullopt; |
162 | } |
163 | |
164 | llvm::Expected<uint32_t> ValueObjectVTable::CalculateNumChildren(uint32_t max) { |
165 | if (UpdateValueIfNeeded(update_format: false)) |
166 | return m_num_vtable_entries <= max ? m_num_vtable_entries : max; |
167 | return 0; |
168 | } |
169 | |
170 | ValueType ValueObjectVTable::GetValueType() const { return eValueTypeVTable; } |
171 | |
172 | ConstString ValueObjectVTable::GetTypeName() { |
173 | if (m_vtable_symbol) |
174 | return m_vtable_symbol->GetName(); |
175 | return ConstString(); |
176 | } |
177 | |
178 | ConstString ValueObjectVTable::GetQualifiedTypeName() { return GetTypeName(); } |
179 | |
180 | ConstString ValueObjectVTable::GetDisplayTypeName() { |
181 | if (m_vtable_symbol) |
182 | return m_vtable_symbol->GetDisplayName(); |
183 | return ConstString(); |
184 | } |
185 | |
186 | bool ValueObjectVTable::IsInScope() { return GetParent()->IsInScope(); } |
187 | |
188 | ValueObject *ValueObjectVTable::CreateChildAtIndex(size_t idx, |
189 | bool synthetic_array_member, |
190 | int32_t synthetic_index) { |
191 | if (synthetic_array_member) |
192 | return nullptr; |
193 | return new ValueObjectVTableChild(*this, idx, m_addr_size); |
194 | } |
195 | |
196 | bool ValueObjectVTable::UpdateValue() { |
197 | m_error.Clear(); |
198 | m_flags.m_children_count_valid = false; |
199 | SetValueIsValid(false); |
200 | m_num_vtable_entries = 0; |
201 | ValueObject *parent = GetParent(); |
202 | if (!parent) { |
203 | m_error.SetErrorString("no parent object" ); |
204 | return false; |
205 | } |
206 | |
207 | ProcessSP process_sp = GetProcessSP(); |
208 | if (!process_sp) { |
209 | m_error.SetErrorString("no process" ); |
210 | return false; |
211 | } |
212 | |
213 | const LanguageType language = parent->GetObjectRuntimeLanguage(); |
214 | LanguageRuntime *language_runtime = process_sp->GetLanguageRuntime(language); |
215 | |
216 | if (language_runtime == nullptr) { |
217 | m_error.SetErrorStringWithFormat( |
218 | "no language runtime support for the language \"%s\"" , |
219 | Language::GetNameForLanguageType(language)); |
220 | return false; |
221 | } |
222 | |
223 | // Get the vtable information from the language runtime. |
224 | llvm::Expected<LanguageRuntime::VTableInfo> vtable_info_or_err = |
225 | language_runtime->GetVTableInfo(in_value&: *parent, /*check_type=*/true); |
226 | if (!vtable_info_or_err) { |
227 | m_error = vtable_info_or_err.takeError(); |
228 | return false; |
229 | } |
230 | |
231 | TargetSP target_sp = GetTargetSP(); |
232 | const addr_t vtable_start_addr = |
233 | vtable_info_or_err->addr.GetLoadAddress(target: target_sp.get()); |
234 | |
235 | m_vtable_symbol = vtable_info_or_err->symbol; |
236 | if (!m_vtable_symbol) { |
237 | m_error.SetErrorStringWithFormat( |
238 | "no vtable symbol found containing 0x%" PRIx64, vtable_start_addr); |
239 | return false; |
240 | } |
241 | |
242 | // Now that we know it's a vtable, we update the object's state. |
243 | SetName(GetTypeName()); |
244 | |
245 | // Calculate the number of entries |
246 | if (!m_vtable_symbol->GetByteSizeIsValid()) { |
247 | m_error.SetErrorStringWithFormat( |
248 | "vtable symbol \"%s\" doesn't have a valid size" , |
249 | m_vtable_symbol->GetMangled().GetDemangledName().GetCString()); |
250 | return false; |
251 | } |
252 | |
253 | m_addr_size = process_sp->GetAddressByteSize(); |
254 | const addr_t vtable_end_addr = |
255 | m_vtable_symbol->GetLoadAddress(target: target_sp.get()) + |
256 | m_vtable_symbol->GetByteSize(); |
257 | m_num_vtable_entries = (vtable_end_addr - vtable_start_addr) / m_addr_size; |
258 | |
259 | m_value.SetValueType(Value::ValueType::LoadAddress); |
260 | m_value.GetScalar() = parent->GetAddressOf(); |
261 | auto type_system_or_err = |
262 | target_sp->GetScratchTypeSystemForLanguage(language: eLanguageTypeC_plus_plus); |
263 | if (type_system_or_err) { |
264 | m_value.SetCompilerType( |
265 | (*type_system_or_err)->GetBasicTypeFromAST(basic_type: eBasicTypeUnsignedLong)); |
266 | } else { |
267 | consumeError(Err: type_system_or_err.takeError()); |
268 | } |
269 | SetValueDidChange(true); |
270 | SetValueIsValid(true); |
271 | return true; |
272 | } |
273 | |
274 | CompilerType ValueObjectVTable::GetCompilerTypeImpl() { return CompilerType(); } |
275 | |
276 | ValueObjectVTable::~ValueObjectVTable() = default; |
277 | |