https://github.com/Michael137 updated https://github.com/llvm/llvm-project/pull/149827
>From fd6b6e8a3168fc233635e783773554ac980edb46 Mon Sep 17 00:00:00 2001 From: Michael Buch <michaelbuc...@gmail.com> Date: Fri, 15 Nov 2024 01:59:36 +0000 Subject: [PATCH 1/5] [lldb][Expression] Encode Module and DIE UIDs into function AsmLabels --- lldb/include/lldb/Core/Module.h | 4 +- lldb/include/lldb/Expression/Expression.h | 25 ++++++ lldb/include/lldb/Symbol/SymbolFile.h | 14 ++++ lldb/include/lldb/Symbol/TypeSystem.h | 16 ++++ lldb/source/Core/Module.cpp | 19 ++++- lldb/source/Expression/Expression.cpp | 16 ++++ lldb/source/Expression/IRExecutionUnit.cpp | 74 +++++++++++++++++ .../Clang/ClangExpressionDeclMap.cpp | 2 +- .../SymbolFile/DWARF/DWARFASTParserClang.cpp | 43 ++++++---- .../SymbolFile/DWARF/SymbolFileDWARF.cpp | 41 ++++++++++ .../SymbolFile/DWARF/SymbolFileDWARF.h | 3 + .../SymbolFile/NativePDB/PdbAstBuilder.cpp | 6 +- .../NativePDB/UdtRecordCompleter.cpp | 5 +- .../Plugins/SymbolFile/PDB/PDBASTParser.cpp | 7 +- .../TypeSystem/Clang/TypeSystemClang.cpp | 81 +++++++++++++++++-- .../TypeSystem/Clang/TypeSystemClang.h | 11 ++- lldb/unittests/Symbol/TestTypeSystemClang.cpp | 12 +-- 17 files changed, 335 insertions(+), 44 deletions(-) diff --git a/lldb/include/lldb/Core/Module.h b/lldb/include/lldb/Core/Module.h index 8bb55c95773bc..3991a12997541 100644 --- a/lldb/include/lldb/Core/Module.h +++ b/lldb/include/lldb/Core/Module.h @@ -86,7 +86,8 @@ struct ModuleFunctionSearchOptions { /// /// The module will parse more detailed information as more queries are made. class Module : public std::enable_shared_from_this<Module>, - public SymbolContextScope { + public SymbolContextScope, + public UserID { public: class LookupInfo; // Static functions that can track the lifetime of module objects. This is @@ -97,6 +98,7 @@ class Module : public std::enable_shared_from_this<Module>, // using the "--global" (-g for short). static size_t GetNumberAllocatedModules(); + static Module *GetAllocatedModuleWithUID(lldb::user_id_t uid); static Module *GetAllocatedModuleAtIndex(size_t idx); static std::recursive_mutex &GetAllocationModuleCollectionMutex(); diff --git a/lldb/include/lldb/Expression/Expression.h b/lldb/include/lldb/Expression/Expression.h index 8de9364436ccf..f32878c9bf876 100644 --- a/lldb/include/lldb/Expression/Expression.h +++ b/lldb/include/lldb/Expression/Expression.h @@ -96,6 +96,31 @@ class Expression { ///invalid. }; +/// Holds parsed information about a function call label that +/// LLDB attaches as an AsmLabel to function AST nodes it parses +/// from debug-info. +/// +/// The format being: +/// +/// <prefix>:<mangled name>:<module id>:<DIE id> +/// +/// The label string needs to stay valid for the entire lifetime +/// of this object. +struct FunctionCallLabel { + llvm::StringRef m_lookup_name; + lldb::user_id_t m_module_id; + + /// Mostly for debuggability. + lldb::user_id_t m_die_id; +}; + +/// LLDB attaches this prefix to mangled names of functions that it get called +/// from JITted expressions. +inline constexpr llvm::StringRef FunctionCallLabelPrefix = "$__lldb_func"; + +bool consumeFunctionCallLabelPrefix(llvm::StringRef &name); +bool hasFunctionCallLabelPrefix(llvm::StringRef name); + } // namespace lldb_private #endif // LLDB_EXPRESSION_EXPRESSION_H diff --git a/lldb/include/lldb/Symbol/SymbolFile.h b/lldb/include/lldb/Symbol/SymbolFile.h index e95f95553c17c..6aca276fc85b6 100644 --- a/lldb/include/lldb/Symbol/SymbolFile.h +++ b/lldb/include/lldb/Symbol/SymbolFile.h @@ -18,6 +18,7 @@ #include "lldb/Symbol/CompilerType.h" #include "lldb/Symbol/Function.h" #include "lldb/Symbol/SourceModule.h" +#include "lldb/Symbol/SymbolContext.h" #include "lldb/Symbol/Type.h" #include "lldb/Symbol/TypeList.h" #include "lldb/Symbol/TypeSystem.h" @@ -328,6 +329,19 @@ class SymbolFile : public PluginInterface { GetMangledNamesForFunction(const std::string &scope_qualified_name, std::vector<ConstString> &mangled_names); + /// Resolves the function DIE identified by \c lookup_name within + /// this SymbolFile. + /// + /// \param[in,out] sc_list The resolved functions will be appended to this + /// list. + /// + /// \param[in] lookup_name The UID of the function DIE to resolve. + /// + virtual llvm::Error FindAndResolveFunction(SymbolContextList &sc_list, + llvm::StringRef lookup_name) { + return llvm::createStringError("Not implemented"); + } + virtual void GetTypes(lldb_private::SymbolContextScope *sc_scope, lldb::TypeClass type_mask, lldb_private::TypeList &type_list) = 0; diff --git a/lldb/include/lldb/Symbol/TypeSystem.h b/lldb/include/lldb/Symbol/TypeSystem.h index cb1f0130b548d..742c09251ea2f 100644 --- a/lldb/include/lldb/Symbol/TypeSystem.h +++ b/lldb/include/lldb/Symbol/TypeSystem.h @@ -548,6 +548,22 @@ class TypeSystem : public PluginInterface, bool GetHasForcefullyCompletedTypes() const { return m_has_forcefully_completed_types; } + + /// Returns the components of the specified function call label. + /// + /// The format of \c label is described in \c FunctionCallLabel. + /// The label prefix is not one of the components. + virtual llvm::Expected<llvm::SmallVector<llvm::StringRef, 3>> + splitFunctionCallLabel(llvm::StringRef label) const { + return llvm::createStringError("Not implemented."); + } + + // Decodes the function label into a \c FunctionCallLabel. + virtual llvm::Expected<FunctionCallLabel> + makeFunctionCallLabel(llvm::StringRef label) const { + return llvm::createStringError("Not implemented."); + } + protected: SymbolFile *m_sym_file = nullptr; /// Used for reporting statistics. diff --git a/lldb/source/Core/Module.cpp b/lldb/source/Core/Module.cpp index 90997dada3666..edd79aff5d065 100644 --- a/lldb/source/Core/Module.cpp +++ b/lldb/source/Core/Module.cpp @@ -121,6 +121,15 @@ size_t Module::GetNumberAllocatedModules() { return GetModuleCollection().size(); } +Module *Module::GetAllocatedModuleWithUID(lldb::user_id_t uid) { + std::lock_guard<std::recursive_mutex> guard( + GetAllocationModuleCollectionMutex()); + for (Module *mod : GetModuleCollection()) + if (mod->GetID() == uid) + return mod; + return nullptr; +} + Module *Module::GetAllocatedModuleAtIndex(size_t idx) { std::lock_guard<std::recursive_mutex> guard( GetAllocationModuleCollectionMutex()); @@ -130,8 +139,11 @@ Module *Module::GetAllocatedModuleAtIndex(size_t idx) { return nullptr; } +// TODO: needs a mutex +static lldb::user_id_t g_unique_id = 1; + Module::Module(const ModuleSpec &module_spec) - : m_unwind_table(*this), m_file_has_changed(false), + : UserID(g_unique_id++), m_unwind_table(*this), m_file_has_changed(false), m_first_file_changed_log(false) { // Scope for locker below... { @@ -236,7 +248,8 @@ Module::Module(const ModuleSpec &module_spec) Module::Module(const FileSpec &file_spec, const ArchSpec &arch, ConstString object_name, lldb::offset_t object_offset, const llvm::sys::TimePoint<> &object_mod_time) - : m_mod_time(FileSystem::Instance().GetModificationTime(file_spec)), + : UserID(g_unique_id++), + m_mod_time(FileSystem::Instance().GetModificationTime(file_spec)), m_arch(arch), m_file(file_spec), m_object_name(object_name), m_object_offset(object_offset), m_object_mod_time(object_mod_time), m_unwind_table(*this), m_file_has_changed(false), @@ -257,7 +270,7 @@ Module::Module(const FileSpec &file_spec, const ArchSpec &arch, } Module::Module() - : m_unwind_table(*this), m_file_has_changed(false), + : UserID(g_unique_id++), m_unwind_table(*this), m_file_has_changed(false), m_first_file_changed_log(false) { std::lock_guard<std::recursive_mutex> guard( GetAllocationModuleCollectionMutex()); diff --git a/lldb/source/Expression/Expression.cpp b/lldb/source/Expression/Expression.cpp index 93f585edfce3d..e19c804caa3c6 100644 --- a/lldb/source/Expression/Expression.cpp +++ b/lldb/source/Expression/Expression.cpp @@ -10,6 +10,10 @@ #include "lldb/Target/ExecutionContextScope.h" #include "lldb/Target/Target.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" + using namespace lldb_private; Expression::Expression(Target &target) @@ -26,3 +30,15 @@ Expression::Expression(ExecutionContextScope &exe_scope) m_jit_end_addr(LLDB_INVALID_ADDRESS) { assert(m_target_wp.lock()); } + +bool lldb_private::consumeFunctionCallLabelPrefix(llvm::StringRef &name) { + // On Darwin mangled names get a '_' prefix. + name.consume_front("_"); + return name.consume_front(FunctionCallLabelPrefix); +} + +bool lldb_private::hasFunctionCallLabelPrefix(llvm::StringRef name) { + // On Darwin mangled names get a '_' prefix. + name.consume_front("_"); + return name.starts_with(FunctionCallLabelPrefix); +} diff --git a/lldb/source/Expression/IRExecutionUnit.cpp b/lldb/source/Expression/IRExecutionUnit.cpp index 6f812b91a8b1d..a9ea244889cce 100644 --- a/lldb/source/Expression/IRExecutionUnit.cpp +++ b/lldb/source/Expression/IRExecutionUnit.cpp @@ -13,6 +13,7 @@ #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" +#include "llvm/Support/Error.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/raw_ostream.h" @@ -20,6 +21,7 @@ #include "lldb/Core/Disassembler.h" #include "lldb/Core/Module.h" #include "lldb/Core/Section.h" +#include "lldb/Expression/Expression.h" #include "lldb/Expression/IRExecutionUnit.h" #include "lldb/Expression/ObjectFileJIT.h" #include "lldb/Host/HostInfo.h" @@ -36,6 +38,7 @@ #include "lldb/Utility/LLDBAssert.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" +#include "lldb/lldb-defines.h" #include <optional> @@ -771,6 +774,63 @@ class LoadAddressResolver { lldb::addr_t m_best_internal_load_address = LLDB_INVALID_ADDRESS; }; +/// Returns address of the function referred to by the special function call +/// label \c label. +/// +/// \param[in] label Function call label encoding the unique location of the +/// function to look up. +/// Assumes that the \c FunctionCallLabelPrefix has been +/// stripped from the front of the label. +static llvm::Expected<lldb::addr_t> +ResolveFunctionCallLabel(llvm::StringRef name, + const lldb_private::SymbolContext &sc, + bool &symbol_was_missing_weak) { + symbol_was_missing_weak = false; + + if (!sc.target_sp) + return llvm::createStringError("target not available."); + + auto ts_or_err = sc.target_sp->GetScratchTypeSystemForLanguage( + lldb::eLanguageTypeC_plus_plus); + if (!ts_or_err || !*ts_or_err) + return llvm::joinErrors( + llvm::createStringError( + "failed to find scratch C++ TypeSystem for current target."), + ts_or_err.takeError()); + + auto ts_sp = *ts_or_err; + + auto label_or_err = ts_sp->makeFunctionCallLabel(name); + if (!label_or_err) + return llvm::joinErrors( + llvm::createStringError("failed to create FunctionCallLabel from: %s", + name.data()), + label_or_err.takeError()); + + const auto &label = *label_or_err; + + Module *module = Module::GetAllocatedModuleWithUID(label.m_module_id); + + if (!module) + return llvm::createStringError( + llvm::formatv("failed to find module by UID {0}", label.m_module_id)); + + auto *symbol_file = module->GetSymbolFile(); + if (!symbol_file) + return llvm::createStringError( + llvm::formatv("no SymbolFile found on module {0:x}.", module)); + + SymbolContextList sc_list; + if (auto err = + symbol_file->FindAndResolveFunction(sc_list, label.m_lookup_name)) + return llvm::joinErrors( + llvm::createStringError("failed to resolve function by UID"), + std::move(err)); + + LoadAddressResolver resolver(*sc.target_sp, symbol_was_missing_weak); + return resolver.Resolve(sc_list).value_or(LLDB_INVALID_ADDRESS); +} + lldb::addr_t IRExecutionUnit::FindInSymbols(const std::vector<ConstString> &names, const lldb_private::SymbolContext &sc, @@ -906,6 +966,20 @@ lldb::addr_t IRExecutionUnit::FindInUserDefinedSymbols( lldb::addr_t IRExecutionUnit::FindSymbol(lldb_private::ConstString name, bool &missing_weak) { + if (hasFunctionCallLabelPrefix(name.GetStringRef())) { + if (auto addr_or_err = ResolveFunctionCallLabel(name.GetStringRef(), + m_sym_ctx, missing_weak)) { + return *addr_or_err; + } else { + LLDB_LOG_ERROR(GetLog(LLDBLog::Expressions), addr_or_err.takeError(), + "Failed to resolve function call label {1}: {0}", + name.GetStringRef()); + return LLDB_INVALID_ADDRESS; + } + } + + // TODO: do we still need the following lookups? + std::vector<ConstString> candidate_C_names; std::vector<ConstString> candidate_CPlusPlus_names; diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp index 9f77fbc1d2434..a6c4334bf2e59 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp @@ -1991,7 +1991,7 @@ void ClangExpressionDeclMap::AddContextClassType(NameSearchContext &context, const bool is_artificial = false; CXXMethodDecl *method_decl = m_clang_ast_context->AddMethodToCXXRecordType( - copied_clang_type.GetOpaqueQualType(), "$__lldb_expr", nullptr, + copied_clang_type.GetOpaqueQualType(), "$__lldb_expr", std::nullopt, method_type, lldb::eAccessPublic, is_virtual, is_static, is_inline, is_explicit, is_attr_used, is_artificial); diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp index c76d67b47b336..6e9598c13ed5c 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -24,6 +24,7 @@ #include "Plugins/Language/ObjC/ObjCLanguage.h" #include "lldb/Core/Module.h" #include "lldb/Core/Value.h" +#include "lldb/Expression/Expression.h" #include "lldb/Host/Host.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/Function.h" @@ -249,6 +250,28 @@ static unsigned GetCXXMethodCVQuals(const DWARFDIE &subprogram, return cv_quals; } +static std::optional<std::string> MakeLLDBFuncAsmLabel(const DWARFDIE &die) { + char const *name = die.GetMangledName(/*substitute_name_allowed*/ false); + if (!name) + return std::nullopt; + + auto module_sp = die.GetModule(); + if (!module_sp) + return std::nullopt; + + lldb::user_id_t module_id = module_sp->GetID(); + if (module_id == LLDB_INVALID_UID) + return std::nullopt; + + const auto die_id = die.GetID(); + if (die_id == LLDB_INVALID_UID) + return std::nullopt; + + return llvm::formatv("{0}:{1}:{2:x}:{3:x}", FunctionCallLabelPrefix, name, + module_id, die_id) + .str(); +} + TypeSP DWARFASTParserClang::ParseTypeFromClangModule(const SymbolContext &sc, const DWARFDIE &die, Log *log) { @@ -1231,7 +1254,7 @@ std::pair<bool, TypeSP> DWARFASTParserClang::ParseCXXMethod( clang::CXXMethodDecl *cxx_method_decl = m_ast.AddMethodToCXXRecordType( class_opaque_type.GetOpaqueQualType(), attrs.name.GetCString(), - attrs.mangled_name, clang_type, accessibility, attrs.is_virtual, + MakeLLDBFuncAsmLabel(die), clang_type, accessibility, attrs.is_virtual, is_static, attrs.is_inline, attrs.is_explicit, is_attr_used, attrs.is_artificial); @@ -1384,7 +1407,7 @@ DWARFASTParserClang::ParseSubroutine(const DWARFDIE &die, ignore_containing_context ? m_ast.GetTranslationUnitDecl() : containing_decl_ctx, GetOwningClangModule(die), name, clang_type, attrs.storage, - attrs.is_inline); + attrs.is_inline, MakeLLDBFuncAsmLabel(die)); std::free(name_buf); if (has_template_params) { @@ -1394,7 +1417,7 @@ DWARFASTParserClang::ParseSubroutine(const DWARFDIE &die, ignore_containing_context ? m_ast.GetTranslationUnitDecl() : containing_decl_ctx, GetOwningClangModule(die), attrs.name.GetStringRef(), clang_type, - attrs.storage, attrs.is_inline); + attrs.storage, attrs.is_inline, /*asm_label=*/std::nullopt); clang::FunctionTemplateDecl *func_template_decl = m_ast.CreateFunctionTemplateDecl( containing_decl_ctx, GetOwningClangModule(die), @@ -1406,20 +1429,6 @@ DWARFASTParserClang::ParseSubroutine(const DWARFDIE &die, lldbassert(function_decl); if (function_decl) { - // Attach an asm(<mangled_name>) label to the FunctionDecl. - // This ensures that clang::CodeGen emits function calls - // using symbols that are mangled according to the DW_AT_linkage_name. - // If we didn't do this, the external symbols wouldn't exactly - // match the mangled name LLDB knows about and the IRExecutionUnit - // would have to fall back to searching object files for - // approximately matching function names. The motivating - // example is generating calls to ABI-tagged template functions. - // This is done separately for member functions in - // AddMethodToCXXRecordType. - if (attrs.mangled_name) - function_decl->addAttr(clang::AsmLabelAttr::CreateImplicit( - m_ast.getASTContext(), attrs.mangled_name, /*literal=*/false)); - LinkDeclContextToDIE(function_decl, die); const clang::FunctionProtoType *function_prototype( diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp index 5b16ce5f75138..b6960bfe38fde 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -2471,6 +2471,47 @@ bool SymbolFileDWARF::ResolveFunction(const DWARFDIE &orig_die, return false; } +llvm::Error +SymbolFileDWARF::FindAndResolveFunction(SymbolContextList &sc_list, + llvm::StringRef lookup_name) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + + DWARFDIE die; + Module::LookupInfo info(ConstString(lookup_name), lldb::eFunctionNameTypeFull, + lldb::eLanguageTypeUnknown); + + m_index->GetFunctions(info, *this, {}, [&](DWARFDIE entry) { + if (entry.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0)) + return true; + + // We don't check whether the specification DIE for this function + // corresponds to the declaration DIE because the declaration might be in + // a type-unit but the definition in the compile-unit (and it's + // specifcation would point to the declaration in the compile-unit). We + // rely on the mangled name within the module to be enough to find us the + // unique definition. + die = entry; + return false; + }); + + if (!die.IsValid()) + return llvm::createStringError( + llvm::formatv("failed to find definition DIE for '{0}'", lookup_name)); + + if (!ResolveFunction(die, false, sc_list)) + return llvm::createStringError("failed to resolve function DIE"); + + if (sc_list.IsEmpty()) + return llvm::createStringError("no definition DIE found"); + + if (sc_list.GetSize() > 1) + return llvm::createStringError( + "found %d functions for %s but expected only 1", sc_list.GetSize(), + lookup_name.data()); + + return llvm::Error::success(); +} + bool SymbolFileDWARF::DIEInDeclContext(const CompilerDeclContext &decl_ctx, const DWARFDIE &die, bool only_root_namespaces) { diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h index 2dc862cccca14..ee966366d3f2b 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h @@ -434,6 +434,9 @@ class SymbolFileDWARF : public SymbolFileCommon { DIEArray MergeBlockAbstractParameters(const DWARFDIE &block_die, DIEArray &&variable_dies); + llvm::Error FindAndResolveFunction(SymbolContextList &sc_list, + llvm::StringRef lookup_name) override; + // Given a die_offset, figure out the symbol context representing that die. bool ResolveFunction(const DWARFDIE &die, bool include_inlines, SymbolContextList &sc_list); diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp index 702ec5e5c9ea9..bce721c149fee 100644 --- a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp +++ b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp @@ -88,7 +88,7 @@ struct CreateMethodDecl : public TypeVisitorCallbacks { MethodOptions::CompilerGenerated; function_decl = m_clang.AddMethodToCXXRecordType( parent_ty, proc_name, - /*mangled_name=*/nullptr, func_ct, /*access=*/access_type, + /*mangled_name=*/std::nullopt, func_ct, /*access=*/access_type, /*is_virtual=*/is_virtual, /*is_static=*/is_static, /*is_inline=*/false, /*is_explicit=*/false, /*is_attr_used=*/false, /*is_artificial=*/is_artificial); @@ -903,7 +903,7 @@ PdbAstBuilder::CreateFunctionDecl(PdbCompilandSymId func_id, if (!function_decl) { function_decl = m_clang.AddMethodToCXXRecordType( parent_opaque_ty, func_name, - /*mangled_name=*/nullptr, func_ct, + /*mangled_name=*/std::nullopt, func_ct, /*access=*/lldb::AccessType::eAccessPublic, /*is_virtual=*/false, /*is_static=*/false, /*is_inline=*/false, /*is_explicit=*/false, @@ -913,7 +913,7 @@ PdbAstBuilder::CreateFunctionDecl(PdbCompilandSymId func_id, } else { function_decl = m_clang.CreateFunctionDeclaration( parent, OptionalClangModuleID(), func_name, func_ct, func_storage, - is_inline); + is_inline, /*asm_label=*/std::nullopt); CreateFunctionParameters(func_id, *function_decl, param_count); } return function_decl; diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp index 807ee5b30779c..5e77ea7f85603 100644 --- a/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp +++ b/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp @@ -111,9 +111,8 @@ void UdtRecordCompleter::AddMethod(llvm::StringRef name, TypeIndex type_idx, bool is_artificial = (options & MethodOptions::CompilerGenerated) == MethodOptions::CompilerGenerated; m_ast_builder.clang().AddMethodToCXXRecordType( - derived_opaque_ty, name.data(), nullptr, method_ct, - access_type, attrs.isVirtual(), attrs.isStatic(), false, false, false, - is_artificial); + derived_opaque_ty, name.data(), std::nullopt, method_ct, access_type, + attrs.isVirtual(), attrs.isStatic(), false, false, false, is_artificial); m_cxx_record_map[derived_opaque_ty].insert({name, method_ct}); } diff --git a/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.cpp b/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.cpp index 0090d8ff03ab6..548a3ed25111f 100644 --- a/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.cpp +++ b/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.cpp @@ -954,7 +954,8 @@ PDBASTParser::GetDeclForSymbol(const llvm::pdb::PDBSymbol &symbol) { auto decl = m_ast.CreateFunctionDeclaration( decl_context, OptionalClangModuleID(), name, - type->GetForwardCompilerType(), storage, func->hasInlineAttribute()); + type->GetForwardCompilerType(), storage, func->hasInlineAttribute(), + /*asm_label=*/std::nullopt); std::vector<clang::ParmVarDecl *> params; if (std::unique_ptr<PDBSymbolTypeFunctionSig> sig = func->getSignature()) { @@ -1446,8 +1447,8 @@ PDBASTParser::AddRecordMethod(lldb_private::SymbolFile &symbol_file, // TODO: get mangled name for the method. return m_ast.AddMethodToCXXRecordType( record_type.GetOpaqueQualType(), name.c_str(), - /*mangled_name*/ nullptr, method_comp_type, access, method.isVirtual(), - method.isStatic(), method.hasInlineAttribute(), + /*mangled_name*/ std::nullopt, method_comp_type, access, + method.isVirtual(), method.isStatic(), method.hasInlineAttribute(), /*is_explicit*/ false, // FIXME: Need this field in CodeView. /*is_attr_used*/ false, /*is_artificial*/ method.isCompilerGenerated()); diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index e847ede1a4ba6..03bdc555c0613 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -60,6 +60,7 @@ #include "lldb/Core/Module.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/UniqueCStringMap.h" +#include "lldb/Expression/Expression.h" #include "lldb/Host/StreamFile.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/SymbolFile.h" @@ -2137,7 +2138,8 @@ std::string TypeSystemClang::GetTypeNameForDecl(const NamedDecl *named_decl, FunctionDecl *TypeSystemClang::CreateFunctionDeclaration( clang::DeclContext *decl_ctx, OptionalClangModuleID owning_module, llvm::StringRef name, const CompilerType &function_clang_type, - clang::StorageClass storage, bool is_inline) { + clang::StorageClass storage, bool is_inline, + std::optional<std::string> asm_label) { FunctionDecl *func_decl = nullptr; ASTContext &ast = getASTContext(); if (!decl_ctx) @@ -2158,6 +2160,21 @@ FunctionDecl *TypeSystemClang::CreateFunctionDeclaration( func_decl->setConstexprKind(isConstexprSpecified ? ConstexprSpecKind::Constexpr : ConstexprSpecKind::Unspecified); + + // Attach an asm(<mangled_name>) label to the FunctionDecl. + // This ensures that clang::CodeGen emits function calls + // using symbols that are mangled according to the DW_AT_linkage_name. + // If we didn't do this, the external symbols wouldn't exactly + // match the mangled name LLDB knows about and the IRExecutionUnit + // would have to fall back to searching object files for + // approximately matching function names. The motivating + // example is generating calls to ABI-tagged template functions. + // This is done separately for member functions in + // AddMethodToCXXRecordType. + if (asm_label) + func_decl->addAttr(clang::AsmLabelAttr::CreateImplicit(ast, *asm_label, + /*literal=*/false)); + SetOwningModule(func_decl, owning_module); decl_ctx->addDecl(func_decl); @@ -7647,7 +7664,7 @@ TypeSystemClang::CreateParameterDeclarations( clang::CXXMethodDecl *TypeSystemClang::AddMethodToCXXRecordType( lldb::opaque_compiler_type_t type, llvm::StringRef name, - const char *mangled_name, const CompilerType &method_clang_type, + std::optional<std::string> asm_label, const CompilerType &method_clang_type, lldb::AccessType access, bool is_virtual, bool is_static, bool is_inline, bool is_explicit, bool is_attr_used, bool is_artificial) { if (!type || !method_clang_type.IsValid() || name.empty()) @@ -7780,10 +7797,9 @@ clang::CXXMethodDecl *TypeSystemClang::AddMethodToCXXRecordType( if (is_attr_used) cxx_method_decl->addAttr(clang::UsedAttr::CreateImplicit(getASTContext())); - if (mangled_name != nullptr) { + if (asm_label) cxx_method_decl->addAttr(clang::AsmLabelAttr::CreateImplicit( - getASTContext(), mangled_name, /*literal=*/false)); - } + getASTContext(), *asm_label, /*literal=*/false)); // Parameters on member function declarations in DWARF generally don't // have names, so we omit them when creating the ParmVarDecls. @@ -9037,6 +9053,13 @@ ConstString TypeSystemClang::DeclGetMangledName(void *opaque_decl) { if (!mc || !mc->shouldMangleCXXName(nd)) return {}; + if (const auto *label = nd->getAttr<AsmLabelAttr>()) { + if (auto components_or_err = splitFunctionCallLabel(label->getLabel())) + return ConstString((*components_or_err)[0]); + else + llvm::consumeError(components_or_err.takeError()); + } + llvm::SmallVector<char, 1024> buf; llvm::raw_svector_ostream llvm_ostrm(buf); if (llvm::isa<clang::CXXConstructorDecl>(nd)) { @@ -9759,3 +9782,51 @@ void TypeSystemClang::LogCreation() const { LLDB_LOG(log, "Created new TypeSystem for (ASTContext*){0:x} '{1}'", &getASTContext(), getDisplayName()); } + +// Expected format is: +// $__lldb_func:<mangled name>:<module id>:<definition/declaration DIE id> +llvm::Expected<llvm::SmallVector<llvm::StringRef, 3>> +TypeSystemClang::splitFunctionCallLabel(llvm::StringRef label) const { + if (!consumeFunctionCallLabelPrefix(label)) + return llvm::createStringError( + "expected function call label prefix not found in %s", label.data()); + if (!label.consume_front(":")) + return llvm::createStringError( + "incorrect format: expected ':' as the first character."); + + llvm::SmallVector<llvm::StringRef, 3> components; + label.split(components, ":"); + + if (components.size() != 3) + return llvm::createStringError( + "incorrect format: too many label subcomponents."); + + return components; +} + +llvm::Expected<FunctionCallLabel> +TypeSystemClang::makeFunctionCallLabel(llvm::StringRef label) const { + auto components_or_err = splitFunctionCallLabel(label); + if (!components_or_err) + return llvm::joinErrors( + llvm::createStringError("Failed to decode function call label"), + components_or_err.takeError()); + + const auto &components = *components_or_err; + + llvm::StringRef module_label = components[1]; + llvm::StringRef die_label = components[2]; + + lldb::user_id_t module_id = 0; + if (module_label.consumeInteger(0, module_id)) + return llvm::createStringError( + llvm::formatv("failed to parse module ID from '{0}'.", components[1])); + + lldb::user_id_t die_id; + if (die_label.consumeInteger(/*Radix=*/0, die_id)) + return llvm::createStringError( + llvm::formatv("failed to parse DIE ID from '{0}'.", components[2])); + + return FunctionCallLabel{/*.m_lookup_name =*/components[0], + /*.m_module_id =*/module_id, /*.m_die_id =*/die_id}; +} diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h index 63dee9dceded3..726a0eea9382a 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h @@ -477,7 +477,8 @@ class TypeSystemClang : public TypeSystem { clang::FunctionDecl *CreateFunctionDeclaration( clang::DeclContext *decl_ctx, OptionalClangModuleID owning_module, llvm::StringRef name, const CompilerType &function_Type, - clang::StorageClass storage, bool is_inline); + clang::StorageClass storage, bool is_inline, + std::optional<std::string> asm_label); CompilerType CreateFunctionType(const CompilerType &result_type, @@ -1001,7 +1002,7 @@ class TypeSystemClang : public TypeSystem { clang::CXXMethodDecl *AddMethodToCXXRecordType( lldb::opaque_compiler_type_t type, llvm::StringRef name, - const char *mangled_name, const CompilerType &method_type, + std::optional<std::string> mangled_name, const CompilerType &method_type, lldb::AccessType access, bool is_virtual, bool is_static, bool is_inline, bool is_explicit, bool is_attr_used, bool is_artificial); @@ -1098,6 +1099,12 @@ class TypeSystemClang : public TypeSystem { lldb::opaque_compiler_type_t type, Stream &s, lldb::DescriptionLevel level = lldb::eDescriptionLevelFull) override; + llvm::Expected<llvm::SmallVector<llvm::StringRef, 3>> + splitFunctionCallLabel(llvm::StringRef label) const override; + + llvm::Expected<FunctionCallLabel> + makeFunctionCallLabel(llvm::StringRef label) const override; + static void DumpTypeName(const CompilerType &type); static clang::EnumDecl *GetAsEnumDecl(const CompilerType &type); diff --git a/lldb/unittests/Symbol/TestTypeSystemClang.cpp b/lldb/unittests/Symbol/TestTypeSystemClang.cpp index d555d27bef958..c0428a62f7b3d 100644 --- a/lldb/unittests/Symbol/TestTypeSystemClang.cpp +++ b/lldb/unittests/Symbol/TestTypeSystemClang.cpp @@ -869,7 +869,7 @@ TEST_F(TestTypeSystemClang, TestFunctionTemplateConstruction) { CompilerType clang_type = m_ast->CreateFunctionType(int_type, {}, false, 0U); FunctionDecl *func = m_ast->CreateFunctionDeclaration( TU, OptionalClangModuleID(), "foo", clang_type, StorageClass::SC_None, - false); + false, std::nullopt); TypeSystemClang::TemplateParameterInfos empty_params; // Create the actual function template. @@ -900,7 +900,7 @@ TEST_F(TestTypeSystemClang, TestFunctionTemplateInRecordConstruction) { // 2. It is mirroring the behavior of DWARFASTParserClang::ParseSubroutine. FunctionDecl *func = m_ast->CreateFunctionDeclaration( TU, OptionalClangModuleID(), "foo", clang_type, StorageClass::SC_None, - false); + false, std::nullopt); TypeSystemClang::TemplateParameterInfos empty_params; // Create the actual function template. @@ -938,7 +938,7 @@ TEST_F(TestTypeSystemClang, TestDeletingImplicitCopyCstrDueToMoveCStr) { bool is_attr_used = false; bool is_artificial = false; m_ast->AddMethodToCXXRecordType( - t.GetOpaqueQualType(), class_name, nullptr, function_type, + t.GetOpaqueQualType(), class_name, std::nullopt, function_type, lldb::AccessType::eAccessPublic, is_virtual, is_static, is_inline, is_explicit, is_attr_used, is_artificial); @@ -975,7 +975,7 @@ TEST_F(TestTypeSystemClang, TestNotDeletingUserCopyCstrDueToMoveCStr) { CompilerType function_type = m_ast->CreateFunctionType( return_type, args, /*variadic=*/false, /*quals*/ 0U); m_ast->AddMethodToCXXRecordType( - t.GetOpaqueQualType(), class_name, nullptr, function_type, + t.GetOpaqueQualType(), class_name, std::nullopt, function_type, lldb::AccessType::eAccessPublic, is_virtual, is_static, is_inline, is_explicit, is_attr_used, is_artificial); } @@ -987,7 +987,7 @@ TEST_F(TestTypeSystemClang, TestNotDeletingUserCopyCstrDueToMoveCStr) { m_ast->CreateFunctionType(return_type, args, /*variadic=*/false, /*quals*/ 0U); m_ast->AddMethodToCXXRecordType( - t.GetOpaqueQualType(), class_name, nullptr, function_type, + t.GetOpaqueQualType(), class_name, std::nullopt, function_type, lldb::AccessType::eAccessPublic, is_virtual, is_static, is_inline, is_explicit, is_attr_used, is_artificial); } @@ -1098,7 +1098,7 @@ TEST_F(TestTypeSystemClang, AddMethodToCXXRecordType_ParmVarDecls) { m_ast->CreateFunctionType(return_type, param_types, /*variadic=*/false, /*quals*/ 0U); m_ast->AddMethodToCXXRecordType( - t.GetOpaqueQualType(), "myFunc", nullptr, function_type, + t.GetOpaqueQualType(), "myFunc", std::nullopt, function_type, lldb::AccessType::eAccessPublic, is_virtual, is_static, is_inline, is_explicit, is_attr_used, is_artificial); >From 326d7865190eb132aed85615766b9ebcc2a1c81c Mon Sep 17 00:00:00 2001 From: Michael Buch <michaelbuc...@gmail.com> Date: Mon, 21 Jul 2025 12:15:24 +0100 Subject: [PATCH 2/5] fixup! add quotes --- lldb/source/Expression/IRExecutionUnit.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/source/Expression/IRExecutionUnit.cpp b/lldb/source/Expression/IRExecutionUnit.cpp index a9ea244889cce..40635d0829168 100644 --- a/lldb/source/Expression/IRExecutionUnit.cpp +++ b/lldb/source/Expression/IRExecutionUnit.cpp @@ -972,7 +972,7 @@ lldb::addr_t IRExecutionUnit::FindSymbol(lldb_private::ConstString name, return *addr_or_err; } else { LLDB_LOG_ERROR(GetLog(LLDBLog::Expressions), addr_or_err.takeError(), - "Failed to resolve function call label {1}: {0}", + "Failed to resolve function call label '{1}': {0}", name.GetStringRef()); return LLDB_INVALID_ADDRESS; } >From 4e242f81ad8a93406f71d1aada29c9d253bc8806 Mon Sep 17 00:00:00 2001 From: Michael Buch <michaelbuc...@gmail.com> Date: Mon, 21 Jul 2025 10:02:50 +0100 Subject: [PATCH 3/5] Structors init --- clang/lib/AST/Mangle.cpp | 12 +++ libcxxabi/src/demangle/ItaniumDemangle.h | 2 + lldb/include/lldb/Expression/Expression.h | 5 + lldb/include/lldb/Symbol/SymbolFile.h | 6 +- lldb/include/lldb/Symbol/TypeSystem.h | 2 +- lldb/source/Expression/IRExecutionUnit.cpp | 9 +- .../SymbolFile/DWARF/DWARFASTParserClang.cpp | 32 ++++++- .../SymbolFile/DWARF/SymbolFileDWARF.cpp | 58 +++++++++--- .../SymbolFile/DWARF/SymbolFileDWARF.h | 6 +- .../TypeSystem/Clang/TypeSystemClang.cpp | 92 +++++++++++++++++-- .../TypeSystem/Clang/TypeSystemClang.h | 12 ++- llvm/include/llvm/Demangle/Demangle.h | 3 + llvm/include/llvm/Demangle/ItaniumDemangle.h | 2 + llvm/lib/Demangle/ItaniumDemangle.cpp | 10 +- 14 files changed, 217 insertions(+), 34 deletions(-) diff --git a/clang/lib/AST/Mangle.cpp b/clang/lib/AST/Mangle.cpp index 9652fdbc4e125..d0b9f1435e732 100644 --- a/clang/lib/AST/Mangle.cpp +++ b/clang/lib/AST/Mangle.cpp @@ -152,6 +152,13 @@ bool MangleContext::shouldMangleDeclName(const NamedDecl *D) { return shouldMangleCXXName(D); } +static void tryEmitDebugStructorVariant(GlobalDecl GD, raw_ostream &Out) { + if (llvm::isa<clang::CXXConstructorDecl>(GD.getDecl())) + Out << "C" << GD.getCtorType(); + else if (llvm::isa<clang::CXXDestructorDecl>(GD.getDecl())) + Out << "D" << GD.getDtorType(); +} + void MangleContext::mangleName(GlobalDecl GD, raw_ostream &Out) { const ASTContext &ASTContext = getASTContext(); const NamedDecl *D = cast<NamedDecl>(GD.getDecl()); @@ -165,6 +172,11 @@ void MangleContext::mangleName(GlobalDecl GD, raw_ostream &Out) { // do not add a "\01" prefix. if (!ALA->getIsLiteralLabel() || ALA->getLabel().starts_with("llvm.")) { Out << ALA->getLabel(); + + // TODO: ideally should have separate attribute for this? + if (ASTContext.getLangOpts().DebuggerSupport) + tryEmitDebugStructorVariant(GD, Out); + return; } diff --git a/libcxxabi/src/demangle/ItaniumDemangle.h b/libcxxabi/src/demangle/ItaniumDemangle.h index b306b2013445c..aae56fe5f1df2 100644 --- a/libcxxabi/src/demangle/ItaniumDemangle.h +++ b/libcxxabi/src/demangle/ItaniumDemangle.h @@ -1766,6 +1766,8 @@ class CtorDtorName final : public Node { template<typename Fn> void match(Fn F) const { F(Basename, IsDtor, Variant); } + int getVariant() const { return Variant; } + void printLeft(OutputBuffer &OB) const override { if (IsDtor) OB += "~"; diff --git a/lldb/include/lldb/Expression/Expression.h b/lldb/include/lldb/Expression/Expression.h index f32878c9bf876..fe7a6fa0dc527 100644 --- a/lldb/include/lldb/Expression/Expression.h +++ b/lldb/include/lldb/Expression/Expression.h @@ -112,6 +112,11 @@ struct FunctionCallLabel { /// Mostly for debuggability. lldb::user_id_t m_die_id; + + virtual ~FunctionCallLabel() = default; + virtual std::optional<uint8_t> getItaniumStructorVariant() const { + return std::nullopt; + } }; /// LLDB attaches this prefix to mangled names of functions that it get called diff --git a/lldb/include/lldb/Symbol/SymbolFile.h b/lldb/include/lldb/Symbol/SymbolFile.h index 6aca276fc85b6..8fd1d5490b147 100644 --- a/lldb/include/lldb/Symbol/SymbolFile.h +++ b/lldb/include/lldb/Symbol/SymbolFile.h @@ -337,8 +337,10 @@ class SymbolFile : public PluginInterface { /// /// \param[in] lookup_name The UID of the function DIE to resolve. /// - virtual llvm::Error FindAndResolveFunction(SymbolContextList &sc_list, - llvm::StringRef lookup_name) { + virtual llvm::Error + FindAndResolveFunction(SymbolContextList &sc_list, + llvm::StringRef lookup_name, lldb::user_id_t spec, + std::optional<uint8_t> structor_variant) { return llvm::createStringError("Not implemented"); } diff --git a/lldb/include/lldb/Symbol/TypeSystem.h b/lldb/include/lldb/Symbol/TypeSystem.h index 742c09251ea2f..cd887cd003579 100644 --- a/lldb/include/lldb/Symbol/TypeSystem.h +++ b/lldb/include/lldb/Symbol/TypeSystem.h @@ -559,7 +559,7 @@ class TypeSystem : public PluginInterface, } // Decodes the function label into a \c FunctionCallLabel. - virtual llvm::Expected<FunctionCallLabel> + virtual llvm::Expected<std::unique_ptr<FunctionCallLabel>> makeFunctionCallLabel(llvm::StringRef label) const { return llvm::createStringError("Not implemented."); } diff --git a/lldb/source/Expression/IRExecutionUnit.cpp b/lldb/source/Expression/IRExecutionUnit.cpp index 40635d0829168..7ae6962c78819 100644 --- a/lldb/source/Expression/IRExecutionUnit.cpp +++ b/lldb/source/Expression/IRExecutionUnit.cpp @@ -801,13 +801,13 @@ ResolveFunctionCallLabel(llvm::StringRef name, auto ts_sp = *ts_or_err; auto label_or_err = ts_sp->makeFunctionCallLabel(name); - if (!label_or_err) + if (!label_or_err || !*label_or_err) return llvm::joinErrors( llvm::createStringError("failed to create FunctionCallLabel from: %s", name.data()), label_or_err.takeError()); - const auto &label = *label_or_err; + const auto &label = **label_or_err; Module *module = Module::GetAllocatedModuleWithUID(label.m_module_id); @@ -821,8 +821,9 @@ ResolveFunctionCallLabel(llvm::StringRef name, llvm::formatv("no SymbolFile found on module {0:x}.", module)); SymbolContextList sc_list; - if (auto err = - symbol_file->FindAndResolveFunction(sc_list, label.m_lookup_name)) + if (auto err = symbol_file->FindAndResolveFunction( + sc_list, label.m_lookup_name, label.m_die_id, + label.getItaniumStructorVariant())) return llvm::joinErrors( llvm::createStringError("failed to resolve function by UID"), std::move(err)); diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp index 6e9598c13ed5c..f031fcad8a786 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -250,8 +250,36 @@ static unsigned GetCXXMethodCVQuals(const DWARFDIE &subprogram, return cv_quals; } +static const char *GetMangledOrStructorName(const DWARFDIE &die) { + const char *name = die.GetMangledName(/*substitute_name_allowed*/ false); + if (name) + return name; + + name = die.GetName(); + if (!name) + return nullptr; + + DWARFDIE parent = die.GetParent(); + if (!parent.IsStructUnionOrClass()) + return nullptr; + + const char *parent_name = parent.GetName(); + if (!parent_name) + return nullptr; + + // Constructor. + if (::strcmp(parent_name, name) == 0) + return name; + + // Destructor. + if (name[0] == '~' && ::strcmp(parent_name, name + 1)) + return name; + + return nullptr; +} + static std::optional<std::string> MakeLLDBFuncAsmLabel(const DWARFDIE &die) { - char const *name = die.GetMangledName(/*substitute_name_allowed*/ false); + const char *name = GetMangledOrStructorName(die); if (!name) return std::nullopt; @@ -267,7 +295,7 @@ static std::optional<std::string> MakeLLDBFuncAsmLabel(const DWARFDIE &die) { if (die_id == LLDB_INVALID_UID) return std::nullopt; - return llvm::formatv("{0}:{1}:{2:x}:{3:x}", FunctionCallLabelPrefix, name, + return llvm::formatv("{0}:{1}:{2:x}:{3:x}:", FunctionCallLabelPrefix, name, module_id, die_id) .str(); } diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp index b6960bfe38fde..91c32d2bc3170 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -76,6 +76,7 @@ #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h" +#include "llvm/Demangle/Demangle.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/FormatVariadic.h" @@ -2471,32 +2472,63 @@ bool SymbolFileDWARF::ResolveFunction(const DWARFDIE &orig_die, return false; } -llvm::Error -SymbolFileDWARF::FindAndResolveFunction(SymbolContextList &sc_list, - llvm::StringRef lookup_name) { +llvm::Error SymbolFileDWARF::FindAndResolveFunction( + SymbolContextList &sc_list, llvm::StringRef lookup_name, + lldb::user_id_t die_id, std::optional<uint8_t> structor_variant) { std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); - - DWARFDIE die; - Module::LookupInfo info(ConstString(lookup_name), lldb::eFunctionNameTypeFull, + DWARFDIE die = GetDIE(die_id); + if (!die.IsValid()) + return llvm::createStringError("invalid DIE for UID: %" PRIu64, die_id); + + // eFunctionNameTypeFull for mangled name lookup. + // eFunctionNameTypeMethod is required for structor lookups (since we look + // those up by DW_AT_name). + Module::LookupInfo info(ConstString(lookup_name), + lldb::eFunctionNameTypeFull | + lldb::eFunctionNameTypeMethod, lldb::eLanguageTypeUnknown); m_index->GetFunctions(info, *this, {}, [&](DWARFDIE entry) { if (entry.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0)) return true; - // We don't check whether the specification DIE for this function - // corresponds to the declaration DIE because the declaration might be in - // a type-unit but the definition in the compile-unit (and it's - // specifcation would point to the declaration in the compile-unit). We - // rely on the mangled name within the module to be enough to find us the - // unique definition. + // TODO: this specification check doesn't work if declaration DIE was in a + // type-unit (we should only encode DIEs from .debug_info). + auto spec = entry.GetAttributeValueAsReferenceDIE(DW_AT_specification); + if (!spec) + return true; + + if (spec != die) + return true; + + // We're not picking a specific structor variant DIE, so we're done here. + if (!structor_variant) { + die = entry; + return false; + } + + const char *mangled = + entry.GetMangledName(/*substitute_name_allowed=*/false); + if (!mangled) + return true; + + llvm::ItaniumPartialDemangler D; + if (D.partialDemangle(mangled)) + return true; + + if (D.getCtorOrDtorVariant() != structor_variant) + return true; + die = entry; + return false; }); if (!die.IsValid()) return llvm::createStringError( - llvm::formatv("failed to find definition DIE for '{0}'", lookup_name)); + llvm::formatv("failed to find definition DIE for [lookup_name={0}] " + "[DIE ID={1:x}] [structor={2}]", + lookup_name, die_id, structor_variant.value_or(-1))); if (!ResolveFunction(die, false, sc_list)) return llvm::createStringError("failed to resolve function DIE"); diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h index ee966366d3f2b..4473630f8ce1a 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h @@ -434,8 +434,10 @@ class SymbolFileDWARF : public SymbolFileCommon { DIEArray MergeBlockAbstractParameters(const DWARFDIE &block_die, DIEArray &&variable_dies); - llvm::Error FindAndResolveFunction(SymbolContextList &sc_list, - llvm::StringRef lookup_name) override; + llvm::Error + FindAndResolveFunction(SymbolContextList &sc_list, + llvm::StringRef lookup_name, lldb::user_id_t spec, + std::optional<uint8_t> structor_variant) override; // Given a die_offset, figure out the symbol context representing that die. bool ResolveFunction(const DWARFDIE &die, bool include_inlines, diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index 03bdc555c0613..85ad7b3c40f8b 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -10,6 +10,7 @@ #include "clang/AST/DeclBase.h" #include "clang/AST/ExprCXX.h" +#include "clang/Basic/ABI.h" #include "clang/Frontend/ASTConsumers.h" #include "llvm/Support/Casting.h" #include "llvm/Support/FormatAdapters.h" @@ -9783,8 +9784,40 @@ void TypeSystemClang::LogCreation() const { &getASTContext(), getDisplayName()); } +static llvm::Expected< + std::variant<std::monostate, clang::CXXCtorType, clang::CXXDtorType>> +ParseStructorType(llvm::StringRef label) { + if (label.empty()) + return std::monostate{}; + + bool is_ctor = label.consume_front("C"); + if (!is_ctor && label.starts_with("D")) + return llvm::createStringError("invalid structor type prefix in %s", + label.data()); + + uint8_t type; + if (label.consumeInteger(0, type)) + return llvm::createStringError("failed to parse structor type value %s", + label.data()); + + if (is_ctor) { + if (type > clang::CXXCtorType::Ctor_DefaultClosure) + return llvm::createStringError("C++ constructor type %d out of range", + type); + + return static_cast<CXXCtorType>(type); + } + + if (type > clang::CXXDtorType::Dtor_Deleting) + return llvm::createStringError("C++ destructor type %d out of range", type); + + return static_cast<CXXDtorType>(type); +} + +// clang-format off // Expected format is: -// $__lldb_func:<mangled name>:<module id>:<definition/declaration DIE id> +// $__lldb_func:<mangled name>:<module id>:<definition/declaration DIE id>:[<structor variant>] +// clang-format on llvm::Expected<llvm::SmallVector<llvm::StringRef, 3>> TypeSystemClang::splitFunctionCallLabel(llvm::StringRef label) const { if (!consumeFunctionCallLabelPrefix(label)) @@ -9794,17 +9827,17 @@ TypeSystemClang::splitFunctionCallLabel(llvm::StringRef label) const { return llvm::createStringError( "incorrect format: expected ':' as the first character."); - llvm::SmallVector<llvm::StringRef, 3> components; + llvm::SmallVector<llvm::StringRef, 4> components; label.split(components, ":"); - if (components.size() != 3) + if (components.size() != 4) return llvm::createStringError( "incorrect format: too many label subcomponents."); return components; } -llvm::Expected<FunctionCallLabel> +llvm::Expected<std::unique_ptr<FunctionCallLabel>> TypeSystemClang::makeFunctionCallLabel(llvm::StringRef label) const { auto components_or_err = splitFunctionCallLabel(label); if (!components_or_err) @@ -9816,6 +9849,7 @@ TypeSystemClang::makeFunctionCallLabel(llvm::StringRef label) const { llvm::StringRef module_label = components[1]; llvm::StringRef die_label = components[2]; + llvm::StringRef structor_variant = components[3]; lldb::user_id_t module_id = 0; if (module_label.consumeInteger(0, module_id)) @@ -9827,6 +9861,52 @@ TypeSystemClang::makeFunctionCallLabel(llvm::StringRef label) const { return llvm::createStringError( llvm::formatv("failed to parse DIE ID from '{0}'.", components[2])); - return FunctionCallLabel{/*.m_lookup_name =*/components[0], - /*.m_module_id =*/module_id, /*.m_die_id =*/die_id}; + auto structor_type_or_err = ParseStructorType(structor_variant); + if (!structor_type_or_err) + return llvm::joinErrors( + llvm::createStringError(llvm::formatv( + "failed to parse structor kind from '{0}'.", components[2])), + structor_type_or_err.takeError()); + + auto ret = std::make_unique<ClangFunctionCallLabel>(); + ret->m_lookup_name = components[0]; + ret->m_module_id = module_id; + ret->m_die_id = die_id; + ret->m_structor_type = std::move(*structor_type_or_err); + + return ret; +} + +std::optional<uint8_t> +TypeSystemClang::ClangFunctionCallLabel::getItaniumStructorVariant() const { + if (m_structor_type.index() == 0) + return std::nullopt; + + if (auto *ctor = std::get_if<clang::CXXCtorType>(&m_structor_type)) { + switch (*ctor) { + case clang::CXXCtorType::Ctor_Complete: + return 1; + case clang::CXXCtorType::Ctor_Base: + return 2; + case clang::CXXCtorType::Ctor_Comdat: + case clang::CXXCtorType::Ctor_CopyingClosure: + case clang::CXXCtorType::Ctor_DefaultClosure: + // No Itanium equivalent + return std::nullopt; + } + } else if (auto *dtor = std::get_if<clang::CXXDtorType>(&m_structor_type)) { + switch (*dtor) { + case clang::CXXDtorType::Dtor_Deleting: + return 0; + case clang::CXXDtorType::Dtor_Complete: + return 1; + case clang::CXXDtorType::Dtor_Base: + return 2; + case clang::CXXDtorType::Dtor_Comdat: + // No Itanium equivalent + return std::nullopt; + } + } + + return std::nullopt; } diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h index 726a0eea9382a..6ba17e4bb1cda 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h @@ -18,6 +18,7 @@ #include <set> #include <string> #include <utility> +#include <variant> #include <vector> #include "clang/AST/ASTContext.h" @@ -26,6 +27,7 @@ #include "clang/AST/Decl.h" #include "clang/AST/TemplateBase.h" #include "clang/AST/Type.h" +#include "clang/Basic/ABI.h" #include "clang/Basic/TargetInfo.h" #include "llvm/ADT/APSInt.h" #include "llvm/ADT/SmallVector.h" @@ -1102,7 +1104,15 @@ class TypeSystemClang : public TypeSystem { llvm::Expected<llvm::SmallVector<llvm::StringRef, 3>> splitFunctionCallLabel(llvm::StringRef label) const override; - llvm::Expected<FunctionCallLabel> + struct ClangFunctionCallLabel : public FunctionCallLabel { + /// Structor kind according to the Itanium ABI. + std::variant<std::monostate, clang::CXXCtorType, clang::CXXDtorType> + m_structor_type; + + std::optional<uint8_t> getItaniumStructorVariant() const override; + }; + + llvm::Expected<std::unique_ptr<FunctionCallLabel>> makeFunctionCallLabel(llvm::StringRef label) const override; static void DumpTypeName(const CompilerType &type); diff --git a/llvm/include/llvm/Demangle/Demangle.h b/llvm/include/llvm/Demangle/Demangle.h index 21e7457b6336f..568d57e37823e 100644 --- a/llvm/include/llvm/Demangle/Demangle.h +++ b/llvm/include/llvm/Demangle/Demangle.h @@ -122,6 +122,9 @@ struct ItaniumPartialDemangler { /// If this symbol describes a constructor or destructor. bool isCtorOrDtor() const; + /// If this symbol describes a constructor or destructor. + std::optional<int> getCtorOrDtorVariant() const; + /// If this symbol describes a function. bool isFunction() const; diff --git a/llvm/include/llvm/Demangle/ItaniumDemangle.h b/llvm/include/llvm/Demangle/ItaniumDemangle.h index 5533652736dc8..ec1a527ca7e7c 100644 --- a/llvm/include/llvm/Demangle/ItaniumDemangle.h +++ b/llvm/include/llvm/Demangle/ItaniumDemangle.h @@ -1766,6 +1766,8 @@ class CtorDtorName final : public Node { template<typename Fn> void match(Fn F) const { F(Basename, IsDtor, Variant); } + int getVariant() const { return Variant; } + void printLeft(OutputBuffer &OB) const override { if (IsDtor) OB += "~"; diff --git a/llvm/lib/Demangle/ItaniumDemangle.cpp b/llvm/lib/Demangle/ItaniumDemangle.cpp index 1009cc91ca12a..a5d7a5576fccf 100644 --- a/llvm/lib/Demangle/ItaniumDemangle.cpp +++ b/llvm/lib/Demangle/ItaniumDemangle.cpp @@ -560,13 +560,17 @@ bool ItaniumPartialDemangler::hasFunctionQualifiers() const { } bool ItaniumPartialDemangler::isCtorOrDtor() const { + return getCtorOrDtorVariant().has_value(); +} + +std::optional<int> ItaniumPartialDemangler::getCtorOrDtorVariant() const { const Node *N = static_cast<const Node *>(RootNode); while (N) { switch (N->getKind()) { default: - return false; + return std::nullopt; case Node::KCtorDtorName: - return true; + return static_cast<const CtorDtorName *>(N)->getVariant(); case Node::KAbiTagAttr: N = static_cast<const AbiTagAttr *>(N)->Base; @@ -588,7 +592,7 @@ bool ItaniumPartialDemangler::isCtorOrDtor() const { break; } } - return false; + return std::nullopt; } bool ItaniumPartialDemangler::isFunction() const { >From b716bcb505d11d862dda09d02ae15bd1078314f0 Mon Sep 17 00:00:00 2001 From: Michael Buch <michaelbuc...@gmail.com> Date: Mon, 21 Jul 2025 16:29:08 +0100 Subject: [PATCH 4/5] fixup! fix constructor aliasing --- .../SymbolFile/DWARF/SymbolFileDWARF.cpp | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp index 91c32d2bc3170..51f0c94320766 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -2488,6 +2488,7 @@ llvm::Error SymbolFileDWARF::FindAndResolveFunction( lldb::eFunctionNameTypeMethod, lldb::eLanguageTypeUnknown); + llvm::DenseMap<uint8_t, DWARFDIE> variant_to_die; m_index->GetFunctions(info, *this, {}, [&](DWARFDIE entry) { if (entry.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0)) return true; @@ -2516,14 +2517,30 @@ llvm::Error SymbolFileDWARF::FindAndResolveFunction( if (D.partialDemangle(mangled)) return true; - if (D.getCtorOrDtorVariant() != structor_variant) + auto maybe_variant = D.getCtorOrDtorVariant(); + if (!maybe_variant) return true; - die = entry; + assert(!variant_to_die.contains(*maybe_variant)); - return false; + variant_to_die[*maybe_variant] = entry; + + return true; }); + if (structor_variant) { + if (auto it = variant_to_die.find(*structor_variant); + it != variant_to_die.end()) { + die = it->getSecond(); + } + // TODO: only do this for constructors. + else if (structor_variant == 1) + // On Linux the C1 constructor variant is often an alias + // to C2 (and C1 is never actually emitted into the object file). + if (auto it = variant_to_die.find(2); it != variant_to_die.end()) + die = it->getSecond(); + } + if (!die.IsValid()) return llvm::createStringError( llvm::formatv("failed to find definition DIE for [lookup_name={0}] " >From 03fb460d124f4d60349fbc08bc77dc7a6150853c Mon Sep 17 00:00:00 2001 From: Michael Buch <michaelbuc...@gmail.com> Date: Mon, 21 Jul 2025 16:41:23 +0100 Subject: [PATCH 5/5] fixup! fix custom linkage name test --- .../Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp index 51f0c94320766..62330be36aeeb 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -2483,10 +2483,14 @@ llvm::Error SymbolFileDWARF::FindAndResolveFunction( // eFunctionNameTypeFull for mangled name lookup. // eFunctionNameTypeMethod is required for structor lookups (since we look // those up by DW_AT_name). - Module::LookupInfo info(ConstString(lookup_name), - lldb::eFunctionNameTypeFull | - lldb::eFunctionNameTypeMethod, + Module::LookupInfo info(ConstString(lookup_name), {}, lldb::eLanguageTypeUnknown); + // FIXME: Set this separately because for TestExprsBug35310 Module::LookupInfo + // constructor would unset the eFunctionNameTypeFull! Because of + // GetFunctionNameInfo (which for custom linkage names doesn't return + // eFunctionNameTypeFull) + info.SetNameTypeMask(lldb::eFunctionNameTypeFull | + lldb::eFunctionNameTypeMethod); llvm::DenseMap<uint8_t, DWARFDIE> variant_to_die; m_index->GetFunctions(info, *this, {}, [&](DWARFDIE entry) { @@ -2541,7 +2545,7 @@ llvm::Error SymbolFileDWARF::FindAndResolveFunction( die = it->getSecond(); } - if (!die.IsValid()) + if (!die.IsValid() || die.GetAttributeValueAsUnsigned(DW_AT_declaration, 0)) return llvm::createStringError( llvm::formatv("failed to find definition DIE for [lookup_name={0}] " "[DIE ID={1:x}] [structor={2}]", _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits