[Lldb-commits] [lldb] [llvm] [lldb] Add initial MicrosoftABIRuntime (PR #235090)
via lldb-commits
lldb-commits at lists.llvm.org
Sat Oct 31 14:36:58 PDT 2026
================
@@ -0,0 +1,426 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MicrosoftABIRuntime.h"
+
+#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "clang/AST/RecordLayout.h"
+
+#include <deque>
+#include <mutex>
+
+using namespace lldb;
+using namespace lldb_private;
+
+static const llvm::StringRef s_vtable_demangled_indicator = "::`vftable'";
+static const llvm::StringRef s_rtti_complete_object_locator_indicator =
+ "::`RTTI Complete Object Locator'";
+
+static llvm::StringRef StripDemangledVTableSuffix(llvm::StringRef demangled) {
+ llvm::StringRef name = demangled.split(s_vtable_demangled_indicator).first;
+ name.consume_front("const ");
+ return name;
+}
+
+static bool
+SplitVTableComponents(llvm::StringRef demangled,
+ llvm::SmallVectorImpl<llvm::StringRef> &components) {
+ llvm::StringRef suffix = demangled.split(s_vtable_demangled_indicator).second;
+ if (suffix.empty())
+ return true; // e.g. MyClass::`vftable'
+
+ // Otherwise, suffix looks like "{for `MyBase2's `MyBase1'}" or
+ // "{for `MyBase'}".
+ if (!suffix.consume_front("{for `") || !suffix.consume_back("'}"))
+ return false;
+
+ suffix.split(components, "'s `");
+ return true;
+}
+
+MicrosoftABIRuntime::MicrosoftABIRuntime(Process *process)
+ : CommonABIRuntime(process) {}
+
+bool MicrosoftABIRuntime::GetDynamicTypeAndAddress(
+ ValueObject &in_value, lldb::DynamicValueType use_dynamic,
+ const LanguageRuntime::VTableInfo &vtable_info,
+ TypeAndOrName &class_type_or_name, Address &dynamic_address) {
+ std::optional<size_t> offset_to_top =
+ GetOffsetToTop(in_value, class_type_or_name, vtable_info);
+ if (!offset_to_top)
+ return false;
+
+ lldb::addr_t dynamic_addr =
+ in_value.GetPointerValue().address - *offset_to_top;
+ if (!m_process->GetTarget().ResolveLoadAddress(dynamic_addr, dynamic_address))
+ dynamic_address.SetRawAddress(dynamic_addr);
+
+ return true;
+}
+
+bool MicrosoftABIRuntime::IsVTableSymbol(Mangled &mangled) const {
+ return mangled.GetDemangledName().GetStringRef().contains(
+ s_vtable_demangled_indicator);
+}
+
+TypeAndOrName MicrosoftABIRuntime::GetTypeInfo(
+ ValueObject &in_value, const LanguageRuntime::VTableInfo &vtable_info,
+ llvm::SmallVectorImpl<llvm::StringRef> &vtable_components) {
+ if (!vtable_info.addr.IsSectionOffset() || !vtable_info.symbol)
+ return {};
+
+ // See if we have cached info for this type already
+ TypeAndOrName type_info = GetDynamicTypeInfo(vtable_info.addr);
+ if (type_info)
+ return type_info;
+
+ Log *log = GetLog(LLDBLog::Object);
+ llvm::StringRef symbol_name =
+ vtable_info.symbol->GetMangled().GetDemangledName().GetStringRef();
+ if (!SplitVTableComponents(symbol_name, vtable_components)) {
+ LLDB_LOG(log, "{0:x16}: '{1}' is not a valid vtable symbol",
+ in_value.GetPointerValue().address, symbol_name);
+ return {};
+ }
+
+ llvm::StringRef class_name = StripDemangledVTableSuffix(symbol_name);
+
+ type_info.SetName(class_name);
+ bool any_found = false;
+ TypeSP type_sp = LookupTypeByName(
+ class_name, vtable_info.symbol->CalculateSymbolContextModule(),
+ any_found);
+ if (!any_found)
+ return TypeAndOrName(); // Type is not dynamic.
+
+ if (type_sp) {
+ LLDB_LOG(log,
+ "static-type = '{0}' has dynamic type: uid={1:x}, type-name='{2}'",
+ in_value.GetTypeName(), type_sp->GetID(), type_sp->GetName());
+ type_info.SetTypeSP(std::move(type_sp));
+ }
+
+ if (type_info)
+ SetDynamicTypeInfo(vtable_info.addr, type_info);
+ return type_info;
+}
+
+std::optional<int64_t> MicrosoftABIRuntime::GetOffsetToTop(
+ ValueObject &in_value, TypeAndOrName &class_type_or_name,
+ const LanguageRuntime::VTableInfo &vtable_info) {
+ Log *log = GetLog(LLDBLog::Object);
+
+ llvm::SmallVector<llvm::StringRef, 2> vtable_components;
+ class_type_or_name = GetTypeInfo(in_value, vtable_info, vtable_components);
+ if (!class_type_or_name) {
+ LLDB_LOG(log, "{0:x16}: failed to get type info",
+ in_value.GetPointerValue().address);
+ return std::nullopt;
+ }
+
+ {
+ std::scoped_lock<std::mutex> g(m_mutex);
+ auto offset_to_top_it = m_offset_to_top_map.find(vtable_info.addr);
+ if (offset_to_top_it != m_offset_to_top_map.end())
+ return offset_to_top_it->second;
+ }
+
+ CompilerType type = class_type_or_name.GetCompilerType();
+ if (!type) {
+ LLDB_LOG(log, "{0:x16}: failed to get type info (no compiler type)",
+ in_value.GetPointerValue().address);
+ return std::nullopt;
+ }
+
+ CompilerType in_type = in_value.GetCompilerType();
+ if (in_type.IsPointerOrReferenceType())
+ in_type = in_type.GetPointeeType();
+ if (!in_type.GetCompleteType()) {
+ LLDB_LOG(log, "{0:x16}: {1} is not a complete type",
+ in_value.GetPointerValue().address, in_type.GetTypeName());
+ return std::nullopt;
+ }
+ if (!type.GetCompleteType()) {
+ LLDB_LOG(log, "{0:x16}: {1} is not a complete type",
+ in_value.GetPointerValue().address,
+ type.GetTypeName().GetCString());
+ return std::nullopt;
+ }
+
+ if (TypeSystemClang::AreTypesSame(in_type, type)) {
+ LLDB_LOG(log, "{0:x16}: in type is the same as the dynamic type",
+ in_value.GetPointerValue().address);
+
+ // The dynamic type we found was the same type, so we don't have a
+ // dynamic type here, but if a derived type encounters this vtable symbol,
+ // remember that it has offset 0.
+ std::scoped_lock<std::mutex> g(m_mutex);
+ m_offset_to_top_map[vtable_info.addr] = 0;
+ return std::nullopt;
+ }
+
+ // Unlike in the Itanium ABI, the VTable itself doesn't have a field telling
+ // us the offset to the dynamic type.
+ // When compiled with RTTI, we can use the complete object locator to get this
+ // offset. This is preferred, because it can resolve types across different
+ // modules and handle virtual bases with PDB.
+ // If this fails (e.g. no RTTI available), the offset is determined from the
+ // AST. This requires the types to be from the same type system. Because PDB
+ // doesn't include the offsets of virtual bases, we don't attempt to resolve
+ // these. They're technically present in the AST, but they often have
+ // incorrect offsets.
+ std::optional<int64_t> offset = GetOffsetToTopFromRTTI(vtable_info, type);
+ llvm::StringRef strategy = "RTTI";
+ if (!offset) {
+ offset = GetOffsetToTopFromAST(in_type, in_value, type, vtable_info,
+ vtable_components);
+ strategy = "AST";
+ }
+
+ if (offset) {
+ LLDB_LOG(GetLog(LLDBLog::Object),
+ "{0:x16}: [{1}] '{2}' has offset {3} in '{4}'",
+ in_value.GetPointerValue().address, strategy,
+ in_type.GetTypeName(), *offset, type.GetTypeName());
+
+ std::scoped_lock<std::mutex> g(m_mutex);
+ m_offset_to_top_map[vtable_info.addr] = offset;
+ }
+
+ return offset;
+}
+
+std::optional<int64_t> MicrosoftABIRuntime::GetOffsetToTopFromRTTI(
+ const LanguageRuntime::VTableInfo &vtable_info,
+ const CompilerType &dynamic_type) {
+ // The pointer to the RTTI complete object locator sits one pointer size above
+ // the vftable.
+ Target &target = m_process->GetTarget();
+ const addr_t vtable_load_addr = vtable_info.addr.GetLoadAddress(&target);
+ if (vtable_load_addr == LLDB_INVALID_ADDRESS)
+ return std::nullopt;
+ const uint32_t addr_byte_size = m_process->GetAddressByteSize();
+ const lldb::addr_t complete_object_locator_load_addr =
+ vtable_load_addr - addr_byte_size;
+ // Check for underflow.
+ if (complete_object_locator_load_addr >= vtable_load_addr)
+ return std::nullopt;
+
+ Log *log = GetLog(LLDBLog::Object);
+ // Pointer to the RTTI complete object locator.
+ llvm::Expected<lldb::addr_t> rtti_locator_base_load_addr =
+ m_process->ReadPointerFromMemory(complete_object_locator_load_addr);
+ if (!rtti_locator_base_load_addr ||
+ *rtti_locator_base_load_addr == LLDB_INVALID_ADDRESS) {
+ LLDB_LOG_ERROR(log, rtti_locator_base_load_addr.takeError(),
----------------
Nerixyz wrote:
This doesn't always succeed, `complete_object_locator_load_addr` _could_ be invalid or unloaded.
https://github.com/llvm/llvm-project/pull/235090
More information about the lldb-commits
mailing list