Index: include/lldb/Core/FastDemangle.h =================================================================== --- include/lldb/Core/FastDemangle.h +++ include/lldb/Core/FastDemangle.h @@ -10,11 +10,17 @@ #ifndef liblldb_FastDemangle_h_ #define liblldb_FastDemangle_h_ +#include + +#include + namespace lldb_private { char *FastDemangle(const char *mangled_name); -char *FastDemangle(const char *mangled_name, long mangled_name_length); +char * +FastDemangle(const char *mangled_name, size_t mangled_name_length, + std::function primitive_type_hook = nullptr); } #endif Index: packages/Python/lldbsuite/test/expression_command/call-overloaded-c-fuction/Makefile =================================================================== --- /dev/null +++ packages/Python/lldbsuite/test/expression_command/call-overloaded-c-fuction/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules Index: packages/Python/lldbsuite/test/expression_command/call-overloaded-c-fuction/TestCallOverloadedCFunction.py =================================================================== --- /dev/null +++ packages/Python/lldbsuite/test/expression_command/call-overloaded-c-fuction/TestCallOverloadedCFunction.py @@ -0,0 +1,60 @@ +""" +Test calling user defined C functions using expression evaluation. +This test checks that expression evaluation works correctly for +functions defined with __attribute__((overloadable)). + +Ticket: https://llvm.org/bugs/show_bug.cgi?id=26694 +""" + +from __future__ import print_function + +import lldb + +from lldbsuite.test.decorators import ( + skipIf, + expectedFailureAll +) +from lldbsuite.test.lldbtest import ( + TestBase, + CURRENT_EXECUTABLE_SET, + RUN_SUCCEEDED, + line_number, + no_match, +) +from lldbsuite.test import lldbutil + + +class TestExprCallOverloadedCFunction(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + super(TestExprCallOverloadedCFunction, self).setUp() + # Find the line number to break for main.c. + self.line = line_number('main.c', '// break here') + + @skipIf(compiler=no_match('clang')) # `overloadable` is only supported by clang" + @expectedFailureAll( + oslist=["windows"], + bugnumber="llvm.org/pr24489: Name lookup broken on Windows") + def test(self): + """Test calling overloaded C functions.""" + self.build() + + # Set breakpoint in main and run exe + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + lldbutil.run_break_set_by_file_and_line( + self, + "main.c", + self.line, + num_expected_locations=-1, + loc_exact=True + ) + + self.runCmd("run", RUN_SUCCEEDED) + + # Test floating point type overload. + self.expect("expr get_arg_type(0.5f)", substrs=["$0 = 'F'"]) + + # Test integer type overload + self.expect("expr get_arg_type(8)", substrs=["$1 = 'I'"]) Index: packages/Python/lldbsuite/test/expression_command/call-overloaded-c-fuction/main.c =================================================================== --- /dev/null +++ packages/Python/lldbsuite/test/expression_command/call-overloaded-c-fuction/main.c @@ -0,0 +1,17 @@ +char __attribute__((overloadable)) get_arg_type(float x) +{ + return 'F'; // 'F' for float +} + +char __attribute__((overloadable)) get_arg_type(int x) +{ + return 'I'; // 'I' for int +} + +int main() +{ + char float_result = get_arg_type(0.1f); + char int_result = get_arg_type(2); + + return 0; // break here +} Index: source/Core/FastDemangle.cpp =================================================================== --- source/Core/FastDemangle.cpp +++ source/Core/FastDemangle.cpp @@ -11,6 +11,9 @@ #include #include +#include + +#include "lldb/Core/FastDemangle.h" #include "lldb/lldb-private.h" //#define DEBUG_FAILURES 1 @@ -119,7 +122,9 @@ /// @param storage_size Number of bytes of space available scratch memory /// referenced by storage_ptr - SymbolDemangler(void *storage_ptr, int storage_size) { + SymbolDemangler(void *storage_ptr, size_t storage_size, + std::function builtins_hook = nullptr) + : m_builtins_hook(builtins_hook) { // Use up to 1/8th of the provided space for rewrite ranges m_rewrite_ranges_size = (storage_size >> 3) / sizeof(BufferRange); m_rewrite_ranges = (BufferRange *)storage_ptr; @@ -509,6 +514,9 @@ // ::= u # vendor extended type const char *TryParseBuiltinType() { + if (m_builtins_hook) + m_builtins_hook(m_read_ptr); + switch (*m_read_ptr++) { case 'v': return "void"; @@ -1150,6 +1158,23 @@ return RewriteTemplateArg(count + 1); } + // + // Dv _ + bool TryParseVectorType() { + const int dimension = TryParseNumber(); + if (dimension == -1) + return false; + + if (*m_read_ptr++ != '_') + return false; + + char vec_dimens[32] = {'\0'}; + ::snprintf(vec_dimens, sizeof vec_dimens - 1, " __vector(%d)", dimension); + ParseType(); + Write(vec_dimens); + return true; + } + // ::= // ::= // ::= @@ -1191,9 +1216,12 @@ if (!ParseType()) return false; break; + case 'v': + if (!TryParseVectorType()) + return false; + break; case 'T': case 't': - case 'v': default: #ifdef DEBUG_FAILURES printf("*** Unsupported type: %.3s\n", failed_type); @@ -2346,6 +2374,7 @@ char *m_write_ptr; int m_next_template_arg_index; int m_next_substitute_index; + std::function m_builtins_hook; }; } // Anonymous namespace @@ -2358,9 +2387,10 @@ return demangler.GetDemangledCopy(mangled_name); } -char *FastDemangle(const char *mangled_name, long mangled_name_length) { +char *FastDemangle(const char *mangled_name, size_t mangled_name_length, + std::function builtins_hook) { char buffer[16384]; - SymbolDemangler demangler(buffer, sizeof(buffer)); + SymbolDemangler demangler(buffer, sizeof(buffer), builtins_hook); return demangler.GetDemangledCopy(mangled_name, mangled_name_length); } } // lldb_private namespace Index: source/Expression/IRExecutionUnit.cpp =================================================================== --- source/Expression/IRExecutionUnit.cpp +++ source/Expression/IRExecutionUnit.cpp @@ -776,22 +776,9 @@ } } - // Maybe we're looking for a const symbol but the debug info told us it was - // const... - if (!strncmp(name.GetCString(), "_ZN", 3) && - strncmp(name.GetCString(), "_ZNK", 4)) { - std::string fixed_scratch("_ZNK"); - fixed_scratch.append(name.GetCString() + 3); - CPP_specs.push_back(ConstString(fixed_scratch.c_str())); - } - - // Maybe we're looking for a static symbol but we thought it was global... - if (!strncmp(name.GetCString(), "_Z", 2) && - strncmp(name.GetCString(), "_ZL", 3)) { - std::string fixed_scratch("_ZL"); - fixed_scratch.append(name.GetCString() + 2); - CPP_specs.push_back(ConstString(fixed_scratch.c_str())); - } + std::set alternates; + CPlusPlusLanguage::FindAlternateFunctionManglings(name, alternates); + CPP_specs.insert(CPP_specs.end(), alternates.begin(), alternates.end()); } } Index: source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp =================================================================== --- source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp +++ source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp @@ -1978,11 +1978,12 @@ if (function) { Type *function_type = function->GetType(); - const lldb::LanguageType comp_unit_language = - function->GetCompileUnit()->GetLanguage(); - const bool extern_c = Language::LanguageIsC(comp_unit_language) || - (Language::LanguageIsObjC(comp_unit_language) && - !Language::LanguageIsCPlusPlus(comp_unit_language)); + const auto lang = function->GetCompileUnit()->GetLanguage(); + const auto name = function->GetMangled().GetMangledName().AsCString(); + const bool extern_c = (Language::LanguageIsC(lang) && + !CPlusPlusLanguage::IsCPPMangledName(name)) || + (Language::LanguageIsObjC(lang) && + !Language::LanguageIsCPlusPlus(lang)); if (!extern_c) { TypeSystem *type_system = function->GetDeclContext().GetTypeSystem(); Index: source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h =================================================================== --- source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h +++ source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h @@ -12,6 +12,7 @@ // C Includes // C++ Includes +#include #include // Other libraries and framework includes @@ -93,7 +94,6 @@ } std::unique_ptr GetTypeScavenger() override; - lldb::TypeCategoryImplSP GetFormatters() override; HardcodedFormatters::HardcodedSummaryFinder GetHardcodedSummaries() override; @@ -142,6 +142,12 @@ static uint32_t FindEquivalentNames(ConstString type_name, std::vector &equivalents); + // Given a mangled function name, calculates some alternative manglings since + // the compiler mangling may not line up with the symbol we are expecting + static uint32_t + FindAlternateFunctionManglings(const ConstString mangled, + std::set &candidates); + //------------------------------------------------------------------ // PluginInterface protocol //------------------------------------------------------------------ Index: source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp =================================================================== --- source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -10,17 +10,22 @@ #include "CPlusPlusLanguage.h" // C Includes -// C++ Includes #include #include + +// C++ Includes #include +#include #include +#include // Other libraries and framework includes #include "llvm/ADT/StringRef.h" // Project includes #include "lldb/Core/ConstString.h" +#include "lldb/Core/FastDemangle.h" +#include "lldb/Core/Log.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/RegularExpression.h" #include "lldb/Core/UniqueCStringMap.h" @@ -440,6 +445,101 @@ return count; } +/// Given a mangled function `mangled`, replace all the primitive function type +/// arguments of `search` with type `replace`. +static ConstString SubsPrimitiveParmItanium(llvm::StringRef mangled, + llvm::StringRef search, + llvm::StringRef replace) { + Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE); + + const size_t max_len = + mangled.size() + mangled.count(search) * replace.size() + 1; + + // Make a temporary buffer to fix up the mangled parameter types and copy the + // original there + std::string output_buf; + output_buf.reserve(max_len); + output_buf.insert(0, mangled.str()); + ptrdiff_t replaced_offset = 0; + + auto swap_parms_hook = [&](const char *parsee) { + if (!parsee || !*parsee) + return; + + // Check whether we've found a substitutee + llvm::StringRef s(parsee); + if (s.startswith(search)) { + // account for the case where a replacement is of a different length to + // the original + replaced_offset += replace.size() - search.size(); + + ptrdiff_t replace_idx = (mangled.size() - s.size()) + replaced_offset; + output_buf.erase(replace_idx, search.size()); + output_buf.insert(replace_idx, replace.str()); + } + }; + + // FastDemangle will call our hook for each instance of a primitive type, + // allowing us to perform substitution + const char *const demangled = + FastDemangle(mangled.str().c_str(), mangled.size(), swap_parms_hook); + + if (log) + log->Printf("substituted mangling for %s:{%s} %s:{%s}\n", + mangled.str().c_str(), demangled, output_buf.c_str(), + FastDemangle(output_buf.c_str())); + + return output_buf == mangled ? ConstString() : ConstString(output_buf); +} + +uint32_t CPlusPlusLanguage::FindAlternateFunctionManglings( + const ConstString mangled_name, std::set &alternates) { + const auto start_size = alternates.size(); + /// Get a basic set of alternative manglings for the given symbol `name`, by + /// making a few basic possible substitutions on basic types, storage duration + /// and `const`ness for the given symbol. The output parameter `alternates` + /// is filled with a best-guess, non-exhaustive set of different manglings + /// for the given name. + + // Maybe we're looking for a const symbol but the debug info told us it was + // non-const... + if (!strncmp(mangled_name.GetCString(), "_ZN", 3) && + strncmp(mangled_name.GetCString(), "_ZNK", 4)) { + std::string fixed_scratch("_ZNK"); + fixed_scratch.append(mangled_name.GetCString() + 3); + alternates.insert(ConstString(fixed_scratch)); + } + + // Maybe we're looking for a static symbol but we thought it was global... + if (!strncmp(mangled_name.GetCString(), "_Z", 2) && + strncmp(mangled_name.GetCString(), "_ZL", 3)) { + std::string fixed_scratch("_ZL"); + fixed_scratch.append(mangled_name.GetCString() + 2); + alternates.insert(ConstString(fixed_scratch)); + } + + // `char` is implementation defined as either `signed` or `unsigned`. As a + // result a char parameter has 3 possible manglings: 'c'-char, 'a'-signed + // char, 'h'-unsigned char. If we're looking for symbols with a signed char + // parameter, try finding matches which have the general case 'c'. + if (ConstString char_fixup = + SubsPrimitiveParmItanium(mangled_name.GetStringRef(), "a", "c")) + alternates.insert(char_fixup); + + // long long parameter mangling 'x', may actually just be a long 'l' argument + if (ConstString long_fixup = + SubsPrimitiveParmItanium(mangled_name.GetStringRef(), "x", "l")) + alternates.insert(long_fixup); + + // unsigned long long parameter mangling 'y', may actually just be unsigned + // long 'm' argument + if (ConstString ulong_fixup = + SubsPrimitiveParmItanium(mangled_name.GetStringRef(), "y", "m")) + alternates.insert(ulong_fixup); + + return alternates.size() - start_size; +} + static void LoadLibCxxFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { if (!cpp_category_sp) return; @@ -931,7 +1031,7 @@ return candidate; } }; - + return std::unique_ptr(new CPlusPlusTypeScavenger()); }