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