diff options
Diffstat (limited to 'lldb/source/Plugins/LanguageRuntime/CPlusPlus')
4 files changed, 1170 insertions, 0 deletions
diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp new file mode 100644 index 0000000000000..f38014505a8b7 --- /dev/null +++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp @@ -0,0 +1,353 @@ +//===-- CPPLanguageRuntime.cpp +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include <string.h> + +#include <memory> + +#include "CPPLanguageRuntime.h" + +#include "llvm/ADT/StringRef.h" + +#include "lldb/Symbol/Block.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/Symbol/VariableList.h" + +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/UniqueCStringMap.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Target/ABI.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/SectionLoadList.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/ThreadPlanRunToAddress.h" +#include "lldb/Target/ThreadPlanStepInRange.h" + +using namespace lldb; +using namespace lldb_private; + +static ConstString g_this = ConstString("this"); + +char CPPLanguageRuntime::ID = 0; + +// Destructor +CPPLanguageRuntime::~CPPLanguageRuntime() {} + +CPPLanguageRuntime::CPPLanguageRuntime(Process *process) +    : LanguageRuntime(process) {} + +bool CPPLanguageRuntime::IsWhitelistedRuntimeValue(ConstString name) { +  return name == g_this; +} + +bool CPPLanguageRuntime::GetObjectDescription(Stream &str, +                                              ValueObject &object) { +  // C++ has no generic way to do this. +  return false; +} + +bool CPPLanguageRuntime::GetObjectDescription( +    Stream &str, Value &value, ExecutionContextScope *exe_scope) { +  // C++ has no generic way to do this. +  return false; +} + +CPPLanguageRuntime::LibCppStdFunctionCallableInfo +CPPLanguageRuntime::FindLibCppStdFunctionCallableInfo( +    lldb::ValueObjectSP &valobj_sp) { +  LibCppStdFunctionCallableInfo optional_info; + +  if (!valobj_sp) +    return optional_info; + +  // Member __f_ has type __base*, the contents of which will hold: +  // 1) a vtable entry which may hold type information needed to discover the +  //    lambda being called +  // 2) possibly hold a pointer to the callable object +  // e.g. +  // +  // (lldb) frame var -R  f_display +  // (std::__1::function<void (int)>) f_display = { +  //  __buf_ = { +  //  … +  // } +  //  __f_ = 0x00007ffeefbffa00 +  // } +  // (lldb) memory read -fA 0x00007ffeefbffa00 +  // 0x7ffeefbffa00: ... `vtable for std::__1::__function::__func<void (*) ... +  // 0x7ffeefbffa08: ... `print_num(int) at std_function_cppreference_exam ... +  // +  // We will be handling five cases below, std::function is wrapping: +  // +  // 1) a lambda we know at compile time. We will obtain the name of the lambda +  //    from the first template pameter from __func's vtable. We will look up +  //    the lambda's operator()() and obtain the line table entry. +  // 2) a lambda we know at runtime. A pointer to the lambdas __invoke method +  //    will be stored after the vtable. We will obtain the lambdas name from +  //    this entry and lookup operator()() and obtain the line table entry. +  // 3) a callable object via operator()(). We will obtain the name of the +  //    object from the first template parameter from __func's vtable. We will +  //    look up the objectc operator()() and obtain the line table entry. +  // 4) a member function. A pointer to the function will stored after the +  //    we will obtain the name from this pointer. +  // 5) a free function. A pointer to the function will stored after the vtable +  //    we will obtain the name from this pointer. +  ValueObjectSP member__f_( +      valobj_sp->GetChildMemberWithName(ConstString("__f_"), true)); + +  if (member__f_) { +    ValueObjectSP sub_member__f_( +       member__f_->GetChildMemberWithName(ConstString("__f_"), true)); + +    if (sub_member__f_) +        member__f_ = sub_member__f_; +  } + +  lldb::addr_t member__f_pointer_value = member__f_->GetValueAsUnsigned(0); + +  optional_info.member__f_pointer_value = member__f_pointer_value; + +  ExecutionContext exe_ctx(valobj_sp->GetExecutionContextRef()); +  Process *process = exe_ctx.GetProcessPtr(); + +  if (process == nullptr) +    return optional_info; + +  uint32_t address_size = process->GetAddressByteSize(); +  Status status; + +  // First item pointed to by __f_ should be the pointer to the vtable for +  // a __base object. +  lldb::addr_t vtable_address = +      process->ReadPointerFromMemory(member__f_pointer_value, status); + +  if (status.Fail()) +    return optional_info; + +  lldb::addr_t address_after_vtable = member__f_pointer_value + address_size; +  // As commened above we may not have a function pointer but if we do we will +  // need it. +  lldb::addr_t possible_function_address = +      process->ReadPointerFromMemory(address_after_vtable, status); + +  if (status.Fail()) +    return optional_info; + +  Target &target = process->GetTarget(); + +  if (target.GetSectionLoadList().IsEmpty()) +    return optional_info; + +  Address vtable_addr_resolved; +  SymbolContext sc; +  Symbol *symbol; + +  if (!target.GetSectionLoadList().ResolveLoadAddress(vtable_address, +                                                      vtable_addr_resolved)) +    return optional_info; + +  target.GetImages().ResolveSymbolContextForAddress( +      vtable_addr_resolved, eSymbolContextEverything, sc); +  symbol = sc.symbol; + +  if (symbol == nullptr) +    return optional_info; + +  llvm::StringRef vtable_name(symbol->GetName().GetCString()); +  bool found_expected_start_string = +      vtable_name.startswith("vtable for std::__1::__function::__func<"); + +  if (!found_expected_start_string) +    return optional_info; + +  // Given case 1 or 3 we have a vtable name, we are want to extract the first +  // template parameter +  // +  //  ... __func<main::$_0, std::__1::allocator<main::$_0> ... +  //             ^^^^^^^^^ +  // +  // We do this by find the first < and , and extracting in between. +  // +  // This covers the case of the lambda known at compile time. +  size_t first_open_angle_bracket = vtable_name.find('<') + 1; +  size_t first_comma = vtable_name.find(','); + +  llvm::StringRef first_template_parameter = +      vtable_name.slice(first_open_angle_bracket, first_comma); + +  Address function_address_resolved; + +  // Setup for cases 2, 4 and 5 we have a pointer to a function after the +  // vtable. We will use a process of elimination to drop through each case +  // and obtain the data we need. +  if (target.GetSectionLoadList().ResolveLoadAddress( +          possible_function_address, function_address_resolved)) { +    target.GetImages().ResolveSymbolContextForAddress( +        function_address_resolved, eSymbolContextEverything, sc); +    symbol = sc.symbol; +  } + +  auto get_name = [&first_template_parameter, &symbol]() { +    // Given case 1: +    // +    //    main::$_0 +    // +    // we want to append ::operator()() +    if (first_template_parameter.contains("$_")) +      return llvm::Regex::escape(first_template_parameter.str()) + +             R"(::operator\(\)\(.*\))"; + +    if (symbol != nullptr && +        symbol->GetName().GetStringRef().contains("__invoke")) { + +      llvm::StringRef symbol_name = symbol->GetName().GetStringRef(); +      size_t pos2 = symbol_name.find_last_of(':'); + +      // Given case 2: +      // +      //    main::$_1::__invoke(...) +      // +      // We want to slice off __invoke(...) and append operator()() +      std::string lambda_operator = +          llvm::Regex::escape(symbol_name.slice(0, pos2 + 1).str()) + +          R"(operator\(\)\(.*\))"; + +      return lambda_operator; +    } + +    // Case 3 +    return first_template_parameter.str() + R"(::operator\(\)\(.*\))"; +    ; +  }; + +  std::string func_to_match = get_name(); + +  SymbolContextList scl; + +  target.GetImages().FindSymbolsMatchingRegExAndType( +      RegularExpression{R"(^)" + func_to_match}, eSymbolTypeAny, scl); + +  // Case 1,2 or 3 +  if (scl.GetSize() >= 1) { +    SymbolContext sc2 = scl[0]; + +    AddressRange range; +    sc2.GetAddressRange(eSymbolContextEverything, 0, false, range); + +    Address address = range.GetBaseAddress(); + +    Address addr; +    if (target.ResolveLoadAddress(address.GetCallableLoadAddress(&target), +                                  addr)) { +      LineEntry line_entry; +      addr.CalculateSymbolContextLineEntry(line_entry); + +      if (first_template_parameter.contains("$_") || +          (symbol != nullptr && +           symbol->GetName().GetStringRef().contains("__invoke"))) { +        // Case 1 and 2 +        optional_info.callable_case = LibCppStdFunctionCallableCase::Lambda; +      } else { +        // Case 3 +        optional_info.callable_case = +            LibCppStdFunctionCallableCase::CallableObject; +      } + +      optional_info.callable_symbol = *symbol; +      optional_info.callable_line_entry = line_entry; +      optional_info.callable_address = addr; +      return optional_info; +    } +  } + +  // Case 4 or 5 +  if (symbol && !symbol->GetName().GetStringRef().startswith("vtable for")) { +    optional_info.callable_case = +        LibCppStdFunctionCallableCase::FreeOrMemberFunction; +    optional_info.callable_address = function_address_resolved; +    optional_info.callable_symbol = *symbol; + +    return optional_info; +  } + +  return optional_info; +} + +lldb::ThreadPlanSP +CPPLanguageRuntime::GetStepThroughTrampolinePlan(Thread &thread, +                                                 bool stop_others) { +  ThreadPlanSP ret_plan_sp; + +  lldb::addr_t curr_pc = thread.GetRegisterContext()->GetPC(); + +  TargetSP target_sp(thread.CalculateTarget()); + +  if (target_sp->GetSectionLoadList().IsEmpty()) +    return ret_plan_sp; + +  Address pc_addr_resolved; +  SymbolContext sc; +  Symbol *symbol; + +  if (!target_sp->GetSectionLoadList().ResolveLoadAddress(curr_pc, +                                                          pc_addr_resolved)) +    return ret_plan_sp; + +  target_sp->GetImages().ResolveSymbolContextForAddress( +      pc_addr_resolved, eSymbolContextEverything, sc); +  symbol = sc.symbol; + +  if (symbol == nullptr) +    return ret_plan_sp; + +  llvm::StringRef function_name(symbol->GetName().GetCString()); + +  // Handling the case where we are attempting to step into std::function. +  // The behavior will be that we will attempt to obtain the wrapped +  // callable via FindLibCppStdFunctionCallableInfo() and if we find it we +  // will return a ThreadPlanRunToAddress to the callable. Therefore we will +  // step into the wrapped callable. +  // +  bool found_expected_start_string = +      function_name.startswith("std::__1::function<"); + +  if (!found_expected_start_string) +    return ret_plan_sp; + +  AddressRange range_of_curr_func; +  sc.GetAddressRange(eSymbolContextEverything, 0, false, range_of_curr_func); + +  StackFrameSP frame = thread.GetStackFrameAtIndex(0); + +  if (frame) { +    ValueObjectSP value_sp = frame->FindVariable(g_this); + +    CPPLanguageRuntime::LibCppStdFunctionCallableInfo callable_info = +        FindLibCppStdFunctionCallableInfo(value_sp); + +    if (callable_info.callable_case != LibCppStdFunctionCallableCase::Invalid && +        value_sp->GetValueIsValid()) { +      // We found the std::function wrapped callable and we have its address. +      // We now create a ThreadPlan to run to the callable. +      ret_plan_sp = std::make_shared<ThreadPlanRunToAddress>( +          thread, callable_info.callable_address, stop_others); +      return ret_plan_sp; +    } else { +      // We are in std::function but we could not obtain the callable. +      // We create a ThreadPlan to keep stepping through using the address range +      // of the current function. +      ret_plan_sp = std::make_shared<ThreadPlanStepInRange>( +          thread, range_of_curr_func, sc, eOnlyThisThread, eLazyBoolYes, +          eLazyBoolYes); +      return ret_plan_sp; +    } +  } + +  return ret_plan_sp; +} diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h new file mode 100644 index 0000000000000..28526361efc4a --- /dev/null +++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h @@ -0,0 +1,90 @@ +//===-- CPPLanguageRuntime.h +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CPPLanguageRuntime_h_ +#define liblldb_CPPLanguageRuntime_h_ + +#include <vector> +#include "lldb/Core/PluginInterface.h" +#include "lldb/Target/LanguageRuntime.h" +#include "lldb/lldb-private.h" + +namespace lldb_private { + +class CPPLanguageRuntime : public LanguageRuntime { +public: +  enum class LibCppStdFunctionCallableCase { +    Lambda = 0, +    CallableObject, +    FreeOrMemberFunction, +    Invalid +  }; + +  struct LibCppStdFunctionCallableInfo { +    Symbol callable_symbol; +    Address callable_address; +    LineEntry callable_line_entry; +    lldb::addr_t member__f_pointer_value = 0u; +    LibCppStdFunctionCallableCase callable_case = +        LibCppStdFunctionCallableCase::Invalid; +  }; + +  LibCppStdFunctionCallableInfo +  FindLibCppStdFunctionCallableInfo(lldb::ValueObjectSP &valobj_sp); + +  ~CPPLanguageRuntime() override; + +  static char ID; + +  bool isA(const void *ClassID) const override { +    return ClassID == &ID || LanguageRuntime::isA(ClassID); +  } + +  static bool classof(const LanguageRuntime *runtime) { +    return runtime->isA(&ID); +  } + +  lldb::LanguageType GetLanguageType() const override { +    return lldb::eLanguageTypeC_plus_plus; +  } + +  static CPPLanguageRuntime *Get(Process &process) { +    return llvm::cast_or_null<CPPLanguageRuntime>( +        process.GetLanguageRuntime(lldb::eLanguageTypeC_plus_plus)); +  } + +  bool GetObjectDescription(Stream &str, ValueObject &object) override; + +  bool GetObjectDescription(Stream &str, Value &value, +                            ExecutionContextScope *exe_scope) override; + +  /// Obtain a ThreadPlan to get us into C++ constructs such as std::function. +  /// +  /// \param[in] thread +  ///     Curent thrad of execution. +  /// +  /// \param[in] stop_others +  ///     True if other threads should pause during execution. +  /// +  /// \return +  ///      A ThreadPlan Shared pointer +  lldb::ThreadPlanSP GetStepThroughTrampolinePlan(Thread &thread, +                                                  bool stop_others) override; + +  bool IsWhitelistedRuntimeValue(ConstString name) override; +protected: +  // Classes that inherit from CPPLanguageRuntime can see and modify these +  CPPLanguageRuntime(Process *process); + +private: +  DISALLOW_COPY_AND_ASSIGN(CPPLanguageRuntime); +}; + +} // namespace lldb_private + +#endif // liblldb_CPPLanguageRuntime_h_ diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp new file mode 100644 index 0000000000000..02e62a2632866 --- /dev/null +++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp @@ -0,0 +1,610 @@ +//===-- ItaniumABILanguageRuntime.cpp --------------------------------------*- +//C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ItaniumABILanguageRuntime.h" + +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/Mangled.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectMemory.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/Expression/DiagnosticManager.h" +#include "lldb/Expression/FunctionCaller.h" +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/CommandObjectMultiword.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/TypeList.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/SectionLoadList.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Scalar.h" +#include "lldb/Utility/Status.h" + +#include <vector> + +using namespace lldb; +using namespace lldb_private; + +static const char *vtable_demangled_prefix = "vtable for "; + +char ItaniumABILanguageRuntime::ID = 0; + +bool ItaniumABILanguageRuntime::CouldHaveDynamicValue(ValueObject &in_value) { +  const bool check_cxx = true; +  const bool check_objc = false; +  return in_value.GetCompilerType().IsPossibleDynamicType(nullptr, check_cxx, +                                                          check_objc); +} + +TypeAndOrName ItaniumABILanguageRuntime::GetTypeInfoFromVTableAddress( +    ValueObject &in_value, lldb::addr_t original_ptr, +    lldb::addr_t vtable_load_addr) { +  if (m_process && vtable_load_addr != LLDB_INVALID_ADDRESS) { +    // Find the symbol that contains the "vtable_load_addr" address +    Address vtable_addr; +    Target &target = m_process->GetTarget(); +    if (!target.GetSectionLoadList().IsEmpty()) { +      if (target.GetSectionLoadList().ResolveLoadAddress(vtable_load_addr, +                                                         vtable_addr)) { +        // See if we have cached info for this type already +        TypeAndOrName type_info = GetDynamicTypeInfo(vtable_addr); +        if (type_info) +          return type_info; + +        SymbolContext sc; +        target.GetImages().ResolveSymbolContextForAddress( +            vtable_addr, eSymbolContextSymbol, sc); +        Symbol *symbol = sc.symbol; +        if (symbol != nullptr) { +          const char *name = +              symbol->GetMangled() +                  .GetDemangledName(lldb::eLanguageTypeC_plus_plus) +                  .AsCString(); +          if (name && strstr(name, vtable_demangled_prefix) == name) { +            Log *log( +                lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT)); +            LLDB_LOGF(log, +                      "0x%16.16" PRIx64 +                      ": static-type = '%s' has vtable symbol '%s'\n", +                      original_ptr, in_value.GetTypeName().GetCString(), name); +            // We are a C++ class, that's good.  Get the class name and look it +            // up: +            const char *class_name = name + strlen(vtable_demangled_prefix); +            // We know the class name is absolute, so tell FindTypes that by +            // prefixing it with the root namespace: +            std::string lookup_name("::"); +            lookup_name.append(class_name); +             +            type_info.SetName(class_name); +            const bool exact_match = true; +            TypeList class_types; + +            // First look in the module that the vtable symbol came from and +            // look for a single exact match. +            llvm::DenseSet<SymbolFile *> searched_symbol_files; +            if (sc.module_sp) +              sc.module_sp->FindTypes(ConstString(lookup_name), exact_match, 1, +                                      searched_symbol_files, class_types); + +            // If we didn't find a symbol, then move on to the entire module +            // list in the target and get as many unique matches as possible +            if (class_types.Empty()) +              target.GetImages().FindTypes(nullptr, ConstString(lookup_name), +                                           exact_match, UINT32_MAX, +                                           searched_symbol_files, class_types); + +            lldb::TypeSP type_sp; +            if (class_types.Empty()) { +              LLDB_LOGF(log, "0x%16.16" PRIx64 ": is not dynamic\n", +                        original_ptr); +              return TypeAndOrName(); +            } +            if (class_types.GetSize() == 1) { +              type_sp = class_types.GetTypeAtIndex(0); +              if (type_sp) { +                if (ClangASTContext::IsCXXClassType( +                        type_sp->GetForwardCompilerType())) { +                  LLDB_LOGF( +                      log, +                      "0x%16.16" PRIx64 +                      ": static-type = '%s' has dynamic type: uid={0x%" PRIx64 +                      "}, type-name='%s'\n", +                      original_ptr, in_value.GetTypeName().AsCString(), +                      type_sp->GetID(), type_sp->GetName().GetCString()); +                  type_info.SetTypeSP(type_sp); +                } +              } +            } else { +              size_t i; +              if (log) { +                for (i = 0; i < class_types.GetSize(); i++) { +                  type_sp = class_types.GetTypeAtIndex(i); +                  if (type_sp) { +                    LLDB_LOGF( +                        log, +                        "0x%16.16" PRIx64 +                        ": static-type = '%s' has multiple matching dynamic " +                        "types: uid={0x%" PRIx64 "}, type-name='%s'\n", +                        original_ptr, in_value.GetTypeName().AsCString(), +                        type_sp->GetID(), type_sp->GetName().GetCString()); +                  } +                } +              } + +              for (i = 0; i < class_types.GetSize(); i++) { +                type_sp = class_types.GetTypeAtIndex(i); +                if (type_sp) { +                  if (ClangASTContext::IsCXXClassType( +                          type_sp->GetForwardCompilerType())) { +                    LLDB_LOGF( +                        log, +                        "0x%16.16" PRIx64 ": static-type = '%s' has multiple " +                        "matching dynamic types, picking " +                        "this one: uid={0x%" PRIx64 "}, type-name='%s'\n", +                        original_ptr, in_value.GetTypeName().AsCString(), +                        type_sp->GetID(), type_sp->GetName().GetCString()); +                    type_info.SetTypeSP(type_sp); +                  } +                } +              } + +              if (log) { +                LLDB_LOGF(log, +                          "0x%16.16" PRIx64 +                          ": static-type = '%s' has multiple matching dynamic " +                          "types, didn't find a C++ match\n", +                          original_ptr, in_value.GetTypeName().AsCString()); +              } +            } +            if (type_info) +              SetDynamicTypeInfo(vtable_addr, type_info); +            return type_info; +          } +        } +      } +    } +  } +  return TypeAndOrName(); +} + +bool ItaniumABILanguageRuntime::GetDynamicTypeAndAddress( +    ValueObject &in_value, lldb::DynamicValueType use_dynamic, +    TypeAndOrName &class_type_or_name, Address &dynamic_address, +    Value::ValueType &value_type) { +  // For Itanium, if the type has a vtable pointer in the object, it will be at +  // offset 0 in the object.  That will point to the "address point" within the +  // vtable (not the beginning of the vtable.)  We can then look up the symbol +  // containing this "address point" and that symbol's name demangled will +  // contain the full class name. The second pointer above the "address point" +  // is the "offset_to_top".  We'll use that to get the start of the value +  // object which holds the dynamic type. +  // + +  class_type_or_name.Clear(); +  value_type = Value::ValueType::eValueTypeScalar; + +  // Only a pointer or reference type can have a different dynamic and static +  // type: +  if (!CouldHaveDynamicValue(in_value)) +    return false; + +  // First job, pull out the address at 0 offset from the object. +  AddressType address_type; +  lldb::addr_t original_ptr = in_value.GetPointerValue(&address_type); +  if (original_ptr == LLDB_INVALID_ADDRESS) +    return false; + +  ExecutionContext exe_ctx(in_value.GetExecutionContextRef()); + +  Process *process = exe_ctx.GetProcessPtr(); + +  if (process == nullptr) +    return false; + +  Status error; +  const lldb::addr_t vtable_address_point = +      process->ReadPointerFromMemory(original_ptr, error); + +  if (!error.Success() || vtable_address_point == LLDB_INVALID_ADDRESS) +    return false; + +  class_type_or_name = GetTypeInfoFromVTableAddress(in_value, original_ptr, +                                                    vtable_address_point); + +  if (!class_type_or_name) +    return false; + +  CompilerType type = class_type_or_name.GetCompilerType(); +  // There can only be one type with a given name, so we've just found +  // duplicate definitions, and this one will do as well as any other. We +  // don't consider something to have a dynamic type if it is the same as +  // the static type.  So compare against the value we were handed. +  if (!type) +    return true; + +  if (ClangASTContext::AreTypesSame(in_value.GetCompilerType(), type)) { +    // The dynamic type we found was the same type, so we don't have a +    // dynamic type here... +    return false; +  } + +  // The offset_to_top is two pointers above the vtable pointer. +  const uint32_t addr_byte_size = process->GetAddressByteSize(); +  const lldb::addr_t offset_to_top_location = +      vtable_address_point - 2 * addr_byte_size; +  // Watch for underflow, offset_to_top_location should be less than +  // vtable_address_point +  if (offset_to_top_location >= vtable_address_point) +    return false; +  const int64_t offset_to_top = process->ReadSignedIntegerFromMemory( +      offset_to_top_location, addr_byte_size, INT64_MIN, error); + +  if (offset_to_top == INT64_MIN) +    return false; +  // So the dynamic type is a value that starts at offset_to_top above +  // the original address. +  lldb::addr_t dynamic_addr = original_ptr + offset_to_top; +  if (!process->GetTarget().GetSectionLoadList().ResolveLoadAddress( +          dynamic_addr, dynamic_address)) { +    dynamic_address.SetRawAddress(dynamic_addr); +  } +  return true; +} + +TypeAndOrName ItaniumABILanguageRuntime::FixUpDynamicType( +    const TypeAndOrName &type_and_or_name, ValueObject &static_value) { +  CompilerType static_type(static_value.GetCompilerType()); +  Flags static_type_flags(static_type.GetTypeInfo()); + +  TypeAndOrName ret(type_and_or_name); +  if (type_and_or_name.HasType()) { +    // The type will always be the type of the dynamic object.  If our parent's +    // type was a pointer, then our type should be a pointer to the type of the +    // dynamic object.  If a reference, then the original type should be +    // okay... +    CompilerType orig_type = type_and_or_name.GetCompilerType(); +    CompilerType corrected_type = orig_type; +    if (static_type_flags.AllSet(eTypeIsPointer)) +      corrected_type = orig_type.GetPointerType(); +    else if (static_type_flags.AllSet(eTypeIsReference)) +      corrected_type = orig_type.GetLValueReferenceType(); +    ret.SetCompilerType(corrected_type); +  } else { +    // If we are here we need to adjust our dynamic type name to include the +    // correct & or * symbol +    std::string corrected_name(type_and_or_name.GetName().GetCString()); +    if (static_type_flags.AllSet(eTypeIsPointer)) +      corrected_name.append(" *"); +    else if (static_type_flags.AllSet(eTypeIsReference)) +      corrected_name.append(" &"); +    // the parent type should be a correctly pointer'ed or referenc'ed type +    ret.SetCompilerType(static_type); +    ret.SetName(corrected_name.c_str()); +  } +  return ret; +} + +// Static Functions +LanguageRuntime * +ItaniumABILanguageRuntime::CreateInstance(Process *process, +                                          lldb::LanguageType language) { +  // FIXME: We have to check the process and make sure we actually know that +  // this process supports +  // the Itanium ABI. +  if (language == eLanguageTypeC_plus_plus || +      language == eLanguageTypeC_plus_plus_03 || +      language == eLanguageTypeC_plus_plus_11 || +      language == eLanguageTypeC_plus_plus_14) +    return new ItaniumABILanguageRuntime(process); +  else +    return nullptr; +} + +class CommandObjectMultiwordItaniumABI_Demangle : public CommandObjectParsed { +public: +  CommandObjectMultiwordItaniumABI_Demangle(CommandInterpreter &interpreter) +      : CommandObjectParsed(interpreter, "demangle", +                            "Demangle a C++ mangled name.", +                            "language cplusplus demangle") { +    CommandArgumentEntry arg; +    CommandArgumentData index_arg; + +    // Define the first (and only) variant of this arg. +    index_arg.arg_type = eArgTypeSymbol; +    index_arg.arg_repetition = eArgRepeatPlus; + +    // There is only one variant this argument could be; put it into the +    // argument entry. +    arg.push_back(index_arg); + +    // Push the data for the first argument into the m_arguments vector. +    m_arguments.push_back(arg); +  } + +  ~CommandObjectMultiwordItaniumABI_Demangle() override = default; + +protected: +  bool DoExecute(Args &command, CommandReturnObject &result) override { +    bool demangled_any = false; +    bool error_any = false; +    for (auto &entry : command.entries()) { +      if (entry.ref().empty()) +        continue; + +      // the actual Mangled class should be strict about this, but on the +      // command line if you're copying mangled names out of 'nm' on Darwin, +      // they will come out with an extra underscore - be willing to strip this +      // on behalf of the user.   This is the moral equivalent of the -_/-n +      // options to c++filt +      auto name = entry.ref(); +      if (name.startswith("__Z")) +        name = name.drop_front(); + +      Mangled mangled(name); +      if (mangled.GuessLanguage() == lldb::eLanguageTypeC_plus_plus) { +        ConstString demangled( +            mangled.GetDisplayDemangledName(lldb::eLanguageTypeC_plus_plus)); +        demangled_any = true; +        result.AppendMessageWithFormat("%s ---> %s\n", entry.c_str(), +                                       demangled.GetCString()); +      } else { +        error_any = true; +        result.AppendErrorWithFormat("%s is not a valid C++ mangled name\n", +                                     entry.ref().str().c_str()); +      } +    } + +    result.SetStatus( +        error_any ? lldb::eReturnStatusFailed +                  : (demangled_any ? lldb::eReturnStatusSuccessFinishResult +                                   : lldb::eReturnStatusSuccessFinishNoResult)); +    return result.Succeeded(); +  } +}; + +class CommandObjectMultiwordItaniumABI : public CommandObjectMultiword { +public: +  CommandObjectMultiwordItaniumABI(CommandInterpreter &interpreter) +      : CommandObjectMultiword( +            interpreter, "cplusplus", +            "Commands for operating on the C++ language runtime.", +            "cplusplus <subcommand> [<subcommand-options>]") { +    LoadSubCommand( +        "demangle", +        CommandObjectSP( +            new CommandObjectMultiwordItaniumABI_Demangle(interpreter))); +  } + +  ~CommandObjectMultiwordItaniumABI() override = default; +}; + +void ItaniumABILanguageRuntime::Initialize() { +  PluginManager::RegisterPlugin( +      GetPluginNameStatic(), "Itanium ABI for the C++ language", CreateInstance, +      [](CommandInterpreter &interpreter) -> lldb::CommandObjectSP { +        return CommandObjectSP( +            new CommandObjectMultiwordItaniumABI(interpreter)); +      }); +} + +void ItaniumABILanguageRuntime::Terminate() { +  PluginManager::UnregisterPlugin(CreateInstance); +} + +lldb_private::ConstString ItaniumABILanguageRuntime::GetPluginNameStatic() { +  static ConstString g_name("itanium"); +  return g_name; +} + +// PluginInterface protocol +lldb_private::ConstString ItaniumABILanguageRuntime::GetPluginName() { +  return GetPluginNameStatic(); +} + +uint32_t ItaniumABILanguageRuntime::GetPluginVersion() { return 1; } + +BreakpointResolverSP ItaniumABILanguageRuntime::CreateExceptionResolver( +    Breakpoint *bkpt, bool catch_bp, bool throw_bp) { +  return CreateExceptionResolver(bkpt, catch_bp, throw_bp, false); +} + +BreakpointResolverSP ItaniumABILanguageRuntime::CreateExceptionResolver( +    Breakpoint *bkpt, bool catch_bp, bool throw_bp, bool for_expressions) { +  // One complication here is that most users DON'T want to stop at +  // __cxa_allocate_expression, but until we can do anything better with +  // predicting unwinding the expression parser does.  So we have two forms of +  // the exception breakpoints, one for expressions that leaves out +  // __cxa_allocate_exception, and one that includes it. The +  // SetExceptionBreakpoints does the latter, the CreateExceptionBreakpoint in +  // the runtime the former. +  static const char *g_catch_name = "__cxa_begin_catch"; +  static const char *g_throw_name1 = "__cxa_throw"; +  static const char *g_throw_name2 = "__cxa_rethrow"; +  static const char *g_exception_throw_name = "__cxa_allocate_exception"; +  std::vector<const char *> exception_names; +  exception_names.reserve(4); +  if (catch_bp) +    exception_names.push_back(g_catch_name); + +  if (throw_bp) { +    exception_names.push_back(g_throw_name1); +    exception_names.push_back(g_throw_name2); +  } + +  if (for_expressions) +    exception_names.push_back(g_exception_throw_name); + +  BreakpointResolverSP resolver_sp(new BreakpointResolverName( +      bkpt, exception_names.data(), exception_names.size(), +      eFunctionNameTypeBase, eLanguageTypeUnknown, 0, eLazyBoolNo)); + +  return resolver_sp; +} + +lldb::SearchFilterSP ItaniumABILanguageRuntime::CreateExceptionSearchFilter() { +  Target &target = m_process->GetTarget(); + +  FileSpecList filter_modules; +  if (target.GetArchitecture().GetTriple().getVendor() == llvm::Triple::Apple) { +    // Limit the number of modules that are searched for these breakpoints for +    // Apple binaries. +    filter_modules.EmplaceBack("libc++abi.dylib"); +    filter_modules.EmplaceBack("libSystem.B.dylib"); +  } +  return target.GetSearchFilterForModuleList(&filter_modules); +} + +lldb::BreakpointSP ItaniumABILanguageRuntime::CreateExceptionBreakpoint( +    bool catch_bp, bool throw_bp, bool for_expressions, bool is_internal) { +  Target &target = m_process->GetTarget(); +  FileSpecList filter_modules; +  BreakpointResolverSP exception_resolver_sp = +      CreateExceptionResolver(nullptr, catch_bp, throw_bp, for_expressions); +  SearchFilterSP filter_sp(CreateExceptionSearchFilter()); +  const bool hardware = false; +  const bool resolve_indirect_functions = false; +  return target.CreateBreakpoint(filter_sp, exception_resolver_sp, is_internal, +                                 hardware, resolve_indirect_functions); +} + +void ItaniumABILanguageRuntime::SetExceptionBreakpoints() { +  if (!m_process) +    return; + +  const bool catch_bp = false; +  const bool throw_bp = true; +  const bool is_internal = true; +  const bool for_expressions = true; + +  // For the exception breakpoints set by the Expression parser, we'll be a +  // little more aggressive and stop at exception allocation as well. + +  if (m_cxx_exception_bp_sp) { +    m_cxx_exception_bp_sp->SetEnabled(true); +  } else { +    m_cxx_exception_bp_sp = CreateExceptionBreakpoint( +        catch_bp, throw_bp, for_expressions, is_internal); +    if (m_cxx_exception_bp_sp) +      m_cxx_exception_bp_sp->SetBreakpointKind("c++ exception"); +  } +} + +void ItaniumABILanguageRuntime::ClearExceptionBreakpoints() { +  if (!m_process) +    return; + +  if (m_cxx_exception_bp_sp) { +    m_cxx_exception_bp_sp->SetEnabled(false); +  } +} + +bool ItaniumABILanguageRuntime::ExceptionBreakpointsAreSet() { +  return m_cxx_exception_bp_sp && m_cxx_exception_bp_sp->IsEnabled(); +} + +bool ItaniumABILanguageRuntime::ExceptionBreakpointsExplainStop( +    lldb::StopInfoSP stop_reason) { +  if (!m_process) +    return false; + +  if (!stop_reason || stop_reason->GetStopReason() != eStopReasonBreakpoint) +    return false; + +  uint64_t break_site_id = stop_reason->GetValue(); +  return m_process->GetBreakpointSiteList().BreakpointSiteContainsBreakpoint( +      break_site_id, m_cxx_exception_bp_sp->GetID()); +} + +ValueObjectSP ItaniumABILanguageRuntime::GetExceptionObjectForThread( +    ThreadSP thread_sp) { +  if (!thread_sp->SafeToCallFunctions()) +    return {}; + +  ClangASTContext *clang_ast_context = +      m_process->GetTarget().GetScratchClangASTContext(); +  CompilerType voidstar = +      clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType(); + +  DiagnosticManager diagnostics; +  ExecutionContext exe_ctx; +  EvaluateExpressionOptions options; + +  options.SetUnwindOnError(true); +  options.SetIgnoreBreakpoints(true); +  options.SetStopOthers(true); +  options.SetTimeout(m_process->GetUtilityExpressionTimeout()); +  options.SetTryAllThreads(false); +  thread_sp->CalculateExecutionContext(exe_ctx); + +  const ModuleList &modules = m_process->GetTarget().GetImages(); +  SymbolContextList contexts; +  SymbolContext context; + +  modules.FindSymbolsWithNameAndType( +      ConstString("__cxa_current_exception_type"), eSymbolTypeCode, contexts); +  contexts.GetContextAtIndex(0, context); +  Address addr = context.symbol->GetAddress(); + +  Status error; +  FunctionCaller *function_caller = +      m_process->GetTarget().GetFunctionCallerForLanguage( +          eLanguageTypeC, voidstar, addr, ValueList(), "caller", error); + +  ExpressionResults func_call_ret; +  Value results; +  func_call_ret = function_caller->ExecuteFunction(exe_ctx, nullptr, options, +                                                   diagnostics, results); +  if (func_call_ret != eExpressionCompleted || !error.Success()) { +    return ValueObjectSP(); +  } + +  size_t ptr_size = m_process->GetAddressByteSize(); +  addr_t result_ptr = results.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); +  addr_t exception_addr = +      m_process->ReadPointerFromMemory(result_ptr - ptr_size, error); + +  if (!error.Success()) { +    return ValueObjectSP(); +  } + +  lldb_private::formatters::InferiorSizedWord exception_isw(exception_addr, +                                                            *m_process); +  ValueObjectSP exception = ValueObject::CreateValueObjectFromData( +      "exception", exception_isw.GetAsData(m_process->GetByteOrder()), exe_ctx, +      voidstar); +  exception = exception->GetDynamicValue(eDynamicDontRunTarget); + +  return exception; +} + +TypeAndOrName ItaniumABILanguageRuntime::GetDynamicTypeInfo( +    const lldb_private::Address &vtable_addr) { +  std::lock_guard<std::mutex> locker(m_dynamic_type_map_mutex); +  DynamicTypeCache::const_iterator pos = m_dynamic_type_map.find(vtable_addr); +  if (pos == m_dynamic_type_map.end()) +    return TypeAndOrName(); +  else +    return pos->second; +} + +void ItaniumABILanguageRuntime::SetDynamicTypeInfo( +    const lldb_private::Address &vtable_addr, const TypeAndOrName &type_info) { +  std::lock_guard<std::mutex> locker(m_dynamic_type_map_mutex); +  m_dynamic_type_map[vtable_addr] = type_info; +} diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h new file mode 100644 index 0000000000000..97cc81b8681fc --- /dev/null +++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h @@ -0,0 +1,117 @@ +//===-- ItaniumABILanguageRuntime.h -----------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ItaniumABILanguageRuntime_h_ +#define liblldb_ItaniumABILanguageRuntime_h_ + +#include <map> +#include <mutex> +#include <vector> + +#include "lldb/Breakpoint/BreakpointResolver.h" +#include "lldb/Core/Value.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Target/LanguageRuntime.h" +#include "lldb/lldb-private.h" + +#include "Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h" + +namespace lldb_private { + +class ItaniumABILanguageRuntime : public lldb_private::CPPLanguageRuntime { +public: +  ~ItaniumABILanguageRuntime() override = default; + +  // Static Functions +  static void Initialize(); + +  static void Terminate(); + +  static lldb_private::LanguageRuntime * +  CreateInstance(Process *process, lldb::LanguageType language); + +  static lldb_private::ConstString GetPluginNameStatic(); + +  static char ID; + +  bool isA(const void *ClassID) const override { +    return ClassID == &ID || CPPLanguageRuntime::isA(ClassID); +  } + +  static bool classof(const LanguageRuntime *runtime) { +    return runtime->isA(&ID); +  } + +  bool GetDynamicTypeAndAddress(ValueObject &in_value, +                                lldb::DynamicValueType use_dynamic, +                                TypeAndOrName &class_type_or_name, +                                Address &address, +                                Value::ValueType &value_type) override; + +  TypeAndOrName FixUpDynamicType(const TypeAndOrName &type_and_or_name, +                                 ValueObject &static_value) override; + +  bool CouldHaveDynamicValue(ValueObject &in_value) override; + +  void SetExceptionBreakpoints() override; + +  void ClearExceptionBreakpoints() override; + +  bool ExceptionBreakpointsAreSet() override; + +  bool ExceptionBreakpointsExplainStop(lldb::StopInfoSP stop_reason) override; + +  lldb::BreakpointResolverSP CreateExceptionResolver(Breakpoint *bkpt, +                                                     bool catch_bp, +                                                     bool throw_bp) override; + +  lldb::SearchFilterSP CreateExceptionSearchFilter() override; +   +  lldb::ValueObjectSP GetExceptionObjectForThread( +      lldb::ThreadSP thread_sp) override; + +  // PluginInterface protocol +  lldb_private::ConstString GetPluginName() override; + +  uint32_t GetPluginVersion() override; + +protected: +  lldb::BreakpointResolverSP CreateExceptionResolver(Breakpoint *bkpt, +                                                     bool catch_bp, +                                                     bool throw_bp, +                                                     bool for_expressions); + +  lldb::BreakpointSP CreateExceptionBreakpoint(bool catch_bp, bool throw_bp, +                                               bool for_expressions, +                                               bool is_internal); + +private: +  typedef std::map<lldb_private::Address, TypeAndOrName> DynamicTypeCache; + +  ItaniumABILanguageRuntime(Process *process) +      : // Call CreateInstance instead. +        lldb_private::CPPLanguageRuntime(process), m_cxx_exception_bp_sp(), +        m_dynamic_type_map(), m_dynamic_type_map_mutex() {} + +  lldb::BreakpointSP m_cxx_exception_bp_sp; +  DynamicTypeCache m_dynamic_type_map; +  std::mutex m_dynamic_type_map_mutex; + +  TypeAndOrName GetTypeInfoFromVTableAddress(ValueObject &in_value, +                                             lldb::addr_t original_ptr, +                                             lldb::addr_t vtable_addr); + +  TypeAndOrName GetDynamicTypeInfo(const lldb_private::Address &vtable_addr); + +  void SetDynamicTypeInfo(const lldb_private::Address &vtable_addr, +                          const TypeAndOrName &type_info); +}; + +} // namespace lldb_private + +#endif // liblldb_ItaniumABILanguageRuntime_h_  | 
