[PDB] Parse UDT symbols and pointers to members (combined patch)
Summary:
In this patch I've tried to combine the best ideas from D49368 and D49410,
so it implements following:
- Completion of UDTs from a PDB with a filling of a layout info;
- Pointers to members;
- Fixes the bug relating to a virtual base offset reading from `vbtable`.
The offset was treated as an unsigned, but it can be a negative sometimes.
- Support of MSInheritance attribute
Reviewers: asmith, zturner, rnk, labath, clayborg, lldb-commits
Reviewed By: zturner
Subscribers: aleksandr.urakov, stella.stamenova, JDevlieghere, lldb-commits
Differential Revision: https://reviews.llvm.org/D49980
llvm-svn: 339649
diff --git a/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.cpp b/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.cpp
index 8bea994..713fa6e 100644
--- a/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.cpp
+++ b/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.cpp
@@ -9,11 +9,15 @@
#include "PDBASTParser.h"
+#include "SymbolFilePDB.h"
+
#include "clang/AST/CharUnits.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
+#include "lldb/Core/Module.h"
#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/ClangExternalASTSourceCommon.h"
#include "lldb/Symbol/ClangUtil.h"
#include "lldb/Symbol/Declaration.h"
#include "lldb/Symbol/SymbolFile.h"
@@ -48,8 +52,9 @@
return clang::TTK_Union;
case PDB_UdtType::Interface:
return clang::TTK_Interface;
+ default:
+ llvm_unreachable("unsuported PDB UDT type");
}
- return -1;
}
lldb::Encoding TranslateBuiltinEncoding(PDB_BuiltinType type) {
@@ -199,6 +204,68 @@
decl.SetLine(first_line_up->getLineNumber());
return true;
}
+
+AccessType TranslateMemberAccess(PDB_MemberAccess access) {
+ switch (access) {
+ case PDB_MemberAccess::Private:
+ return eAccessPrivate;
+ case PDB_MemberAccess::Protected:
+ return eAccessProtected;
+ case PDB_MemberAccess::Public:
+ return eAccessPublic;
+ default:
+ return eAccessNone;
+ }
+}
+
+AccessType GetDefaultAccessibilityForUdtKind(PDB_UdtType udt_kind) {
+ switch (udt_kind) {
+ case PDB_UdtType::Struct:
+ case PDB_UdtType::Union:
+ return eAccessPublic;
+ case PDB_UdtType::Class:
+ case PDB_UdtType::Interface:
+ return eAccessPrivate;
+ default:
+ llvm_unreachable("unsupported PDB UDT type");
+ }
+}
+
+AccessType GetAccessibilityForUdt(const PDBSymbolTypeUDT &udt) {
+ AccessType access = TranslateMemberAccess(udt.getAccess());
+ if (access != lldb::eAccessNone || !udt.isNested())
+ return access;
+
+ auto parent = udt.getClassParent();
+ if (!parent)
+ return lldb::eAccessNone;
+
+ auto parent_udt = llvm::dyn_cast<PDBSymbolTypeUDT>(parent.get());
+ if (!parent_udt)
+ return lldb::eAccessNone;
+
+ return GetDefaultAccessibilityForUdtKind(parent_udt->getUdtKind());
+}
+
+clang::MSInheritanceAttr::Spelling GetMSInheritance(
+ const PDBSymbolTypeUDT &udt) {
+ int base_count = 0;
+ bool has_virtual = false;
+
+ auto bases_enum = udt.findAllChildren<PDBSymbolTypeBaseClass>();
+ if (bases_enum) {
+ while (auto base = bases_enum->getNext()) {
+ base_count++;
+ has_virtual |= base->isVirtualBaseClass();
+ }
+ }
+
+ if (has_virtual)
+ return clang::MSInheritanceAttr::Keyword_virtual_inheritance;
+ if (base_count > 1)
+ return clang::MSInheritanceAttr::Keyword_multiple_inheritance;
+ return clang::MSInheritanceAttr::Keyword_single_inheritance;
+}
} // namespace
PDBASTParser::PDBASTParser(lldb_private::ClangASTContext &ast) : m_ast(ast) {}
@@ -216,29 +283,90 @@
Declaration decl;
switch (type.getSymTag()) {
+ case PDB_SymType::BaseClass: {
+ auto symbol_file = m_ast.GetSymbolFile();
+ if (!symbol_file)
+ return nullptr;
+
+ auto ty = symbol_file->ResolveTypeUID(type.getRawSymbol().getTypeId());
+ return ty ? ty->shared_from_this() : nullptr;
+ } break;
case PDB_SymType::UDT: {
auto udt = llvm::dyn_cast<PDBSymbolTypeUDT>(&type);
assert(udt);
- AccessType access = lldb::eAccessPublic;
- PDB_UdtType udt_kind = udt->getUdtKind();
- auto tag_type_kind = TranslateUdtKind(udt_kind);
- if (tag_type_kind == -1)
+
+ // Note that, unnamed UDT being typedef-ed is generated as a UDT symbol
+ // other than a Typedef symbol in PDB. For example,
+ // typedef union { short Row; short Col; } Union;
+ // is generated as a named UDT in PDB:
+ // union Union { short Row; short Col; }
+ // Such symbols will be handled here.
+
+ // Some UDT with trival ctor has zero length. Just ignore.
+ if (udt->getLength() == 0)
return nullptr;
- if (udt_kind == PDB_UdtType::Class)
- access = lldb::eAccessPrivate;
+ // Ignore unnamed-tag UDTs.
+ if (udt->getName().empty())
+ return nullptr;
+
+ auto access = GetAccessibilityForUdt(*udt);
+
+ auto tag_type_kind = TranslateUdtKind(udt->getUdtKind());
+
+ ClangASTMetadata metadata;
+ metadata.SetUserID(type.getSymIndexId());
+ metadata.SetIsDynamicCXXType(false);
CompilerType clang_type = m_ast.CreateRecordType(
tu_decl_ctx, access, udt->getName().c_str(), tag_type_kind,
- lldb::eLanguageTypeC_plus_plus, nullptr);
+ lldb::eLanguageTypeC_plus_plus, &metadata);
+ assert(clang_type.IsValid());
- m_ast.SetHasExternalStorage(clang_type.GetOpaqueQualType(), true);
+ if (udt->isConstType())
+ clang_type = clang_type.AddConstModifier();
+ if (udt->isVolatileType())
+ clang_type = clang_type.AddVolatileModifier();
+
+ clang::CXXRecordDecl *record_decl =
+ m_ast.GetAsCXXRecordDecl(clang_type.GetOpaqueQualType());
+ assert(record_decl);
+ auto inheritance_attr = clang::MSInheritanceAttr::CreateImplicit(
+ *m_ast.getASTContext(), GetMSInheritance(*udt));
+ record_decl->addAttr(inheritance_attr);
+
+ ClangASTContext::StartTagDeclarationDefinition(clang_type);
+
+ Type::ResolveStateTag type_resolve_state_tag;
+ auto children = udt->findAllChildren();
+ if (!children || children->getChildCount() == 0) {
+ // PDB does not have symbol of forwarder. We assume we get an udt w/o any
+ // fields. Just complete it at this point.
+ ClangASTContext::CompleteTagDeclarationDefinition(clang_type);
+
+ m_ast.SetHasExternalStorage(clang_type.GetOpaqueQualType(), false);
+
+ type_resolve_state_tag = Type::eResolveStateFull;
+ } else {
+ // Add the type to the forward declarations. It will help us to avoid
+ // an endless recursion in CompleteTypeFromUdt function.
+ auto clang_type_removed_fast_quals =
+ ClangUtil::RemoveFastQualifiers(clang_type).GetOpaqueQualType();
+ m_forward_decl_clang_type_to_uid[clang_type_removed_fast_quals] =
+ type.getSymIndexId();
+
+ m_ast.SetHasExternalStorage(clang_type.GetOpaqueQualType(), true);
+
+ type_resolve_state_tag = Type::eResolveStateForward;
+ }
+
+ GetDeclarationForSymbol(type, decl);
return std::make_shared<lldb_private::Type>(
type.getSymIndexId(), m_ast.GetSymbolFile(),
ConstString(udt->getName()), udt->getLength(), nullptr,
LLDB_INVALID_UID, lldb_private::Type::eEncodingIsUID, decl, clang_type,
- lldb_private::Type::eResolveStateForward);
+ type_resolve_state_tag);
} break;
case PDB_SymType::Enum: {
auto enum_type = llvm::dyn_cast<PDBSymbolTypeEnum>(&type);
@@ -441,6 +569,26 @@
if (!pointee_type)
return nullptr;
+ if (pointer_type->isPointerToDataMember() ||
+ pointer_type->isPointerToMemberFunction()) {
+ auto class_parent_uid = pointer_type->getRawSymbol().getClassParentId();
+ auto class_parent_type =
+ m_ast.GetSymbolFile()->ResolveTypeUID(class_parent_uid);
+ assert(class_parent_type);
+
+ CompilerType pointer_ast_type;
+ pointer_ast_type = ClangASTContext::CreateMemberPointerType(
+ class_parent_type->GetLayoutCompilerType(),
+ pointee_type->GetForwardCompilerType());
+ assert(pointer_ast_type);
+
+ return std::make_shared<lldb_private::Type>(
+ pointer_type->getSymIndexId(), m_ast.GetSymbolFile(), ConstString(),
+ pointer_type->getLength(), nullptr, LLDB_INVALID_UID,
+ lldb_private::Type::eEncodingIsUID, decl, pointer_ast_type,
+ lldb_private::Type::eResolveStateForward);
+ }
+
CompilerType pointer_ast_type;
pointer_ast_type = pointee_type->GetFullCompilerType();
if (pointer_type->isReference())
@@ -471,6 +619,47 @@
return nullptr;
}
+bool PDBASTParser::CompleteTypeFromPDB(
+ lldb_private::CompilerType &compiler_type) {
+ if (GetClangASTImporter().CanImport(compiler_type))
+ return GetClangASTImporter().CompleteType(compiler_type);
+
+ // Remove the type from the forward declarations to avoid
+ // an endless recursion for types like a linked list.
+ CompilerType compiler_type_no_qualifiers =
+ ClangUtil::RemoveFastQualifiers(compiler_type);
+ auto uid_it = m_forward_decl_clang_type_to_uid.find(
+ compiler_type_no_qualifiers.GetOpaqueQualType());
+ if (uid_it == m_forward_decl_clang_type_to_uid.end())
+ return true;
+
+ auto symbol_file = static_cast<SymbolFilePDB *>(m_ast.GetSymbolFile());
+ if (!symbol_file)
+ return false;
+
+ std::unique_ptr<PDBSymbol> symbol =
+ symbol_file->GetPDBSession().getSymbolById(uid_it->getSecond());
+ if (!symbol)
+ return false;
+
+ m_forward_decl_clang_type_to_uid.erase(uid_it);
+
+ ClangASTContext::SetHasExternalStorage(compiler_type.GetOpaqueQualType(),
+ false);
+
+ switch (symbol->getSymTag()) {
+ case PDB_SymType::UDT: {
+ auto udt = llvm::dyn_cast<PDBSymbolTypeUDT>(symbol.get());
+ if (!udt)
+ return false;
+
+ return CompleteTypeFromUDT(*symbol_file, compiler_type, *udt);
+ }
+ default:
+ llvm_unreachable("not a forward clang type decl!");
+ }
+}
+
bool PDBASTParser::AddEnumValue(CompilerType enum_type,
const PDBSymbolData &enum_value) const {
Declaration decl;
@@ -513,3 +702,187 @@
enum_type.GetOpaqueQualType(), underlying_type, decl, name.c_str(),
raw_value, byte_size * 8);
}
+
+bool PDBASTParser::CompleteTypeFromUDT(
+ lldb_private::SymbolFile &symbol_file,
+ lldb_private::CompilerType &compiler_type,
+ llvm::pdb::PDBSymbolTypeUDT &udt) {
+ ClangASTImporter::LayoutInfo layout_info;
+ layout_info.bit_size = udt.getLength() * 8;
+
+ auto nested_enums = udt.findAllChildren<PDBSymbolTypeUDT>();
+ if (nested_enums)
+ while (auto nested = nested_enums->getNext())
+ symbol_file.ResolveTypeUID(nested->getSymIndexId());
+
+ auto bases_enum = udt.findAllChildren<PDBSymbolTypeBaseClass>();
+ if (bases_enum)
+ AddRecordBases(symbol_file, compiler_type,
+ TranslateUdtKind(udt.getUdtKind()), *bases_enum,
+ layout_info);
+
+ auto members_enum = udt.findAllChildren<PDBSymbolData>();
+ if (members_enum)
+ AddRecordMembers(symbol_file, compiler_type, *members_enum, layout_info);
+
+ auto methods_enum = udt.findAllChildren<PDBSymbolFunc>();
+ if (methods_enum)
+ AddRecordMethods(symbol_file, compiler_type, *methods_enum);
+
+ m_ast.AddMethodOverridesForCXXRecordType(compiler_type.GetOpaqueQualType());
+ ClangASTContext::BuildIndirectFields(compiler_type);
+ ClangASTContext::CompleteTagDeclarationDefinition(compiler_type);
+
+ clang::CXXRecordDecl *record_decl =
+ m_ast.GetAsCXXRecordDecl(compiler_type.GetOpaqueQualType());
+ if (!record_decl)
+ return static_cast<bool>(compiler_type);
+
+ GetClangASTImporter().InsertRecordDecl(record_decl, layout_info);
+
+ return static_cast<bool>(compiler_type);
+}
+
+void PDBASTParser::AddRecordMembers(
+ lldb_private::SymbolFile &symbol_file,
+ lldb_private::CompilerType &record_type,
+ PDBDataSymbolEnumerator &members_enum,
+ lldb_private::ClangASTImporter::LayoutInfo &layout_info) const {
+ while (auto member = members_enum.getNext()) {
+ if (member->isCompilerGenerated())
+ continue;
+
+ auto member_name = member->getName();
+
+ auto member_type = symbol_file.ResolveTypeUID(member->getTypeId());
+ if (!member_type)
+ continue;
+
+ auto member_comp_type = member_type->GetLayoutCompilerType();
+ if (!member_comp_type.GetCompleteType()) {
+ symbol_file.GetObjectFile()->GetModule()->ReportError(
+ ":: Class '%s' has a member '%s' of type '%s' "
+ "which does not have a complete definition.",
+ record_type.GetTypeName().GetCString(), member_name.c_str(),
+ member_comp_type.GetTypeName().GetCString());
+ if (ClangASTContext::StartTagDeclarationDefinition(member_comp_type))
+ ClangASTContext::CompleteTagDeclarationDefinition(member_comp_type);
+ }
+
+ auto access = TranslateMemberAccess(member->getAccess());
+
+ switch (member->getDataKind()) {
+ case PDB_DataKind::Member: {
+ auto location_type = member->getLocationType();
+
+ auto bit_size = member->getLength();
+ if (location_type == PDB_LocType::ThisRel)
+ bit_size *= 8;
+
+ auto decl = ClangASTContext::AddFieldToRecordType(
+ record_type, member_name.c_str(), member_comp_type, access, bit_size);
+ if (!decl)
+ continue;
+
+ auto offset = member->getOffset() * 8;
+ if (location_type == PDB_LocType::BitField)
+ offset += member->getBitPosition();
+
+ layout_info.field_offsets.insert(std::make_pair(decl, offset));
+
+ break;
+ }
+ case PDB_DataKind::StaticMember:
+ ClangASTContext::AddVariableToRecordType(record_type, member_name.c_str(),
+ member_comp_type, access);
+ break;
+ default:
+ llvm_unreachable("unsupported PDB data kind");
+ }
+ }
+}
+
+void PDBASTParser::AddRecordBases(
+ lldb_private::SymbolFile &symbol_file,
+ lldb_private::CompilerType &record_type, int record_kind,
+ PDBBaseClassSymbolEnumerator &bases_enum,
+ lldb_private::ClangASTImporter::LayoutInfo &layout_info) const {
+ std::vector<clang::CXXBaseSpecifier *> base_classes;
+ while (auto base = bases_enum.getNext()) {
+ auto base_type = symbol_file.ResolveTypeUID(base->getTypeId());
+ if (!base_type)
+ continue;
+
+ auto base_comp_type = base_type->GetFullCompilerType();
+ if (!base_comp_type.GetCompleteType()) {
+ symbol_file.GetObjectFile()->GetModule()->ReportError(
+ ":: Class '%s' has a base class '%s' "
+ "which does not have a complete definition.",
+ record_type.GetTypeName().GetCString(),
+ base_comp_type.GetTypeName().GetCString());
+ if (ClangASTContext::StartTagDeclarationDefinition(base_comp_type))
+ ClangASTContext::CompleteTagDeclarationDefinition(base_comp_type);
+ }
+
+ auto access = TranslateMemberAccess(base->getAccess());
+
+ auto is_virtual = base->isVirtualBaseClass();
+
+ auto base_class_spec = m_ast.CreateBaseClassSpecifier(
+ base_comp_type.GetOpaqueQualType(), access, is_virtual,
+ record_kind == clang::TTK_Class);
+ if (!base_class_spec)
+ continue;
+
+ base_classes.push_back(base_class_spec);
+
+ if (is_virtual)
+ continue;
+
+ auto decl = m_ast.GetAsCXXRecordDecl(base_comp_type.GetOpaqueQualType());
+ if (!decl)
+ continue;
+
+ auto offset = clang::CharUnits::fromQuantity(base->getOffset());
+ layout_info.base_offsets.insert(std::make_pair(decl, offset));
+ }
+ if (!base_classes.empty()) {
+ m_ast.SetBaseClassesForClassType(record_type.GetOpaqueQualType(),
+ &base_classes.front(),
+ base_classes.size());
+ ClangASTContext::DeleteBaseClassSpecifiers(&base_classes.front(),
+ base_classes.size());
+ }
+}
+
+void PDBASTParser::AddRecordMethods(
+ lldb_private::SymbolFile &symbol_file,
+ lldb_private::CompilerType &record_type,
+ PDBFuncSymbolEnumerator &methods_enum) const {
+ while (auto method = methods_enum.getNext()) {
+ auto method_type = symbol_file.ResolveTypeUID(method->getSymIndexId());
+ // MSVC specific __vecDelDtor.
+ if (!method_type)
+ break;
+
+ auto method_comp_type = method_type->GetFullCompilerType();
+ if (!method_comp_type.GetCompleteType()) {
+ symbol_file.GetObjectFile()->GetModule()->ReportError(
+ ":: Class '%s' has a method '%s' whose type cannot be completed.",
+ record_type.GetTypeName().GetCString(),
+ method_comp_type.GetTypeName().GetCString());
+ if (ClangASTContext::StartTagDeclarationDefinition(method_comp_type))
+ ClangASTContext::CompleteTagDeclarationDefinition(method_comp_type);
+ }
+
+ // TODO: get mangled name for the method.
+ m_ast.AddMethodToCXXRecordType(
+ record_type.GetOpaqueQualType(), method->getName().c_str(),
+ /*mangled_name*/ nullptr, method_comp_type,
+ TranslateMemberAccess(method->getAccess()), method->isVirtual(),
+ method->isStatic(), method->hasInlineAttribute(),
+ /*is_explicit*/ false, // FIXME: Need this field in CodeView.
+ /*is_attr_used*/ false,
+ /*is_artificial*/ method->isCompilerGenerated());
+ }
+}