LLDB mainline
MicrosoftABIRuntime.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
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
10
13#include "clang/AST/RecordLayout.h"
14
15#include <deque>
16#include <mutex>
17
18using namespace lldb;
19using namespace lldb_private;
20
21static const llvm::StringRef s_vtable_demangled_indicator = "::`vftable'";
22static const llvm::StringRef s_rtti_complete_object_locator_indicator =
23 "::`RTTI Complete Object Locator'";
24
25static llvm::StringRef StripDemangledVTableSuffix(llvm::StringRef demangled) {
26 llvm::StringRef name = demangled.split(s_vtable_demangled_indicator).first;
27 name.consume_front("const ");
28 return name;
29}
30
31static bool
32SplitVTableComponents(llvm::StringRef demangled,
34 llvm::StringRef suffix = demangled.split(s_vtable_demangled_indicator).second;
35 if (suffix.empty())
36 return true; // e.g. MyClass::`vftable'
37
38 // Otherwise, suffix looks like "{for `MyBase2's `MyBase1'}" or
39 // "{for `MyBase'}".
40 if (!suffix.consume_front("{for `") || !suffix.consume_back("'}"))
41 return false;
42
43 suffix.split(components, "'s `");
44 return true;
45}
46
49
51 ValueObject &in_value, lldb::DynamicValueType use_dynamic,
52 const LanguageRuntime::VTableInfo &vtable_info,
53 TypeAndOrName &class_type_or_name, Address &dynamic_address) {
54 std::optional<int64_t> offset_to_top =
55 GetOffsetToTop(in_value, class_type_or_name, vtable_info);
56 if (!offset_to_top)
57 return false;
58
59 lldb::addr_t dynamic_addr =
60 in_value.GetPointerValue().address - *offset_to_top;
61 if (!m_process->GetTarget().ResolveLoadAddress(dynamic_addr, dynamic_address))
62 dynamic_address.SetRawAddress(dynamic_addr);
63
64 return true;
65}
66
68 return mangled.GetDemangledName().GetStringRef().contains(
70}
71
73 ValueObject &in_value, const LanguageRuntime::VTableInfo &vtable_info,
74 llvm::SmallVectorImpl<llvm::StringRef> &vtable_components) {
75 if (!vtable_info.addr.IsSectionOffset() || !vtable_info.symbol)
76 return {};
77
78 // See if we have cached info for this type already
79 TypeAndOrName type_info = GetDynamicTypeInfo(vtable_info.addr);
80 if (type_info)
81 return type_info;
82
84 llvm::StringRef symbol_name =
86 if (!SplitVTableComponents(symbol_name, vtable_components)) {
87 LLDB_LOG(log, "{0:x16}: '{1}' is not a valid vtable symbol",
88 in_value.GetPointerValue().address, symbol_name);
89 return {};
90 }
91
92 llvm::StringRef class_name = StripDemangledVTableSuffix(symbol_name);
93
94 type_info.SetName(class_name);
95 bool any_found = false;
96 TypeSP type_sp = LookupTypeByName(
97 class_name, vtable_info.symbol->CalculateSymbolContextModule(),
98 any_found);
99 if (!any_found)
100 return TypeAndOrName(); // Type is not dynamic.
101
102 if (type_sp) {
103 LLDB_LOG(log,
104 "static-type = '{0}' has dynamic type: uid={1:x}, type-name='{2}'",
105 in_value.GetTypeName(), type_sp->GetID(), type_sp->GetName());
106 type_info.SetTypeSP(std::move(type_sp));
107 }
108
109 if (type_info)
110 SetDynamicTypeInfo(vtable_info.addr, type_info);
111 return type_info;
112}
113
115 ValueObject &in_value, TypeAndOrName &class_type_or_name,
116 const LanguageRuntime::VTableInfo &vtable_info) {
117 Log *log = GetLog(LLDBLog::Object);
118
119 llvm::SmallVector<llvm::StringRef, 2> vtable_components;
120 class_type_or_name = GetTypeInfo(in_value, vtable_info, vtable_components);
121 if (!class_type_or_name) {
122 LLDB_LOG(log, "{0:x16}: failed to get type info",
123 in_value.GetPointerValue().address);
124 return std::nullopt;
125 }
126
127 CompilerType type = class_type_or_name.GetCompilerType();
128 if (!type) {
129 LLDB_LOG(log, "{0:x16}: failed to get type info (no compiler type)",
130 in_value.GetPointerValue().address);
131 return std::nullopt;
132 }
133
134 CompilerType in_type = in_value.GetCompilerType();
135 if (in_type.IsPointerOrReferenceType())
136 in_type = in_type.GetPointeeType();
137 if (!in_type.GetCompleteType()) {
138 LLDB_LOG(log, "{0:x16}: {1} is not a complete type",
139 in_value.GetPointerValue().address, in_type.GetTypeName());
140 return std::nullopt;
141 }
142 if (!type.GetCompleteType()) {
143 LLDB_LOG(log, "{0:x16}: {1} is not a complete type",
144 in_value.GetPointerValue().address,
145 type.GetTypeName().GetCString());
146 return std::nullopt;
147 }
148
149 if (TypeSystemClang::AreTypesSame(in_type, type)) {
150 LLDB_LOG(log, "{0:x16}: in type is the same as the dynamic type",
151 in_value.GetPointerValue().address);
152
153 // The dynamic type we found was the same type, so we don't have a
154 // dynamic type here, but if a derived type encounters this vtable symbol,
155 // remember that it has offset 0.
156 std::scoped_lock<std::mutex> g(m_mutex);
157 m_offset_to_top_map[vtable_info.addr] = 0;
158 return std::nullopt;
159 }
160
161 {
162 std::scoped_lock<std::mutex> g(m_mutex);
163 auto offset_to_top_it = m_offset_to_top_map.find(vtable_info.addr);
164 if (offset_to_top_it != m_offset_to_top_map.end())
165 return offset_to_top_it->second;
166 }
167
168 // Unlike in the Itanium ABI, the VTable itself doesn't have a field telling
169 // us the offset to the dynamic type.
170 // When compiled with RTTI, we can use the complete object locator to get this
171 // offset. This is preferred, because it can resolve types across different
172 // modules and handle virtual bases with PDB.
173 // If this fails (e.g. no RTTI available), the offset is determined from the
174 // AST. This requires the types to be from the same type system. Because PDB
175 // doesn't include the offsets of virtual bases, we don't attempt to resolve
176 // these. They're technically present in the AST, but they often have
177 // incorrect offsets.
178 std::optional<int64_t> offset = GetOffsetToTopFromRTTI(vtable_info, type);
179 llvm::StringRef strategy = "RTTI";
180 if (!offset) {
181 offset = GetOffsetToTopFromAST(in_type, in_value, type, vtable_info,
182 vtable_components);
183 strategy = "AST";
184 }
185
186 if (offset) {
188 "{0:x16}: [{1}] '{2}' has offset {3} in '{4}'",
189 in_value.GetPointerValue().address, strategy,
190 in_type.GetTypeName(), *offset, type.GetTypeName());
191
192 // TODO: Ideally, we could also store the case where we can't find the
193 // offset. This would need to be invalidated when new modules or debug info
194 // is loaded, as those could contain info about the vtables.
195 std::scoped_lock<std::mutex> g(m_mutex);
196 m_offset_to_top_map[vtable_info.addr] = *offset;
197 }
198
199 return offset;
200}
201
203 const LanguageRuntime::VTableInfo &vtable_info,
204 const CompilerType &dynamic_type) {
205 // The pointer to the RTTI complete object locator sits one pointer size above
206 // the vftable.
207 Target &target = m_process->GetTarget();
208 const addr_t vtable_load_addr = vtable_info.addr.GetLoadAddress(&target);
209 if (vtable_load_addr == LLDB_INVALID_ADDRESS)
210 return std::nullopt;
211 const uint32_t addr_byte_size = m_process->GetAddressByteSize();
212 const lldb::addr_t complete_object_locator_load_addr =
213 vtable_load_addr - addr_byte_size;
214 // Check for underflow.
215 if (complete_object_locator_load_addr >= vtable_load_addr)
216 return std::nullopt;
217
218 Log *log = GetLog(LLDBLog::Object);
219 // Pointer to the RTTI complete object locator.
220 llvm::Expected<lldb::addr_t> rtti_locator_base_load_addr =
221 m_process->ReadPointerFromMemory(complete_object_locator_load_addr);
222 if (!rtti_locator_base_load_addr ||
223 *rtti_locator_base_load_addr == LLDB_INVALID_ADDRESS) {
224 LLDB_LOG_ERROR(log, rtti_locator_base_load_addr.takeError(),
225 "Failed to read address of RTTI complete object locator at "
226 "{1:x16}: {0}",
227 complete_object_locator_load_addr);
228 return std::nullopt;
229 }
230
232 Address rtti_locator_base_addr;
233 if (!m_process->GetTarget().ResolveLoadAddress(*rtti_locator_base_load_addr,
234 rtti_locator_base_addr)) {
235 LLDB_LOG_ERROR(log, error.takeError(),
236 "Failed to resolve load address of RTTI complete object "
237 "locator at {1:x16}: {0}",
238 *rtti_locator_base_load_addr);
239 return std::nullopt;
240 }
241
242 // Check if we're actually looking at the RTTI complete object locator.
243 Symbol *symbol = rtti_locator_base_addr.CalculateSymbolContextSymbol();
244 if (symbol == nullptr)
245 return std::nullopt;
246 if (!symbol->GetName().GetStringRef().contains(
248 return std::nullopt;
249
250 LLDB_LOG(log, "Found RTTI complete object locator: {0:x16}",
251 *rtti_locator_base_load_addr);
252
253 auto ts = dynamic_type.GetTypeSystem();
254 if (!ts)
255 return std::nullopt;
256
257 // On all supported platforms running the MS ABI, `int` is four bytes.
258 constexpr uint32_t int_size = 4;
259
260 // See MSRTTIBuilder::getCompleteObjectLocator in Clang for the layout.
261 // We're interested in the offset which is the second int in the struct.
262 Address offset_from_top_addr = rtti_locator_base_addr;
263 offset_from_top_addr.Slide(int_size);
264
265 error.Clear();
266 const int64_t offset_from_top = target.ReadSignedIntegerFromMemory(
267 offset_from_top_addr, int_size, INT64_MIN, error);
268 if (offset_from_top == INT64_MIN || error.Fail()) {
269 LLDB_LOG_ERROR(log, error.takeError(),
270 "Failed to read 'offset from top' from RTTI complete object "
271 "locator at {1:x16}: {0}",
272 offset_from_top_addr.GetOffset());
273 return std::nullopt;
274 }
275
276 return offset_from_top;
277}
278
279/// Try to find the offset of `target_base` inside `haystack` while visiting all
280/// `checkpoints`.
281static std::optional<size_t>
282GetOffsetOfBaseFromAST(clang::ASTContext &ast,
283 const clang::CXXRecordDecl *haystack,
284 const clang::CXXRecordDecl *target_base,
285 const llvm::ArrayRef<llvm::StringRef> checkpoints) {
286 struct BaseEntry {
287 const clang::CXXRecordDecl *decl;
288 /// If this is `std::nullopt`, then this entry is on a path where we can't
289 /// determine the offset.
290 std::optional<size_t> offset;
291 /// Remaining checkpoints on this path.
292 llvm::ArrayRef<llvm::StringRef> checkpoints;
293 /// Was `target_base` found on this path?
294 bool found_target = false;
295 };
296
297 // This does a breadth-first search over all bases. Starting point is the root
298 // (`haystack`).
299 std::deque<BaseEntry> bases;
300 bases.emplace_back(BaseEntry{
301 /*decl=*/haystack,
302 /*offset=*/0,
303 /*checkpoints=*/checkpoints,
304 });
305
306 // Go through virtual bases first.
307 std::string base_name;
308 for (const clang::CXXBaseSpecifier &base : haystack->vbases()) {
309 const clang::CXXRecordDecl *base_decl = llvm::cast<clang::CXXRecordDecl>(
310 base.getType()->castAs<clang::RecordType>()->getDecl());
311 // Although clang gives us some offset here, this offset is not from the
312 // debug info and thus likely wrong. Neither PDB nor DWARF include it.
313 if (!checkpoints.empty()) {
314 base_name = base_decl->getDeclName().getAsString();
315 if (base_name == checkpoints.back())
316 return std::nullopt;
317 } else if (base_decl == target_base) {
318 return std::nullopt;
319 }
320
321 // We still need to keep track of this class as `target_base` might be
322 // inside this base.
323 bases.emplace_back(BaseEntry{
324 /*decl=*/base_decl,
325 /*offset=*/std::nullopt,
326 /*checkpoints=*/checkpoints,
327 });
328 }
329
330 while (!bases.empty()) {
331 BaseEntry current = bases.front();
332 assert(
333 (!current.found_target || !current.checkpoints.empty()) &&
334 "if we found the target we should only need to check the checkpoints");
335 bases.pop_front();
336 const clang::ASTRecordLayout &record_layout =
337 ast.getASTRecordLayout(current.decl);
338
339 for (const clang::CXXBaseSpecifier &base : current.decl->bases()) {
340 if (base.isVirtual())
341 continue; // We already looked through virtual bases.
342
343 const clang::CXXRecordDecl *base_decl = llvm::cast<clang::CXXRecordDecl>(
344 base.getType()->castAs<clang::RecordType>()->getDecl());
345 bool found_target = base_decl == target_base || current.found_target;
346 std::optional<size_t> offset = current.offset;
347 if (offset)
348 *offset += record_layout.getBaseClassOffset(base_decl).getQuantity();
349
350 if (!current.checkpoints.empty()) {
351 base_name = base_decl->getDeclName().getAsString();
352 if (base_name == current.checkpoints.back()) {
353 // Last component was the base we were looking for.
354 if (found_target && current.checkpoints.size() == 1)
355 return offset;
356
357 // Found a checkpoint, so we have to take this path.
358 bases.clear();
359 bases.emplace_back(BaseEntry{
360 /*decl=*/base_decl,
361 /*offset=*/offset,
362 /*checkpoints=*/current.checkpoints.drop_back(),
363 /*found_target=*/found_target,
364 });
365 continue;
366 }
367 } else if (found_target) {
368 return offset;
369 }
370
371 bases.emplace_back(BaseEntry{
372 /*decl=*/base_decl,
373 /*offset=*/offset,
374 /*checkpoints=*/current.checkpoints,
375 /*found_target=*/found_target,
376 });
377 }
378 }
379
380 return std::nullopt;
381}
382
384 const CompilerType &in_type, ValueObject &in_value,
385 const CompilerType &dyn_type,
386 const LanguageRuntime::VTableInfo &vtable_info,
387 llvm::ArrayRef<llvm::StringRef> vtable_components) {
388 Log *log = GetLog(LLDBLog::Object);
389
390 CompilerType in_base_type = in_type;
391
392 clang::CXXRecordDecl *in_decl =
394 clang::CXXRecordDecl *target_decl =
396 if (!in_decl || !target_decl) {
397 LLDB_LOG(log, "{0:x16}: types are not record types (in={1}, target={2})",
398 in_value.GetPointerValue().address, in_base_type.GetTypeName(),
399 dyn_type.GetTypeName());
400 return std::nullopt;
401 }
402
404 if (!ts || ts != in_base_type.GetTypeSystem<TypeSystemClang>()) {
405 LLDB_LOG(log, "{0:x16}: '{1}' and '{2}' are from different type systems",
406 in_value.GetPointerValue().address,
407 in_decl->getDeclName().getAsString(),
408 target_decl->getDeclName().getAsString());
409 return std::nullopt;
410 }
411
412 std::optional<size_t> offset = GetOffsetOfBaseFromAST(
413 ts->getASTContext(), target_decl, in_decl, vtable_components);
414 if (!offset) {
415 LLDB_LOG(log, "{0:x16}: failed to find offset of '{1}' in '{2}'",
416 in_value.GetPointerValue().address,
417 in_decl->getDeclName().getAsString(),
418 target_decl->getDeclName().getAsString());
419 return std::nullopt;
420 }
421
422 return offset;
423}
static llvm::raw_ostream & error(Stream &strm)
#define LLDB_LOG(log,...)
The LLDB_LOG* macros defined below are the way to emit log messages.
Definition Log.h:375
#define LLDB_LOG_ERROR(log, error,...)
Definition Log.h:405
static std::optional< size_t > GetOffsetOfBaseFromAST(clang::ASTContext &ast, const clang::CXXRecordDecl *haystack, const clang::CXXRecordDecl *target_base, const llvm::ArrayRef< llvm::StringRef > checkpoints)
Try to find the offset of target_base inside haystack while visiting all checkpoints.
static bool SplitVTableComponents(llvm::StringRef demangled, llvm::SmallVectorImpl< llvm::StringRef > &components)
static const llvm::StringRef s_rtti_complete_object_locator_indicator
static const llvm::StringRef s_vtable_demangled_indicator
static llvm::StringRef StripDemangledVTableSuffix(llvm::StringRef demangled)
A section + offset based address class.
Definition Address.h:62
lldb::addr_t GetLoadAddress(Target *target) const
Get the load address.
Definition Address.cpp:303
void SetRawAddress(lldb::addr_t addr)
Definition Address.h:441
bool Slide(int64_t offset)
Definition Address.h:446
lldb::addr_t GetOffset() const
Get the section relative offset value.
Definition Address.h:329
bool IsSectionOffset() const
Check if an address is section offset.
Definition Address.h:342
Symbol * CalculateSymbolContextSymbol() const
Definition Address.cpp:888
void SetDynamicTypeInfo(const lldb_private::Address &vtable_addr, const TypeAndOrName &type_info)
lldb::TypeSP LookupTypeByName(llvm::StringRef type_name, lldb::ModuleSP preferred_module, bool &any_found) const
Find a type by its name, preferably in preferred_module.
TypeAndOrName GetDynamicTypeInfo(const lldb_private::Address &vtable_addr)
Generic representation of a type in a programming language.
TypeSystemSPWrapper GetTypeSystem() const
Accessors.
lldb::opaque_compiler_type_t GetOpaqueQualType() const
ConstString GetTypeName(bool BaseOnly=false) const
CompilerType GetPointeeType() const
If this type is a pointer type, return the type that the pointer points to, else return an invalid ty...
bool GetCompleteType() const
Type Completion.
bool IsPointerOrReferenceType(CompilerType *pointee_type=nullptr) const
llvm::StringRef GetStringRef() const
Get the string value as a llvm::StringRef.
const char * GetCString() const
Get the string value as a C string.
A class that handles mangled names.
Definition Mangled.h:34
ConstString GetDemangledName() const
Demangled name get accessor.
Definition Mangled.cpp:171
bool GetDynamicTypeAndAddress(ValueObject &in_value, lldb::DynamicValueType use_dynamic, const LanguageRuntime::VTableInfo &vtable_info, TypeAndOrName &class_type_or_name, Address &dynamic_address) override
std::optional< int64_t > GetOffsetToTopFromAST(const CompilerType &in_type, ValueObject &in_value, const CompilerType &dyn_type, const LanguageRuntime::VTableInfo &vtable_info, llvm::ArrayRef< llvm::StringRef > vtable_components)
std::optional< int64_t > GetOffsetToTopFromRTTI(const LanguageRuntime::VTableInfo &vtable_info, const CompilerType &dynamic_type)
std::optional< int64_t > GetOffsetToTop(ValueObject &in_value, TypeAndOrName &class_type_or_name, const LanguageRuntime::VTableInfo &vtable_info)
bool IsVTableSymbol(Mangled &mangled) const override
TypeAndOrName GetTypeInfo(ValueObject &in_value, const LanguageRuntime::VTableInfo &vtable_info, llvm::SmallVectorImpl< llvm::StringRef > &vtable_components)
A plug-in interface definition class for debugging a process.
Definition Process.h:369
An error handling class.
Definition Status.h:118
lldb::ModuleSP CalculateSymbolContextModule() override
Definition Symbol.cpp:446
Mangled & GetMangled()
Definition Symbol.h:162
ConstString GetName() const
Definition Symbol.cpp:614
int64_t ReadSignedIntegerFromMemory(const Address &addr, size_t integer_byte_size, int64_t fail_value, Status &error, bool force_live_memory=false)
Definition Target.cpp:2436
Sometimes you can find the name of the type corresponding to an object, but we don't have debug infor...
void SetName(ConstString type_name)
void SetTypeSP(lldb::TypeSP type_sp)
A TypeSystem implementation based on Clang.
static bool AreTypesSame(CompilerType type1, CompilerType type2, bool ignore_qualifiers=false)
static clang::CXXRecordDecl * GetAsCXXRecordDecl(lldb::opaque_compiler_type_t type)
virtual ConstString GetTypeName()
CompilerType GetCompilerType()
#define LLDB_INVALID_ADDRESS
A class that represents a running process on the host machine.
Log * GetLog(Cat mask)
Retrieve the Log object for the channel associated with the given log enum.
Definition Log.h:338
std::shared_ptr< lldb_private::Type > TypeSP
std::shared_ptr< lldb_private::TypeSystemClang > TypeSystemClangSP
uint64_t addr_t
Definition lldb-types.h:80
Symbol * symbol
Address of the vtable's virtual function table.