[Lldb-commits] [lldb] [lldb][NativePDB] Handle LF_CLASS2 and friends (PR #231615)
via lldb-commits
lldb-commits at lists.llvm.org
Tue Oct 13 11:56:55 PDT 2026
https://github.com/Nerixyz created https://github.com/llvm/llvm-project/pull/231615
In #228761 I added support for `LF_CLASS2`, `LF_STRUCTURE2`, `LF_INTERFACE2`, and `LF_UNION2`. They map to the same high-level types (`ClassRecord` etc.), so the migration for lldb isn't that big. Most changes are adding a case for the `2` record.
>From d8795cdbf6b91c46697642fb6392a1abfc75d539 Mon Sep 17 00:00:00 2001
From: Nerixyz <nerixdev at outlook.de>
Date: Tue, 13 Oct 2026 20:50:29 +0200
Subject: [PATCH] [lldb][NativePDB] Handle LF_CLASS2 and friends
---
.../NativePDB/PdbAstBuilderClang.cpp | 4 ++
.../Plugins/SymbolFile/NativePDB/PdbUtil.cpp | 40 +++++++++++--
.../NativePDB/SymbolFileNativePDB.cpp | 9 ++-
.../NativePDB/UdtRecordCompleter.cpp | 5 +-
.../Shell/SymbolFile/NativePDB/lf-class2.cpp | 59 +++++++++++++++++++
5 files changed, 108 insertions(+), 9 deletions(-)
create mode 100644 lldb/test/Shell/SymbolFile/NativePDB/lf-class2.cpp
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderClang.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderClang.cpp
index 66a3990a54f635..c4604759fc7ea2 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderClang.cpp
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderClang.cpp
@@ -104,12 +104,16 @@ struct CreateMethodDecl : public TypeVisitorCallbacks {
static clang::TagTypeKind TranslateUdtKind(const TagRecord &cr) {
switch (cr.Kind) {
case TypeRecordKind::Class:
+ case TypeRecordKind::Class2:
return clang::TagTypeKind::Class;
case TypeRecordKind::Struct:
+ case TypeRecordKind::Struct2:
return clang::TagTypeKind::Struct;
case TypeRecordKind::Union:
+ case TypeRecordKind::Union2:
return clang::TagTypeKind::Union;
case TypeRecordKind::Interface:
+ case TypeRecordKind::Interface2:
return clang::TagTypeKind::Interface;
case TypeRecordKind::Enum:
return clang::TagTypeKind::Enum;
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp
index 8f739be137eacd..c159b38f0054c6 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp
@@ -198,13 +198,17 @@ CVTagRecord CVTagRecord::create(CVType type) {
assert(IsTagRecord(type) && "type is not a tag record!");
switch (type.kind()) {
case LF_CLASS:
+ case LF_CLASS2:
case LF_STRUCTURE:
- case LF_INTERFACE: {
+ case LF_STRUCTURE2:
+ case LF_INTERFACE:
+ case LF_INTERFACE2: {
ClassRecord cr;
llvm::cantFail(TypeDeserializer::deserializeAs<ClassRecord>(type, cr));
return CVTagRecord(std::move(cr));
}
- case LF_UNION: {
+ case LF_UNION:
+ case LF_UNION2: {
UnionRecord ur;
llvm::cantFail(TypeDeserializer::deserializeAs<UnionRecord>(type, ur));
return CVTagRecord(std::move(ur));
@@ -220,8 +224,10 @@ CVTagRecord CVTagRecord::create(CVType type) {
}
CVTagRecord::CVTagRecord(ClassRecord &&c)
- : cvclass(std::move(c)),
- m_kind(cvclass.Kind == TypeRecordKind::Struct ? Struct : Class) {}
+ : cvclass(std::move(c)), m_kind((cvclass.Kind == TypeRecordKind::Struct ||
+ cvclass.Kind == TypeRecordKind::Struct2)
+ ? Struct
+ : Class) {}
CVTagRecord::CVTagRecord(UnionRecord &&u)
: cvunion(std::move(u)), m_kind(Union) {}
CVTagRecord::CVTagRecord(EnumRecord &&e) : cvenum(std::move(e)), m_kind(Enum) {}
@@ -290,9 +296,13 @@ PDB_SymType lldb_private::npdb::CVTypeToPDBType(TypeLeafKind kind) {
case LF_BINTERFACE:
return PDB_SymType::BaseInterface;
case LF_CLASS:
+ case LF_CLASS2:
case LF_STRUCTURE:
+ case LF_STRUCTURE2:
case LF_INTERFACE:
+ case LF_INTERFACE2:
case LF_UNION:
+ case LF_UNION2:
return PDB_SymType::UDT;
case LF_POINTER:
return PDB_SymType::PointerType;
@@ -499,11 +509,15 @@ bool lldb_private::npdb::IsForwardRefUdt(CVType cvt) {
EnumRecord er;
switch (cvt.kind()) {
case LF_CLASS:
+ case LF_CLASS2:
case LF_STRUCTURE:
+ case LF_STRUCTURE2:
case LF_INTERFACE:
+ case LF_INTERFACE2:
llvm::cantFail(TypeDeserializer::deserializeAs<ClassRecord>(cvt, cr));
return cr.isForwardRef();
case LF_UNION:
+ case LF_UNION2:
llvm::cantFail(TypeDeserializer::deserializeAs<UnionRecord>(cvt, ur));
return ur.isForwardRef();
case LF_ENUM:
@@ -517,8 +531,11 @@ bool lldb_private::npdb::IsForwardRefUdt(CVType cvt) {
bool lldb_private::npdb::IsTagRecord(llvm::codeview::CVType cvt) {
switch (cvt.kind()) {
case LF_CLASS:
+ case LF_CLASS2:
case LF_STRUCTURE:
+ case LF_STRUCTURE2:
case LF_UNION:
+ case LF_UNION2:
case LF_ENUM:
return true;
default:
@@ -529,8 +546,11 @@ bool lldb_private::npdb::IsTagRecord(llvm::codeview::CVType cvt) {
bool lldb_private::npdb::IsClassStructUnion(llvm::codeview::CVType cvt) {
switch (cvt.kind()) {
case LF_CLASS:
+ case LF_CLASS2:
case LF_STRUCTURE:
+ case LF_STRUCTURE2:
case LF_UNION:
+ case LF_UNION2:
return true;
default:
return false;
@@ -568,13 +588,17 @@ lldb_private::npdb::TranslateMemberAccess(MemberAccess access) {
TypeIndex lldb_private::npdb::GetFieldListIndex(CVType cvt) {
switch (cvt.kind()) {
case LF_CLASS:
+ case LF_CLASS2:
case LF_STRUCTURE:
- case LF_INTERFACE: {
+ case LF_STRUCTURE2:
+ case LF_INTERFACE:
+ case LF_INTERFACE2: {
ClassRecord cr;
cantFail(TypeDeserializer::deserializeAs<ClassRecord>(cvt, cr));
return cr.FieldList;
}
- case LF_UNION: {
+ case LF_UNION:
+ case LF_UNION2: {
UnionRecord ur;
cantFail(TypeDeserializer::deserializeAs<UnionRecord>(cvt, ur));
return ur.FieldList;
@@ -1184,10 +1208,14 @@ size_t lldb_private::npdb::GetSizeOfType(PdbTypeSymId id,
case LF_ARRAY:
return GetSizeOfTypeInternal<ArrayRecord>(cvt);
case LF_CLASS:
+ case LF_CLASS2:
case LF_STRUCTURE:
+ case LF_STRUCTURE2:
case LF_INTERFACE:
+ case LF_INTERFACE2:
return GetSizeOfTypeInternal<ClassRecord>(cvt);
case LF_UNION:
+ case LF_UNION2:
return GetSizeOfTypeInternal<UnionRecord>(cvt);
case LF_BITFIELD: {
BitFieldRecord record;
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
index 9ed1d327851fbe..a7ce8b8cad8043 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
@@ -298,8 +298,11 @@ static llvm::StringRef GetSimpleTypeName(SimpleTypeKind kind) {
static bool IsClassRecord(TypeLeafKind kind) {
switch (kind) {
case LF_STRUCTURE:
+ case LF_STRUCTURE2:
case LF_CLASS:
+ case LF_CLASS2:
case LF_INTERFACE:
+ case LF_INTERFACE2:
return true;
default:
return false;
@@ -928,7 +931,7 @@ TypeSP SymbolFileNativePDB::CreateType(PdbTypeSymId type_id, CompilerType ct) {
return CreateTagType(type_id, er, ct);
}
- if (cvt.kind() == LF_UNION) {
+ if (cvt.kind() == LF_UNION || cvt.kind() == LF_UNION2) {
UnionRecord ur;
if (auto err = TypeDeserializer::deserializeAs<UnionRecord>(cvt, ur)) {
LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err),
@@ -3102,11 +3105,15 @@ SymbolFileNativePDB::GetContextForType(TypeIndex ti) {
type = m_index->tpi().getType(ti);
switch (type.kind()) {
case LF_CLASS:
+ case LF_CLASS2:
case LF_STRUCTURE:
+ case LF_STRUCTURE2:
case LF_INTERFACE:
+ case LF_INTERFACE2:
el.kind = CompilerContextKind::ClassOrStruct;
continue;
case LF_UNION:
+ case LF_UNION2:
el.kind = CompilerContextKind::Union;
continue;
case LF_ENUM:
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp
index 417d7630a93753..b019d95d79b5b8 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp
@@ -70,11 +70,12 @@ clang::QualType UdtRecordCompleter::AddBaseClassForTypeIndex(
clang::QualType qt = m_ast_builder.GetOrCreateClangType(type_id);
CVType udt_cvt = m_index.tpi().getType(ti);
-
+ bool base_of_class =
+ udt_cvt.kind() == LF_CLASS || udt_cvt.kind() == LF_CLASS2;
std::unique_ptr<clang::CXXBaseSpecifier> base_spec =
m_ast_builder.clang().CreateBaseClassSpecifier(
qt.getAsOpaquePtr(), TranslateMemberAccess(access),
- vtable_idx.has_value(), udt_cvt.kind() == LF_CLASS);
+ vtable_idx.has_value(), base_of_class);
if (!base_spec)
return {};
diff --git a/lldb/test/Shell/SymbolFile/NativePDB/lf-class2.cpp b/lldb/test/Shell/SymbolFile/NativePDB/lf-class2.cpp
new file mode 100644
index 00000000000000..f5508f87068637
--- /dev/null
+++ b/lldb/test/Shell/SymbolFile/NativePDB/lf-class2.cpp
@@ -0,0 +1,59 @@
+// Test that lldb can handle the LF_CLASS2/STRUCTURE2/UNION2 records.
+
+// clang-format off
+// REQUIRES: lld, x86
+
+// Test various interesting cases for AST reconstruction.
+// RUN: %clang_cl --target=x86_64-windows-msvc -mllvm=-use-codeview-tagrecord2 \
+// RUN: -GS- -GR- -Od -Z7 -c /Fo%t.obj -- %s
+// RUN: lld-link -debug:full -nodefaultlib -entry:main %t.obj -out:%t.exe -pdb:%t.pdb
+// RUN: llvm-pdbutil dump --types %t.pdb | FileCheck --check-prefix=PDB %s
+// RUN: lldb-test symbols --dump-ast %t.exe | FileCheck --check-prefix=STRUCT %s
+// RUN: lldb-test symbols --dump-ast %t.exe | FileCheck --check-prefix=CLASS %s
+
+struct AStruct {
+ char c1;
+ virtual ~AStruct() = default;
+ virtual int something() { return 1; }
+};
+
+class AClass : public AStruct {
+public:
+ int i;
+ union Inner {
+ short s;
+ unsigned short u;
+ };
+ Inner inner;
+};
+
+void *operator new(unsigned long long n) { return nullptr; }
+void operator delete(void *p, unsigned long long i) {}
+
+int main() {
+ AClass a;
+ return a.inner.s;
+}
+
+// Make sure that we actually check the *2 records.
+// PDB-DAG: LF_CLASS2
+// PDB-DAG: LF_STRUCTURE2
+// PDB-DAG: LF_UNION2
+
+// CLASS: class AClass : public AStruct {
+// CLASS-NEXT: AClass();
+// CLASS-NEXT: ~AClass();
+// CLASS-NEXT: union Inner {
+// CLASS-NEXT: short s;
+// CLASS-NEXT: unsigned short u;
+// CLASS-NEXT: };
+// CLASS-NEXT: int i;
+// CLASS-NEXT: AClass::Inner inner;
+// CLASS-NEXT: };
+
+// STRUCT: struct AStruct {
+// STRUCT-NEXT: AStruct();
+// STRUCT-NEXT: virtual int something();
+// STRUCT-NEXT: virtual ~AStruct();
+// STRUCT-NEXT: char c1;
+// STRUCT-NEXT: };
More information about the lldb-commits
mailing list