https://github.com/mentlerd updated 
https://github.com/llvm/llvm-project/pull/210584

>From f956ed6861540cfbd86ab0fe1aaf2a2654fec7b1 Mon Sep 17 00:00:00 2001
From: mentlerd <[email protected]>
Date: Sat, 18 Jul 2026 19:31:57 +0200
Subject: [PATCH 1/4] Adversary test against dynamic type resolution's current
 implementation

The current implementation is entirely typename based, and as such fails
for more complicated cases where the demangler and debug info don't agree
on the spelling of a type associated with the vtable's symbol.
---
 .../dynamic-value-complex-typename/Makefile   |  3 ++
 .../TestDynamicValueComplexTypename.py        | 30 ++++++++++++++++
 .../dynamic-value-complex-typename/main.cpp   | 34 +++++++++++++++++++
 3 files changed, 67 insertions(+)
 create mode 100644 
lldb/test/API/lang/cpp/dynamic-value-complex-typename/Makefile
 create mode 100644 
lldb/test/API/lang/cpp/dynamic-value-complex-typename/TestDynamicValueComplexTypename.py
 create mode 100644 
lldb/test/API/lang/cpp/dynamic-value-complex-typename/main.cpp

diff --git a/lldb/test/API/lang/cpp/dynamic-value-complex-typename/Makefile 
b/lldb/test/API/lang/cpp/dynamic-value-complex-typename/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/lang/cpp/dynamic-value-complex-typename/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
diff --git 
a/lldb/test/API/lang/cpp/dynamic-value-complex-typename/TestDynamicValueComplexTypename.py
 
b/lldb/test/API/lang/cpp/dynamic-value-complex-typename/TestDynamicValueComplexTypename.py
new file mode 100644
index 0000000000000..4d3bd81248479
--- /dev/null
+++ 
b/lldb/test/API/lang/cpp/dynamic-value-complex-typename/TestDynamicValueComplexTypename.py
@@ -0,0 +1,30 @@
+import lldb
+import lldbsuite.test.lldbutil as lldbutil
+from lldbsuite.test.lldbtest import *
+
+
+class DynamicValueComplexTypename(TestBase):
+
+    def test(self):
+        self.build()
+        lldbutil.run_to_source_breakpoint(
+            self, "break here", lldb.SBFileSpec("main.cpp")
+        )
+
+        self.checkVarType("simple", "Simple")
+        self.checkVarType("templated", "Templated<")
+        self.checkVarType("complicated", "Complicated<")
+        self.checkVarType("evil", "Evil<")
+
+    def checkVarType(self, name, type_prefix):
+        var = self.frame().FindVariable(name)
+        self.assertTrue(var.IsValid())
+
+        # Check if we recognized the dynamic type. Most names are unreasonably
+        # complex to match against exactly, but all should have a sane prefix
+        self.assertStartsWith(var.GetType().GetName(), type_prefix)
+
+        # Check if we resolved the compiler type too
+        marker = var.Dereference().GetChildAtIndex(0)
+        self.assertEqual(marker.GetName(), f"is_{name}")
+        self.assertEqual(marker.GetValueAsUnsigned(), 1)
diff --git a/lldb/test/API/lang/cpp/dynamic-value-complex-typename/main.cpp 
b/lldb/test/API/lang/cpp/dynamic-value-complex-typename/main.cpp
new file mode 100644
index 0000000000000..840242391f84f
--- /dev/null
+++ b/lldb/test/API/lang/cpp/dynamic-value-complex-typename/main.cpp
@@ -0,0 +1,34 @@
+
+struct Interface {
+  virtual int Foo() { return 0; }
+};
+
+struct Simple : public Interface {
+  bool is_simple = true;
+};
+
+template <typename A, typename B> struct Templated : public Interface {
+  bool is_templated = true;
+};
+
+template <typename A, int B> struct Complicated : public Interface {
+  bool is_complicated = true;
+};
+
+template <typename A> struct Evil : public Interface {
+  bool is_evil = true;
+};
+
+namespace {
+template <typename A, typename B> struct Pair {};
+} // namespace
+
+int main() {
+  auto lambda = [] {};
+
+  Interface *simple = new Simple();
+  Interface *templated = new Templated<int, double>();
+  Interface *complicated = new Complicated<Pair<Interface, int>, 42>();
+  Interface *evil = new Evil<decltype(lambda)>();
+  return 0; // break here
+}

>From 0dd810c61dc7c55045f81eb2aab885c716a424e4 Mon Sep 17 00:00:00 2001
From: mentlerd <[email protected]>
Date: Sat, 18 Jul 2026 23:04:37 +0200
Subject: [PATCH 2/4] Use SymbolFile, and __clang_vtable variable to locate the
 dynamic type

This entirely avoids all pitfalls related to symbol name mangling, as we use the
parent/child relation between the vtable and associated class emitted by clang.
---
 lldb/include/lldb/Symbol/SymbolFile.h         |  6 ++
 lldb/include/lldb/Symbol/Variable.h           |  2 +
 .../CPlusPlus/ItaniumABIRuntime.cpp           | 58 +++++++++++++++++++
 .../SymbolFile/DWARF/SymbolFileDWARF.cpp      | 14 +++++
 .../SymbolFile/DWARF/SymbolFileDWARF.h        |  2 +
 lldb/source/Symbol/Variable.cpp               |  7 +++
 .../TestDynamicValueComplexTypename.py        |  6 +-
 7 files changed, 94 insertions(+), 1 deletion(-)

diff --git a/lldb/include/lldb/Symbol/SymbolFile.h 
b/lldb/include/lldb/Symbol/SymbolFile.h
index ae6504c016d7b..b49c5ae230062 100644
--- a/lldb/include/lldb/Symbol/SymbolFile.h
+++ b/lldb/include/lldb/Symbol/SymbolFile.h
@@ -537,6 +537,12 @@ class SymbolFile : public PluginInterface {
 
   std::string GetObjectName() const;
 
+  /// Get the semantically innermost non-function type that encloses the
+  /// provided variable
+  virtual lldb::TypeSP GetTypeEnclosingVariableUID(lldb::user_id_t uid) {
+    return {};
+  }
+
 protected:
   void AssertModuleLock();
 
diff --git a/lldb/include/lldb/Symbol/Variable.h 
b/lldb/include/lldb/Symbol/Variable.h
index 1e6a36d584212..97b6750797202 100644
--- a/lldb/include/lldb/Symbol/Variable.h
+++ b/lldb/include/lldb/Symbol/Variable.h
@@ -62,6 +62,8 @@ class Variable : public UserID, public 
std::enable_shared_from_this<Variable> {
 
   Type *GetType();
 
+  lldb::TypeSP GetEnclosingType();
+
   lldb::LanguageType GetLanguage() const;
 
   lldb::ValueType GetScope() const { return m_scope; }
diff --git 
a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABIRuntime.cpp 
b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABIRuntime.cpp
index 4d9cf31c8904d..6fb920237d900 100644
--- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABIRuntime.cpp
+++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABIRuntime.cpp
@@ -14,6 +14,9 @@
 #include "lldb/Expression/FunctionCaller.h"
 #include "lldb/Utility/LLDBLog.h"
 
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/ValueObject/ValueObjectVariable.h"
+
 using namespace lldb;
 using namespace lldb_private;
 
@@ -39,6 +42,61 @@ ItaniumABIRuntime::GetTypeInfo(ValueObject &in_value,
                 ": static-type = '%s' has vtable symbol '%s'\n",
                 in_value.GetPointerValue().address,
                 in_value.GetTypeName().GetCString(), 
symbol_name.str().c_str());
+
+      // clang emits DW_TAG_variable '__clang_vtable' inside the enclosing
+      // class, try looking for that before falling back to name based type
+      // lookup
+      if (ModuleSP module_sp =
+              vtable_info.symbol->CalculateSymbolContextModule()) {
+        auto vptr = vtable_info.addr.GetLoadAddress(&m_process->GetTarget());
+
+        // NOTE: vptr points at the 'address point', not the vtable itself
+        auto vtable = vptr - m_process->GetAddressByteSize() * 2;
+
+        VariableList vars;
+
+        // SymbolFile doesn't provide an API to lookup a global variable with a
+        // specific location, so we have to resort to an unbounded name lookup
+        module_sp->FindGlobalVariables(ConstString("__clang_vtable"),
+                                       CompilerDeclContext(), -1, vars);
+
+        LLDB_LOGF(log,
+                  "0x%16.16" PRIx64 ": found %zu __clang_vtable variables\n",
+                  in_value.GetPointerValue().address, vars.GetSize());
+
+        for (lldb::VariableSP var : vars) {
+          auto valobj = ValueObjectVariable::Create(m_process, var);
+          if (valobj->GetLoadAddress() != vtable)
+            continue;
+
+          auto type_sp = var->GetEnclosingType();
+          if (!type_sp)
+            continue;
+
+          if (!TypeSystemClang::IsCXXClassType(
+                  type_sp->GetForwardCompilerType()))
+            continue;
+
+          LLDB_LOGF(log,
+                    "0x%16.16" PRIx64
+                    ": static-type = '%s' has dynamic type: uid={0x%" PRIx64
+                    "}, type-name='%s'\n",
+                    in_value.GetPointerValue().address,
+                    in_value.GetTypeName().AsCString(""), type_sp->GetID(),
+                    type_sp->GetName().GetCString());
+
+          type_info.SetTypeSP(type_sp);
+
+          SetDynamicTypeInfo(vtable_info.addr, type_info);
+          return type_info;
+        }
+
+        LLDB_LOGF(log,
+                  "0x%16.16" PRIx64 ": __clang_vtable based lookup failed, "
+                  "falling back to demangled typename\n",
+                  in_value.GetPointerValue().address);
+      }
+
       // We are a C++ class, that's good.  Get the class name and look it
       // up:
       llvm::StringRef class_name = symbol_name;
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp 
b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
index 8075c8f61193c..bbc1668cfed36 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -2336,6 +2336,11 @@ void SymbolFileDWARF::FindGlobalVariables(
   bool name_is_mangled = Mangled::GetManglingScheme(name.GetStringRef()) !=
                          Mangled::eManglingSchemeNone;
 
+  // Technically not a mangled name, but a support variable emitted by clang.
+  // Regardless, we need an exact lookup
+  if (name == "__clang_vtable")
+    name_is_mangled = true;
+
   if (!CPlusPlusLanguage::ExtractContextAndIdentifier(name.GetStringRef(),
                                                       context, basename))
     basename = name.GetStringRef();
@@ -4734,3 +4739,12 @@ DWOStats SymbolFileDWARF::GetDwoStats() {
 
   return stats;
 }
+
+lldb::TypeSP SymbolFileDWARF::GetTypeEnclosingVariableUID(lldb::user_id_t uid) 
{
+  DWARFDIE die = GetDIE(uid);
+
+  if (die.Tag() != DW_TAG_variable)
+    return nullptr;
+
+  return GetTypeForDIE(die.GetParentDeclContextDIE());
+}
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h 
b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
index 21a190d1b38f0..a87428fb86743 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
@@ -378,6 +378,8 @@ class SymbolFileDWARF : public SymbolFileCommon {
   /// Returns the DWARFIndex for this symbol, if it exists.
   DWARFIndex *getIndex() { return m_index.get(); }
 
+  lldb::TypeSP GetTypeEnclosingVariableUID(lldb::user_id_t uid) override;
+
 private:
   /// Find the definition DIE for the specified \c label in this
   /// SymbolFile.
diff --git a/lldb/source/Symbol/Variable.cpp b/lldb/source/Symbol/Variable.cpp
index 70a61fc7789c9..7bf7fcdd9c3b1 100644
--- a/lldb/source/Symbol/Variable.cpp
+++ b/lldb/source/Symbol/Variable.cpp
@@ -110,6 +110,13 @@ Type *Variable::GetType() {
   return nullptr;
 }
 
+lldb::TypeSP Variable::GetEnclosingType() {
+  Type *type = GetType();
+  if (type)
+    return type->GetSymbolFile()->GetTypeEnclosingVariableUID(GetID());
+  return nullptr;
+}
+
 void Variable::Dump(Stream *s, bool show_context) const {
   s->Printf("%p: ", static_cast<const void *>(this));
   s->Indent();
diff --git 
a/lldb/test/API/lang/cpp/dynamic-value-complex-typename/TestDynamicValueComplexTypename.py
 
b/lldb/test/API/lang/cpp/dynamic-value-complex-typename/TestDynamicValueComplexTypename.py
index 4d3bd81248479..f33d4ec948e0d 100644
--- 
a/lldb/test/API/lang/cpp/dynamic-value-complex-typename/TestDynamicValueComplexTypename.py
+++ 
b/lldb/test/API/lang/cpp/dynamic-value-complex-typename/TestDynamicValueComplexTypename.py
@@ -1,10 +1,14 @@
 import lldb
 import lldbsuite.test.lldbutil as lldbutil
 from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
 
 
 class DynamicValueComplexTypename(TestBase):
-
+    @skipIf(compiler=no_match("clang"))
+    @skipIf(compiler="clang", compiler_version=["<", "24.0"])  # __clang_vtable
+    @expectedFailureAll(debug_info=no_match(["dwarf", "dsym"]))  # Not 
implemented
+    @expectedFailureAll(oslist=["windows"])  # Not Itanium ABI
     def test(self):
         self.build()
         lldbutil.run_to_source_breakpoint(

>From c87dcc72d2e412bc9f2e98af87f9e9ca0aab9d11 Mon Sep 17 00:00:00 2001
From: mentlerd <[email protected]>
Date: Sun, 19 Jul 2026 11:05:01 +0200
Subject: [PATCH 3/4] Plumbing for multiplexing SymbolFile implementations

---
 lldb/include/lldb/Symbol/SymbolFileOnDemand.h        |  4 ++++
 .../SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp     | 12 ++++++++++++
 .../SymbolFile/DWARF/SymbolFileDWARFDebugMap.h       |  2 ++
 3 files changed, 18 insertions(+)

diff --git a/lldb/include/lldb/Symbol/SymbolFileOnDemand.h 
b/lldb/include/lldb/Symbol/SymbolFileOnDemand.h
index 46e33189d721b..9a2e0bc03f56f 100644
--- a/lldb/include/lldb/Symbol/SymbolFileOnDemand.h
+++ b/lldb/include/lldb/Symbol/SymbolFileOnDemand.h
@@ -253,6 +253,10 @@ class SymbolFileOnDemand : public lldb_private::SymbolFile 
{
     return m_sym_file_impl->CopyType(other_type);
   }
 
+  lldb::TypeSP GetTypeEnclosingVariableUID(lldb::user_id_t uid) override {
+    return m_sym_file_impl->GetTypeEnclosingVariableUID(uid);
+  }
+
 private:
   Log *GetLog() const { return ::lldb_private::GetLog(LLDBLog::OnDemand); }
 
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp 
b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
index 219ab7bbf84cc..24aeae71e365e 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
@@ -1642,6 +1642,18 @@ void SymbolFileDWARFDebugMap::GetCompileOptions(
   });
 }
 
+lldb::TypeSP
+SymbolFileDWARFDebugMap::GetTypeEnclosingVariableUID(lldb::user_id_t uid) {
+  lldb::TypeSP type;
+  ForEachSymbolFile("Looking up enclosing type for variable",
+                    [&](SymbolFileDWARF &oso_dwarf) {
+                      type = oso_dwarf.GetTypeEnclosingVariableUID(uid);
+                      return type ? IterationAction::Stop
+                                  : IterationAction::Continue;
+                    });
+  return type;
+}
+
 llvm::Expected<SymbolContext>
 SymbolFileDWARFDebugMap::ResolveFunctionCallLabel(FunctionCallLabel &label) {
   const uint64_t oso_idx = GetOSOIndexFromUserID(label.symbol_id);
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h 
b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
index 48b80974882b3..39141471cc40f 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
@@ -147,6 +147,8 @@ class SymbolFileDWARFDebugMap : public SymbolFileCommon {
   void
   GetCompileOptions(std::unordered_map<lldb::CompUnitSP, Args> &args) override;
 
+  lldb::TypeSP GetTypeEnclosingVariableUID(lldb::user_id_t uid) override;
+
   llvm::Expected<SymbolContext>
   ResolveFunctionCallLabel(FunctionCallLabel &label) override;
 

>From 42f12b328c956589eceb589013b61f3ac6e47326 Mon Sep 17 00:00:00 2001
From: mentlerd <[email protected]>
Date: Sun, 19 Jul 2026 11:59:57 +0200
Subject: [PATCH 4/4] assertStartsWith is too modern for Linux CI

---
 .../TestDynamicValueComplexTypename.py                      | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git 
a/lldb/test/API/lang/cpp/dynamic-value-complex-typename/TestDynamicValueComplexTypename.py
 
b/lldb/test/API/lang/cpp/dynamic-value-complex-typename/TestDynamicValueComplexTypename.py
index f33d4ec948e0d..7d1557098b1f8 100644
--- 
a/lldb/test/API/lang/cpp/dynamic-value-complex-typename/TestDynamicValueComplexTypename.py
+++ 
b/lldb/test/API/lang/cpp/dynamic-value-complex-typename/TestDynamicValueComplexTypename.py
@@ -26,7 +26,11 @@ def checkVarType(self, name, type_prefix):
 
         # Check if we recognized the dynamic type. Most names are unreasonably
         # complex to match against exactly, but all should have a sane prefix
-        self.assertStartsWith(var.GetType().GetName(), type_prefix)
+        type_name = var.GetType().GetName()
+        self.assertTrue(
+            type_name.startswith(type_prefix),
+            f"Expected '{type_prefix}' prefix, got '{type_name}'",
+        )
 
         # Check if we resolved the compiler type too
         marker = var.Dereference().GetChildAtIndex(0)

_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to