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,112 @@ return count; } +/// Given a mangled function `mangled`, replace all the primitive function type +/// arguments of `mangled` with type `replace`. +static ConstString SubsPrimitiveParmItanium(const char *mangled, + const char *search, + const char *replace) { + Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE); + assert(mangled && search && replace); + + const size_t search_len = ::strlen(search); + const size_t replace_len = ::strlen(replace); + const size_t mangled_len = ::strlen(mangled); + const size_t max_len = + mangled_len + llvm::StringRef(mangled).count(search) * replace_len + 1; + + // Make a temporary buffer to fix up the mangled parameter types + // and copy the original there + std::unique_ptr output_buf(new char[max_len]); + ::memset(output_buf.get(), '\0', max_len); + ::strncpy(output_buf.get(), mangled, max_len - 1); + + size_t avail_sz = max_len - 1; + ptrdiff_t replaced_offset = 0; + + auto swap_parms_hook = [&](const char *s) { + if (!s || !*s) + return; + + // Check whether we've found a substitutee + if (::strncmp(s, search, search_len) == 0) { + // account for the case where a replacement is of a different length + // to the original + replaced_offset += replace_len - search_len; + + ptrdiff_t replace_idx = (s - mangled) + replaced_offset; + for (size_t i = 0; avail_sz && replace[i]; ++i, ++s) { + output_buf[replace_idx++] = replace[i]; + avail_sz--; + } + + // append the remains of the mangled string + for (char *p = output_buf.get() + replace_idx; *s && avail_sz--;) + *p++ = *s++; + } + }; + + // FastDemangle will call our hook for each instance of a primitive type, + // allowing us to perform substitution + const char *const demangled = + FastDemangle(mangled, mangled_len, swap_parms_hook); + + if (log) + log->Printf("substituted mangling for %s:{%s} %s:{%s}\n", mangled, + demangled, output_buf.get(), FastDemangle(output_buf.get())); + + return ::strcmp(output_buf.get(), mangled) ? ConstString(output_buf.get()) + : ConstString(); +} + +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.GetCString(), "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.GetCString(), "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.GetCString(), "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 +1042,7 @@ return candidate; } }; - + return std::unique_ptr(new CPlusPlusTypeScavenger()); }