https://github.com/Michael137 updated https://github.com/llvm/llvm-project/pull/148877
>From f640beff5da2edb9d928e3a0b1ce4128194c055b 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/3] [lldb][Expression] Encode Module and DIE UIDs into function AsmLabels --- lldb/include/lldb/Core/Module.h | 4 +- lldb/include/lldb/Expression/Expression.h | 35 ++++++++++ lldb/include/lldb/Symbol/SymbolFile.h | 14 ++++ lldb/source/Core/Module.cpp | 19 +++++- lldb/source/Expression/Expression.cpp | 65 +++++++++++++++++++ lldb/source/Expression/IRExecutionUnit.cpp | 63 ++++++++++++++++++ .../Clang/ClangExpressionDeclMap.cpp | 2 +- .../SymbolFile/DWARF/DWARFASTParserClang.cpp | 44 ++++++++----- .../SymbolFile/DWARF/SymbolFileDWARF.cpp | 45 +++++++++++++ .../SymbolFile/DWARF/SymbolFileDWARF.h | 3 + .../SymbolFile/NativePDB/PdbAstBuilder.cpp | 6 +- .../NativePDB/UdtRecordCompleter.cpp | 5 +- .../Plugins/SymbolFile/PDB/PDBASTParser.cpp | 7 +- .../TypeSystem/Clang/TypeSystemClang.cpp | 33 ++++++++-- .../TypeSystem/Clang/TypeSystemClang.h | 5 +- lldb/unittests/Symbol/TestTypeSystemClang.cpp | 12 ++-- 16 files changed, 318 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..5cf21eb505dac 100644 --- a/lldb/include/lldb/Expression/Expression.h +++ b/lldb/include/lldb/Expression/Expression.h @@ -96,6 +96,41 @@ 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 { + /// Mostly for debuggability. + llvm::StringRef m_pubname; + + lldb::user_id_t m_die_id; + lldb::user_id_t m_module_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); + +/// 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. +llvm::Expected<llvm::SmallVector<llvm::StringRef, 3>> +splitFunctionCallLabel(llvm::StringRef label); + +// Decodes the function label into a \c FunctionCallLabel. +llvm::Expected<FunctionCallLabel> makeFunctionCallLabel(llvm::StringRef label); + } // 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..70dc20664df20 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 uniquely identified by \c uid within + /// this SymbolFile. + /// + /// \param[in,out] sc_list The resolved functions will be appended to this + /// list. + /// + /// \param[in] uid The UID of the function DIE to resolve. + /// + virtual llvm::Error ResolveFunctionUID(SymbolContextList &sc_list, + lldb::user_id_t uid) { + 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/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..216dcce8c678c 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,64 @@ 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); +} + +// Expected format is: +// $__lldb_func:<mangled name>:<module id>:<definition/declaration DIE id> +llvm::Expected<llvm::SmallVector<llvm::StringRef, 3>> +lldb_private::splitFunctionCallLabel(llvm::StringRef label) { + 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> +lldb_private::makeFunctionCallLabel(llvm::StringRef label) { + 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 '%s'.", components[1].data())); + + lldb::user_id_t die_id; + if (die_label.consumeInteger(/*Radix=*/0, die_id)) + return llvm::createStringError("failed to parse DIE ID from '%s'.", + components[2].data()); + + return FunctionCallLabel{ + .m_pubname = components[0], .m_die_id = die_id, .m_module_id = module_id}; +} diff --git a/lldb/source/Expression/IRExecutionUnit.cpp b/lldb/source/Expression/IRExecutionUnit.cpp index 6f812b91a8b1d..b79edc5dfadc1 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,52 @@ 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; + + auto label_or_err = 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->ResolveFunctionUID(sc_list, label.m_die_id)) + return llvm::joinErrors( + llvm::createStringError("failed to resolve function by UID"), + std::move(err)); + + if (!sc.target_sp) + return llvm::createStringError("target not available."); + + 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 +955,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..6d9f8294f741a 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,29 @@ static unsigned GetCXXMethodCVQuals(const DWARFDIE &subprogram, return cv_quals; } +static std::optional<std::string> MakeLLDBFuncAsmLabel(const DWARFDIE &die) { + std::optional<std::string> label; + char const *name = die.GetPubname(); + if (name) + label.emplace(name); + + 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 ? name : "", module_id, die_id) + .str(); +} + TypeSP DWARFASTParserClang::ParseTypeFromClangModule(const SymbolContext &sc, const DWARFDIE &die, Log *log) { @@ -1231,7 +1255,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 +1408,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 +1418,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 +1430,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..b781cff767e4f 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -2471,6 +2471,51 @@ bool SymbolFileDWARF::ResolveFunction(const DWARFDIE &orig_die, return false; } +llvm::Error SymbolFileDWARF::ResolveFunctionUID(SymbolContextList &sc_list, + lldb::user_id_t uid) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + auto die = GetDIE(uid); + + if (!die.IsValid()) + return llvm::createStringError("invalid input DIE"); + + // Look up by DW_AT_linkage_name if we can. Otherwise we can use the + // DW_AT_name. + char const *name = die.GetPubname(); + if (!name) + return llvm::createStringError("input DIE has no name"); + + // If we're trying to resolve a function declaration DIE, find the definition. + if (die.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0)) { + Module::LookupInfo info(ConstString(name), lldb::eFunctionNameTypeMethod, + lldb::eLanguageTypeUnknown); + + m_index->GetFunctions(info, *this, {}, [&](DWARFDIE entry) { + if (entry.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0)) + return true; + + if (auto spec = entry.GetAttributeValueAsReferenceDIE( + llvm::dwarf::DW_AT_specification); + spec == die) { + die = entry; + return false; + } + + return true; + }); + } + + 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"); + + assert(sc_list.GetSize() == 1); + + 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..377d412825a6a 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h @@ -154,6 +154,9 @@ class SymbolFileDWARF : public SymbolFileCommon { std::vector<CompilerContext> GetCompilerContextForUID(lldb::user_id_t uid) override; + llvm::Error ResolveFunctionUID(SymbolContextList &sc_list, + lldb::user_id_t uid) override; + void ParseDeclsForContext(CompilerDeclContext decl_ctx) override; uint32_t ResolveSymbolContext(const Address &so_addr, 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..448a64c4c4306 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)) { diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h index 63dee9dceded3..dcf77f9ec1cb8 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); 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 f20f6285ff9c5a47306680da34dc9cf7b14d1042 Mon Sep 17 00:00:00 2001 From: Michael Buch <michaelbuc...@gmail.com> Date: Sat, 19 Jul 2025 12:36:18 +0100 Subject: [PATCH 2/3] fixup! ManualDWARFIndex: add mangled names into methods index --- .../SymbolFile/DWARF/ManualDWARFIndex.cpp | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp b/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp index 523820874752a..58364bd096935 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp @@ -299,6 +299,10 @@ void ManualDWARFIndex::IndexUnitImpl(DWARFUnit &unit, case DW_TAG_inlined_subroutine: case DW_TAG_subprogram: if (has_address) { + // If we have a mangled name, then the DW_AT_name attribute is + // usually the method name without the class or any parameters + bool is_method = DWARFDIE(&unit, &die).IsMethod(); + if (name) { bool is_objc_method = false; if (cu_language == eLanguageTypeObjC || @@ -326,10 +330,6 @@ void ManualDWARFIndex::IndexUnitImpl(DWARFUnit &unit, ref); } } - // If we have a mangled name, then the DW_AT_name attribute is - // usually the method name without the class or any parameters - bool is_method = DWARFDIE(&unit, &die).IsMethod(); - if (is_method) set.function_methods.Insert(ConstString(name), ref); else @@ -338,7 +338,11 @@ void ManualDWARFIndex::IndexUnitImpl(DWARFUnit &unit, if (!is_method && !mangled_cstr && !is_objc_method) set.function_fullnames.Insert(ConstString(name), ref); } - if (mangled_cstr) { + + auto insert_mangled = [&](NameToDIE &index_set) { + if (!mangled_cstr) + return; + // Make sure our mangled name isn't the same string table entry as // our name. If it starts with '_', then it is ok, else compare the // string to make sure it isn't the same and we don't end up with @@ -346,9 +350,14 @@ void ManualDWARFIndex::IndexUnitImpl(DWARFUnit &unit, if (name && name != mangled_cstr && ((mangled_cstr[0] == '_') || (::strcmp(name, mangled_cstr) != 0))) { - set.function_fullnames.Insert(ConstString(mangled_cstr), ref); + index_set.Insert(ConstString(mangled_cstr), ref); } - } + }; + + if (is_method) + insert_mangled(set.function_methods); + + insert_mangled(set.function_fullnames); } break; >From dd5782e26241da554315b9b0ebe2ff8cd9200dbb Mon Sep 17 00:00:00 2001 From: Michael Buch <michaelbuc...@gmail.com> Date: Sat, 19 Jul 2025 22:54:27 +0100 Subject: [PATCH 3/3] fixup! don't check specification DIE --- .../SymbolFile/DWARF/DWARFASTParserClang.cpp | 11 +++++----- .../SymbolFile/DWARF/SymbolFileDWARF.cpp | 21 +++++++++---------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp index 6d9f8294f741a..6e9598c13ed5c 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -251,10 +251,9 @@ static unsigned GetCXXMethodCVQuals(const DWARFDIE &subprogram, } static std::optional<std::string> MakeLLDBFuncAsmLabel(const DWARFDIE &die) { - std::optional<std::string> label; - char const *name = die.GetPubname(); - if (name) - label.emplace(name); + char const *name = die.GetMangledName(/*substitute_name_allowed*/ false); + if (!name) + return std::nullopt; auto module_sp = die.GetModule(); if (!module_sp) @@ -268,8 +267,8 @@ 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 ? name : "", module_id, die_id) + 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 b781cff767e4f..cfc75db691a72 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -2479,9 +2479,8 @@ llvm::Error SymbolFileDWARF::ResolveFunctionUID(SymbolContextList &sc_list, if (!die.IsValid()) return llvm::createStringError("invalid input DIE"); - // Look up by DW_AT_linkage_name if we can. Otherwise we can use the - // DW_AT_name. - char const *name = die.GetPubname(); + // Rely on DW_AT_linkage_name to find us the definition DIE. + char const *name = die.GetMangledName(/*substitute_name_allowed=*/false); if (!name) return llvm::createStringError("input DIE has no name"); @@ -2494,14 +2493,14 @@ llvm::Error SymbolFileDWARF::ResolveFunctionUID(SymbolContextList &sc_list, if (entry.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0)) return true; - if (auto spec = entry.GetAttributeValueAsReferenceDIE( - llvm::dwarf::DW_AT_specification); - spec == die) { - die = entry; - return false; - } - - 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; }); } _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits