[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