Michael137 created this revision. Michael137 added a reviewer: aprantl. Herald added a project: All. Michael137 added a reviewer: jingham. Michael137 edited the summary of this revision. Michael137 updated this revision to Diff 450751. Michael137 added a comment. Michael137 added a reviewer: labath. Michael137 published this revision for review. Herald added a project: LLDB. Herald added a subscriber: lldb-commits.
- Rebase - Fix API tests: - Weaken pre-condition for non-function mangled names - Allow non-C++ mangled names When resolving symbols during IR execution, lldb makes a last effort attempt to resolve external symbols from object files by approximate name matching. It currently uses `CPlusPlusNameParser` to parse the demangled function name and arguments for the unresolved symbol and its candidates. However, this hand-rolled C++ parser doesn’t support ABI tags which, depending on the demangler, get demangled into `[abi:tag]`. This lack of parsing support causes lldb to never consider a candidate mangled function name that has ABI tags. The issue reproduces by calling an ABI-tagged template function from the expression evaluator. This is particularly problematic with the recent addition of ABI tags to numerous libcxx APIs. This patch deals with the above symbol resolution issue by using the Itanium mangle tree API to compare mangled function symbols and no longer relying on parsing demangled names via `CPlusPlusNameParser`. Since the MSVC mangler doesn't expose the mangle tree we continue using the old matching logic when the candidate symbol has the MSVC mangling scheme. **Testing** - Added API tests Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D131335 Files: lldb/packages/Python/lldbsuite/test/make/Makefile.rules lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h lldb/test/API/lang/cpp/abi_tag_lookup/Makefile lldb/test/API/lang/cpp/abi_tag_lookup/TestAbiTagLookup.py lldb/test/API/lang/cpp/abi_tag_lookup/main.cpp
Index: lldb/test/API/lang/cpp/abi_tag_lookup/main.cpp =================================================================== --- /dev/null +++ lldb/test/API/lang/cpp/abi_tag_lookup/main.cpp @@ -0,0 +1,69 @@ +#include <cstdio> + +namespace A { +template <typename T> bool operator<(const T &, const T &) { return true; } + +template <typename T> +[[gnu::abi_tag("Two", "Tags")]] bool operator>(const T &, const T &) { + return true; +} + +template <typename T> +[[gnu::abi_tag("OneTag")]] bool operator==(const T &, const T &) { + return true; +} + +[[gnu::abi_tag("Foo")]] int withAbiTagInNS(const int &, const int &) { + return 1; +} + +template <typename T> +[[gnu::abi_tag("Bar")]] int withAbiTagInNS(const T &, const T &) { + return 2; +} + +struct B {}; +} // namespace A + +template <typename T> +[[gnu::abi_tag("Baz")]] int withAbiTag(const T &, const T &) { + return 3; +} + +struct Simple { + int mem; +}; + +struct [[gnu::abi_tag("Qux")]] Tagged { + int mem; + + int const &Value() const { return mem; } +}; + +template <typename T> struct [[gnu::abi_tag("Quux", "Quuux")]] TaggedTemplate { + T mem; + + T const &Value() const { return mem; } +}; + +// clang-format off +inline namespace [[gnu::abi_tag("Inline", "NS")]] v1 { +template <typename T> int withImplicitTag(T const &t) { return t.mem; } +} // namespace +// clang-format on + +int main() { + A::B b1; + A::B b2; + Tagged t{.mem = 4}; + TaggedTemplate<int> tt{.mem = 5}; + + int result = (b1 < b2) + (b1 > b2) + (b1 == b2) + withAbiTag(b1, b2) + + A::withAbiTagInNS(1.0, 2.0) + withAbiTagInNS(b1, b2) + + A::withAbiTagInNS(1, 2) + withImplicitTag(Tagged{.mem = 6}) + + withImplicitTag(Simple{.mem = 6}) + t.Value() + tt.Value(); + + std::puts("Break here"); + + return result; +} Index: lldb/test/API/lang/cpp/abi_tag_lookup/TestAbiTagLookup.py =================================================================== --- /dev/null +++ lldb/test/API/lang/cpp/abi_tag_lookup/TestAbiTagLookup.py @@ -0,0 +1,55 @@ +""" +Test that we can call functions and use types +annotated (and thus mangled) with ABI tags. +""" + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class AbiTagLookupTestCase(TestBase): + + @skipIfWindows + @expectedFailureAll(debug_info=["dwarf", "gmodules", "dwo"]) + def test_abi_tag_lookup(self): + self.build() + lldbutil.run_to_source_breakpoint(self, 'Break here', + lldb.SBFileSpec("main.cpp", False)) + + # Qualified/unqualified lookup to templates in namespace + self.expect_expr("operator<(b1, b2)", result_type="bool", result_value="true") + self.expect_expr("A::operator<(b1, b2)", result_type="bool", result_value="true") + self.expect_expr("b1 < b2", result_type="bool", result_value="true") + + # Qualified/unqualified lookup to templates with ABI tags in namespace + self.expect_expr("operator>(b1, b2)", result_type="bool", result_value="true") + self.expect_expr("A::operator>(b1, b2)", result_type="bool", result_value="true") + self.expect_expr("b1 > b2", result_type="bool", result_value="true") + + # Call non-operator templates with ABI tags + self.expect_expr("A::withAbiTagInNS(1, 1)", result_type="int", result_value="1") + + self.expect_expr("A::withAbiTagInNS(1.0, 1.0)", result_type="int", result_value="2") + self.expect_expr("withAbiTagInNS(b1, b2)", result_type="int", result_value="2") + self.expect_expr("A::withAbiTagInNS(b1, b2)", result_type="int", result_value="2") + + self.expect_expr("withAbiTag(b1, b2)", result_type="int", result_value="3") + + # Structures with ABI tags + self.expect_expr("t.Value()", result_type="const int", result_value="4") + self.expect_expr("tt.Value()", result_type="const int", result_value="5") + + self.expect_expr("Tagged{.mem = 6}", result_type="Tagged", + result_children=[ValueCheck(name="mem", value="6")]) + + # Inline namespaces with ABI tags + self.expect_expr("v1::withImplicitTag(Simple{.mem = 6})", result_type="int", result_value="6") + self.expect_expr("withImplicitTag(Simple{.mem = 6})", result_type="int", result_value="6") + + # FIXME: this currently fails because CPlusPlusLanguage::FindBestAlternateFunctionMangledName + # requires at least the parameters of the symbols it compares to match. However, + # the IR we generate doesn't include ABI tags on the parameter. To fix this we need + # to add ABI tag attributes to the decls that DWARFASTParserClang generates. + self.expect("expr v1::withImplicitTag(Tagged{.mem = 6})", error=True, substrs=["error: Couldn't lookup symbols:"]) + self.expect("expr withImplicitTag(Tagged{.mem = 6})", error=True, substrs=["error: Couldn't lookup symbols:"]) Index: lldb/test/API/lang/cpp/abi_tag_lookup/Makefile =================================================================== --- /dev/null +++ lldb/test/API/lang/cpp/abi_tag_lookup/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules Index: lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h =================================================================== --- lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h +++ lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h @@ -143,6 +143,16 @@ // PluginInterface protocol llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + +private: + void + CollectAlternateFunctionNamesFromMangled(std::vector<ConstString> &results, + ConstString name, + const SymbolContext &sc) const; + + void CollectAlternateFunctionNamesItanium(std::vector<ConstString> &results, + ConstString name, + const SymbolContext &sc) const; }; } // namespace lldb_private Index: lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp =================================================================== --- lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -18,6 +18,8 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Demangle/ItaniumDemangle.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/ErrorHandling.h" #include "lldb/Core/Mangled.h" #include "lldb/Core/Module.h" @@ -514,6 +516,22 @@ return alternates; } +char const *printNode(const llvm::itanium_demangle::Node *RootNode) { + llvm::itanium_demangle::OutputBuffer OB; + if (!initializeOutputBuffer(nullptr, nullptr, OB, 128)) + return nullptr; + RootNode->print(OB); + OB += '\0'; + return OB.getBuffer(); +} + +char const *getFunctionName(llvm::itanium_demangle::Node const *root) { + auto *Name = + static_cast<llvm::itanium_demangle::FunctionEncoding const *>(root) + ->getName(); + return printNode(Name); +} + static ConstString FindBestAlternateFunctionMangledName( const Mangled mangled, const SymbolContext &sym_ctx) { ConstString demangled = mangled.GetDemangledName(); @@ -564,6 +582,71 @@ return ConstString(); } +ConstString +FindBestAlternateFunctionMangledName(llvm::itanium_demangle::Node const *node, + const SymbolContext &sym_ctx) { + if (!sym_ctx.module_sp) + return ConstString(); + + lldb_private::SymbolFile *sym_file = sym_ctx.module_sp->GetSymbolFile(); + if (!sym_file) + return ConstString(); + + using namespace llvm::itanium_demangle; + assert(node != nullptr); + + if (node->getKind() != Node::KFunctionEncoding) + return ConstString(); + + std::vector<ConstString> alternates; + sym_file->GetMangledNamesForFunction(getFunctionName(node), alternates); + + auto const *encoding = static_cast<FunctionEncoding const *>(node); + Qualifiers quals = encoding->getCVQuals(); + FunctionRefQual refQual = encoding->getRefQual(); + NodeArray params = encoding->getParams(); + auto const *attrs = encoding->getAttrs(); + + std::vector<ConstString> param_matches; + for (size_t i = 0; i < alternates.size(); i++) { + ConstString alternate_mangled_name = alternates[i]; + char const *alternate = alternate_mangled_name.AsCString(); + + ManglingParser<NodeAllocator> alternate_parser( + alternate, alternate + std::strlen(alternate)); + if (auto const *alt_node = alternate_parser.parse()) { + assert(alt_node->getKind() == Node::KFunctionEncoding); + auto const *alt_encoding = + static_cast<FunctionEncoding const *>(alt_node); + Qualifiers alt_quals = alt_encoding->getCVQuals(); + FunctionRefQual alt_refQual = alt_encoding->getRefQual(); + NodeArray alt_params = alt_encoding->getParams(); + auto const *alt_attrs = alt_encoding->getAttrs(); + + if (params == alt_params) { + if (quals == alt_quals && refQual == alt_refQual) { + if ((attrs && alt_attrs && attrs->equals(alt_attrs)) || + (!attrs && !alt_attrs)) { + // Perfect match. Return it. + return alternate_mangled_name; + } + } + + // Not a perfect match, but count matching parameters + // as an approximate match. + param_matches.push_back(alternate_mangled_name); + } + } + } + + // No perfect match. Return one of the approximate + // matches as a best match + if (param_matches.size()) + return param_matches[0]; + + // No matches. + return ConstString(); +} static void LoadLibCxxFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { if (!cpp_category_sp) @@ -1411,27 +1494,78 @@ return file_path.contains("/usr/include/c++/"); } +void CPlusPlusLanguage::CollectAlternateFunctionNamesFromMangled( + std::vector<ConstString> &results, ConstString name, + const SymbolContext &sc) const { + Mangled mangled(name); + if (SymbolNameFitsToLanguage(mangled)) { + if (ConstString best_alternate = + FindBestAlternateFunctionMangledName(mangled, sc)) { + results.push_back(best_alternate); + } + } + + std::vector<ConstString> alternates = + GenerateAlternateFunctionManglings(name); + results.insert(results.end(), alternates.begin(), alternates.end()); + + // As a last-ditch fallback, try the base name for C++ names. It's + // terrible, but the DWARF doesn't always encode "extern C" correctly. + ConstString basename = GetDemangledFunctionNameWithoutArguments(mangled); + results.push_back(basename); +} + +void CPlusPlusLanguage::CollectAlternateFunctionNamesItanium( + std::vector<ConstString> &results, ConstString name, + const SymbolContext &sc) const { + if (!name) + return; + + char const *mangled = name.GetCString(); + using namespace llvm::itanium_demangle; + ManglingParser<NodeAllocator> parser(mangled, mangled + std::strlen(mangled)); + auto const *node = parser.parse(); + + if (node && SymbolNameFitsToLanguage(mangled)) { + if (ConstString best_alternate = + FindBestAlternateFunctionMangledName(node, sc)) { + results.push_back(best_alternate); + } + } + + std::vector<ConstString> alternates = + GenerateAlternateFunctionManglings(name); + results.insert(results.end(), alternates.begin(), alternates.end()); + + // As a last-ditch fallback, try the base name for C++ names. It's + // terrible, but the DWARF doesn't always encode "extern C" correctly. + if (node) { + ConstString basename(getFunctionName(node)); + results.push_back(basename); + } +} + void CPlusPlusLanguage::CollectAlternateFunctionNames( std::vector<ConstString> &results, const std::vector<ConstString> &mangled_names, const SymbolContext &sc) const { for (const ConstString &name : mangled_names) { - Mangled mangled(name); - if (SymbolNameFitsToLanguage(mangled)) { - if (ConstString best_alternate = - FindBestAlternateFunctionMangledName(mangled, sc)) { - results.push_back(best_alternate); + auto scheme = Mangled::GetManglingScheme(name.GetStringRef()); + switch (scheme) { + case Mangled::ManglingScheme::eManglingSchemeItanium: + CollectAlternateFunctionNamesItanium(results, name, sc); + break; + case Mangled::ManglingScheme::eManglingSchemeNone: + if (name.GetStringRef().startswith("__Z")) { + CollectAlternateFunctionNamesItanium(results, name, sc); + break; } + LLVM_FALLTHROUGH; + case Mangled::ManglingScheme::eManglingSchemeMSVC: + case Mangled::ManglingScheme::eManglingSchemeRustV0: + case Mangled::ManglingScheme::eManglingSchemeD: + CollectAlternateFunctionNamesFromMangled(results, name, sc); + break; } - - std::vector<ConstString> alternates = - GenerateAlternateFunctionManglings(name); - results.insert(results.end(), alternates.begin(), alternates.end()); - - // As a last-ditch fallback, try the base name for C++ names. It's - // terrible, but the DWARF doesn't always encode "extern C" correctly. - ConstString basename = - GetDemangledFunctionNameWithoutArguments(mangled); - results.push_back(basename); } } Index: lldb/packages/Python/lldbsuite/test/make/Makefile.rules =================================================================== --- lldb/packages/Python/lldbsuite/test/make/Makefile.rules +++ lldb/packages/Python/lldbsuite/test/make/Makefile.rules @@ -387,8 +387,7 @@ endif ifeq (1,$(USE_LIBCPP)) - # TODO: Teach LLDB to handle ABI tags in libc++ namespaces. - CXXFLAGS += -DLLDB_USING_LIBCPP -D_LIBCPP_NO_ABI_TAG + CXXFLAGS += -DLLDB_USING_LIBCPP ifeq "$(OS)" "Android" # Nothing to do, this is already handled in # Android.rules.
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits