[Lldb-commits] [lldb] [lldb] Read virtual bases relative to vbptr on MS ABI (PR #214960)
via lldb-commits
lldb-commits at lists.llvm.org
Sat Aug 8 05:07:18 PDT 2026
https://github.com/Nerixyz created https://github.com/llvm/llvm-project/pull/214960
In the Microsoft ABI, the offset to a virtual base we read from the v(b)table is relative to the vbptr (pointer to vbtable). Unlike on Itanium, where the offset is relative to the start of the record. This makes a difference when the virtual base isn't the first one. Because then the `vbptr` is offset.
For example in the test I added, the record layout for `UserUser` looks like this (https://godbolt.org/z/TYnMW5hcs):
```
class UserUser size(48):
+---
0 | +--- (base class Padding4)
0 | | member
| +---
8 | +--- (base class User)
8 | | +--- (base class Padding3)
8 | | | member
| | +---
16 | | {vbptr}
24 | | member
| | <alignment member> (size=6)
| | <alignment member> (size=4)
| +---
32 | +--- (base class Padding5)
32 | | member
| +---
34 | member
| <alignment member> (size=4)
| <alignment member> (size=4)
+---
+--- (virtual base VBase1)
40 | member
+---
+--- (virtual base VBase2)
42 | member
+---
UserUser::$vbtable@:
0 | -8
1 | 24 (UserUserd(User+8)VBase1)
2 | 26 (UserUserd(User+8)VBase2)
```
When trying to read the virtual base in `User`, we'd read the `vbtable` at offset 1 and get 24 as the offset (-> 16 + 24 = 40).
In our tests, the virtual base was always the first one, so this never showed up.
>From d92087117ad8322327f5cfcf3e10ecfa61266f28 Mon Sep 17 00:00:00 2001
From: Nerixyz <nerixdev at outlook.de>
Date: Sat, 8 Aug 2026 13:50:28 +0200
Subject: [PATCH] [lldb] Read virtual bases relative to vbptr on MS ABI
---
.../TypeSystem/Clang/TypeSystemClang.cpp | 4 ++
lldb/test/API/lang/cpp/virtual-bases/Makefile | 3 ++
.../cpp/virtual-bases/TestCppVirtualBases.py | 49 +++++++++++++++++++
lldb/test/API/lang/cpp/virtual-bases/main.cpp | 30 ++++++++++++
4 files changed, 86 insertions(+)
create mode 100644 lldb/test/API/lang/cpp/virtual-bases/Makefile
create mode 100644 lldb/test/API/lang/cpp/virtual-bases/TestCppVirtualBases.py
create mode 100644 lldb/test/API/lang/cpp/virtual-bases/main.cpp
diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
index 4a637f0817759..806042e009059 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
@@ -313,6 +313,10 @@ static bool GetVBaseBitOffset(VTableContextBase &vtable_ctx,
if (base_offset == INT64_MAX)
return false;
+ // In the Microsoft ABI, the offset we read in the vtable is relative to the
+ // vbptr in the type itself, not relative to the start of the record.
+ if (vtable_ctx.isMicrosoft())
+ base_offset += record_layout.getVBPtrOffset().getQuantity();
bit_offset = base_offset * 8;
return true;
diff --git a/lldb/test/API/lang/cpp/virtual-bases/Makefile b/lldb/test/API/lang/cpp/virtual-bases/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/lang/cpp/virtual-bases/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
diff --git a/lldb/test/API/lang/cpp/virtual-bases/TestCppVirtualBases.py b/lldb/test/API/lang/cpp/virtual-bases/TestCppVirtualBases.py
new file mode 100644
index 0000000000000..141dd0321bb77
--- /dev/null
+++ b/lldb/test/API/lang/cpp/virtual-bases/TestCppVirtualBases.py
@@ -0,0 +1,49 @@
+"""
+Test reading virtual bases through the vtable.
+"""
+
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+import lldbsuite.test.lldbutil as lldbutil
+
+
+class TestCppVirtualBases(TestBase):
+ @no_debug_info_test
+ def test(self):
+ self.build()
+ lldbutil.run_to_source_breakpoint(
+ self, "// break here", lldb.SBFileSpec("main.cpp")
+ )
+
+ children = [
+ ValueCheck(
+ type="Padding4",
+ children=[ValueCheck(type="short", name="member", value="4")],
+ ),
+ ValueCheck(
+ type="User",
+ children=[
+ ValueCheck(
+ type="Padding3",
+ children=[ValueCheck(type="short", name="member", value="3")],
+ ),
+ ValueCheck(
+ type="VBase1",
+ children=[ValueCheck(type="short", name="member", value="1")],
+ ),
+ ValueCheck(
+ type="VBase2",
+ children=[ValueCheck(type="short", name="member", value="2")],
+ ),
+ ValueCheck(type="short", name="member", value="6"),
+ ],
+ ),
+ ValueCheck(
+ type="Padding5",
+ children=[ValueCheck(type="short", name="member", value="5")],
+ ),
+ ValueCheck(type="short", name="member", value="7"),
+ ]
+ self.expect_expr("useruser", result_type="UserUser", result_children=children)
+ self.expect_var_path("useruser", type="UserUser", children=children)
diff --git a/lldb/test/API/lang/cpp/virtual-bases/main.cpp b/lldb/test/API/lang/cpp/virtual-bases/main.cpp
new file mode 100644
index 0000000000000..8addd388dfcb2
--- /dev/null
+++ b/lldb/test/API/lang/cpp/virtual-bases/main.cpp
@@ -0,0 +1,30 @@
+struct VBase1 {
+ short member = 1;
+};
+struct VBase2 {
+ short member = 2;
+};
+
+struct Padding3 {
+ short member = 3;
+};
+struct Padding4 {
+ short member = 4;
+};
+struct Padding5 {
+ short member = 5;
+};
+
+struct User : public Padding3, public virtual VBase1, public virtual VBase2 {
+ short member = 6;
+};
+
+struct UserUser : public Padding4, public User, public Padding5 {
+ short member = 7;
+};
+
+int main() {
+ UserUser useruser;
+
+ return 0; // break here
+}
More information about the lldb-commits
mailing list