Esempio n. 1
0
ObjCLanguageRuntime::ClassDescriptorSP
ObjCLanguageRuntime::GetClassDescriptor(ValueObject &valobj) {
  ClassDescriptorSP objc_class_sp;
  // if we get an invalid VO (which might still happen when playing around
  // with pointers returned by the expression parser, don't consider this
  // a valid ObjC object)
  if (valobj.GetCompilerType().IsValid()) {
    addr_t isa_pointer = valobj.GetPointerValue();
    if (isa_pointer != LLDB_INVALID_ADDRESS) {
      ExecutionContext exe_ctx(valobj.GetExecutionContextRef());

      Process *process = exe_ctx.GetProcessPtr();
      if (process) {
        Error error;
        ObjCISA isa = process->ReadPointerFromMemory(isa_pointer, error);
        if (isa != LLDB_INVALID_ADDRESS)
          objc_class_sp = GetClassDescriptorFromISA(isa);
      }
    }
  }
  return objc_class_sp;
}
Esempio n. 2
0
lldb::addr_t
AppleObjCRuntimeV1::GetISAHashTablePointer ()
{
    if (m_isa_hash_table_ptr == LLDB_INVALID_ADDRESS)
    {
        ModuleSP objc_module_sp(GetObjCModule());
        
        if (!objc_module_sp)
            return LLDB_INVALID_ADDRESS;
        
        static ConstString g_objc_debug_class_hash("_objc_debug_class_hash");
        
        const Symbol *symbol = objc_module_sp->FindFirstSymbolWithNameAndType(g_objc_debug_class_hash, lldb::eSymbolTypeData);
        if (symbol && symbol->ValueIsAddress())
        {
            Process *process = GetProcess();
            if (process)
            {

                lldb::addr_t objc_debug_class_hash_addr = symbol->GetAddressRef().GetLoadAddress(&process->GetTarget());
            
                if (objc_debug_class_hash_addr != LLDB_INVALID_ADDRESS)
                {
                    Error error;
                    lldb::addr_t objc_debug_class_hash_ptr = process->ReadPointerFromMemory(objc_debug_class_hash_addr, error);
                    if (objc_debug_class_hash_ptr != 0 &&
                        objc_debug_class_hash_ptr != LLDB_INVALID_ADDRESS)
                    {
                        m_isa_hash_table_ptr = objc_debug_class_hash_ptr;
                    }
                }
            }
        }
    }
    return m_isa_hash_table_ptr;
}
Esempio n. 3
0
bool Address::Dump(Stream *s, ExecutionContextScope *exe_scope, DumpStyle style,
                   DumpStyle fallback_style, uint32_t addr_size) const {
  // If the section was nullptr, only load address is going to work unless we
  // are
  // trying to deref a pointer
  SectionSP section_sp(GetSection());
  if (!section_sp && style != DumpStyleResolvedPointerDescription)
    style = DumpStyleLoadAddress;

  ExecutionContext exe_ctx(exe_scope);
  Target *target = exe_ctx.GetTargetPtr();
  // If addr_byte_size is UINT32_MAX, then determine the correct address
  // byte size for the process or default to the size of addr_t
  if (addr_size == UINT32_MAX) {
    if (target)
      addr_size = target->GetArchitecture().GetAddressByteSize();
    else
      addr_size = sizeof(addr_t);
  }

  Address so_addr;
  switch (style) {
  case DumpStyleInvalid:
    return false;

  case DumpStyleSectionNameOffset:
    if (section_sp) {
      section_sp->DumpName(s);
      s->Printf(" + %" PRIu64, m_offset);
    } else {
      s->Address(m_offset, addr_size);
    }
    break;

  case DumpStyleSectionPointerOffset:
    s->Printf("(Section *)%p + ", static_cast<void *>(section_sp.get()));
    s->Address(m_offset, addr_size);
    break;

  case DumpStyleModuleWithFileAddress:
    if (section_sp) {
      ModuleSP module_sp = section_sp->GetModule();
      if (module_sp)
        s->Printf("%s[", module_sp->GetFileSpec().GetFilename().AsCString(
                             "<Unknown>"));
      else
        s->Printf("%s[", "<Unknown>");
    }
    LLVM_FALLTHROUGH;
  case DumpStyleFileAddress: {
    addr_t file_addr = GetFileAddress();
    if (file_addr == LLDB_INVALID_ADDRESS) {
      if (fallback_style != DumpStyleInvalid)
        return Dump(s, exe_scope, fallback_style, DumpStyleInvalid, addr_size);
      return false;
    }
    s->Address(file_addr, addr_size);
    if (style == DumpStyleModuleWithFileAddress && section_sp)
      s->PutChar(']');
  } break;

  case DumpStyleLoadAddress: {
    addr_t load_addr = GetLoadAddress(target);

    /*
     * MIPS:
     * Display address in compressed form for MIPS16 or microMIPS
     * if the address belongs to eAddressClassCodeAlternateISA.
    */
    if (target) {
      const llvm::Triple::ArchType llvm_arch =
          target->GetArchitecture().GetMachine();
      if (llvm_arch == llvm::Triple::mips ||
          llvm_arch == llvm::Triple::mipsel ||
          llvm_arch == llvm::Triple::mips64 ||
          llvm_arch == llvm::Triple::mips64el)
        load_addr = GetCallableLoadAddress(target);
    }

    if (load_addr == LLDB_INVALID_ADDRESS) {
      if (fallback_style != DumpStyleInvalid)
        return Dump(s, exe_scope, fallback_style, DumpStyleInvalid, addr_size);
      return false;
    }
    s->Address(load_addr, addr_size);
  } break;

  case DumpStyleResolvedDescription:
  case DumpStyleResolvedDescriptionNoModule:
  case DumpStyleResolvedDescriptionNoFunctionArguments:
  case DumpStyleNoFunctionName:
    if (IsSectionOffset()) {
      uint32_t pointer_size = 4;
      ModuleSP module_sp(GetModule());
      if (target)
        pointer_size = target->GetArchitecture().GetAddressByteSize();
      else if (module_sp)
        pointer_size = module_sp->GetArchitecture().GetAddressByteSize();

      bool showed_info = false;
      if (section_sp) {
        SectionType sect_type = section_sp->GetType();
        switch (sect_type) {
        case eSectionTypeData:
          if (module_sp) {
            SymbolVendor *sym_vendor = module_sp->GetSymbolVendor();
            if (sym_vendor) {
              Symtab *symtab = sym_vendor->GetSymtab();
              if (symtab) {
                const addr_t file_Addr = GetFileAddress();
                Symbol *symbol =
                    symtab->FindSymbolContainingFileAddress(file_Addr);
                if (symbol) {
                  const char *symbol_name = symbol->GetName().AsCString();
                  if (symbol_name) {
                    s->PutCString(symbol_name);
                    addr_t delta =
                        file_Addr - symbol->GetAddressRef().GetFileAddress();
                    if (delta)
                      s->Printf(" + %" PRIu64, delta);
                    showed_info = true;
                  }
                }
              }
            }
          }
          break;

        case eSectionTypeDataCString:
          // Read the C string from memory and display it
          showed_info = true;
          ReadCStringFromMemory(exe_scope, *this, s);
          break;

        case eSectionTypeDataCStringPointers:
          if (ReadAddress(exe_scope, *this, pointer_size, so_addr)) {
#if VERBOSE_OUTPUT
            s->PutCString("(char *)");
            so_addr.Dump(s, exe_scope, DumpStyleLoadAddress,
                         DumpStyleFileAddress);
            s->PutCString(": ");
#endif
            showed_info = true;
            ReadCStringFromMemory(exe_scope, so_addr, s);
          }
          break;

        case eSectionTypeDataObjCMessageRefs:
          if (ReadAddress(exe_scope, *this, pointer_size, so_addr)) {
            if (target && so_addr.IsSectionOffset()) {
              SymbolContext func_sc;
              target->GetImages().ResolveSymbolContextForAddress(
                  so_addr, eSymbolContextEverything, func_sc);
              if (func_sc.function != nullptr || func_sc.symbol != nullptr) {
                showed_info = true;
#if VERBOSE_OUTPUT
                s->PutCString("(objc_msgref *) -> { (func*)");
                so_addr.Dump(s, exe_scope, DumpStyleLoadAddress,
                             DumpStyleFileAddress);
#else
                s->PutCString("{ ");
#endif
                Address cstr_addr(*this);
                cstr_addr.SetOffset(cstr_addr.GetOffset() + pointer_size);
                func_sc.DumpStopContext(s, exe_scope, so_addr, true, true,
                                        false, true, true);
                if (ReadAddress(exe_scope, cstr_addr, pointer_size, so_addr)) {
#if VERBOSE_OUTPUT
                  s->PutCString("), (char *)");
                  so_addr.Dump(s, exe_scope, DumpStyleLoadAddress,
                               DumpStyleFileAddress);
                  s->PutCString(" (");
#else
                  s->PutCString(", ");
#endif
                  ReadCStringFromMemory(exe_scope, so_addr, s);
                }
#if VERBOSE_OUTPUT
                s->PutCString(") }");
#else
                s->PutCString(" }");
#endif
              }
            }
          }
          break;

        case eSectionTypeDataObjCCFStrings: {
          Address cfstring_data_addr(*this);
          cfstring_data_addr.SetOffset(cfstring_data_addr.GetOffset() +
                                       (2 * pointer_size));
          if (ReadAddress(exe_scope, cfstring_data_addr, pointer_size,
                          so_addr)) {
#if VERBOSE_OUTPUT
            s->PutCString("(CFString *) ");
            cfstring_data_addr.Dump(s, exe_scope, DumpStyleLoadAddress,
                                    DumpStyleFileAddress);
            s->PutCString(" -> @");
#else
            s->PutChar('@');
#endif
            if (so_addr.Dump(s, exe_scope, DumpStyleResolvedDescription))
              showed_info = true;
          }
        } break;

        case eSectionTypeData4:
          // Read the 4 byte data and display it
          showed_info = true;
          s->PutCString("(uint32_t) ");
          DumpUInt(exe_scope, *this, 4, s);
          break;

        case eSectionTypeData8:
          // Read the 8 byte data and display it
          showed_info = true;
          s->PutCString("(uint64_t) ");
          DumpUInt(exe_scope, *this, 8, s);
          break;

        case eSectionTypeData16:
          // Read the 16 byte data and display it
          showed_info = true;
          s->PutCString("(uint128_t) ");
          DumpUInt(exe_scope, *this, 16, s);
          break;

        case eSectionTypeDataPointers:
          // Read the pointer data and display it
          if (ReadAddress(exe_scope, *this, pointer_size, so_addr)) {
            s->PutCString("(void *)");
            so_addr.Dump(s, exe_scope, DumpStyleLoadAddress,
                         DumpStyleFileAddress);

            showed_info = true;
            if (so_addr.IsSectionOffset()) {
              SymbolContext pointer_sc;
              if (target) {
                target->GetImages().ResolveSymbolContextForAddress(
                    so_addr, eSymbolContextEverything, pointer_sc);
                if (pointer_sc.function != nullptr ||
                    pointer_sc.symbol != nullptr) {
                  s->PutCString(": ");
                  pointer_sc.DumpStopContext(s, exe_scope, so_addr, true, false,
                                             false, true, true);
                }
              }
            }
          }
          break;

        default:
          break;
        }
      }

      if (!showed_info) {
        if (module_sp) {
          SymbolContext sc;
          module_sp->ResolveSymbolContextForAddress(
              *this, eSymbolContextEverything, sc);
          if (sc.function || sc.symbol) {
            bool show_stop_context = true;
            const bool show_module = (style == DumpStyleResolvedDescription);
            const bool show_fullpaths = false;
            const bool show_inlined_frames = true;
            const bool show_function_arguments =
                (style != DumpStyleResolvedDescriptionNoFunctionArguments);
            const bool show_function_name = (style != DumpStyleNoFunctionName);
            if (sc.function == nullptr && sc.symbol != nullptr) {
              // If we have just a symbol make sure it is in the right section
              if (sc.symbol->ValueIsAddress()) {
                if (sc.symbol->GetAddressRef().GetSection() != GetSection()) {
                  // don't show the module if the symbol is a trampoline symbol
                  show_stop_context = false;
                }
              }
            }
            if (show_stop_context) {
              // We have a function or a symbol from the same
              // sections as this address.
              sc.DumpStopContext(s, exe_scope, *this, show_fullpaths,
                                 show_module, show_inlined_frames,
                                 show_function_arguments, show_function_name);
            } else {
              // We found a symbol but it was in a different
              // section so it isn't the symbol we should be
              // showing, just show the section name + offset
              Dump(s, exe_scope, DumpStyleSectionNameOffset);
            }
          }
        }
      }
    } else {
      if (fallback_style != DumpStyleInvalid)
        return Dump(s, exe_scope, fallback_style, DumpStyleInvalid, addr_size);
      return false;
    }
    break;

  case DumpStyleDetailedSymbolContext:
    if (IsSectionOffset()) {
      ModuleSP module_sp(GetModule());
      if (module_sp) {
        SymbolContext sc;
        module_sp->ResolveSymbolContextForAddress(
            *this, eSymbolContextEverything | eSymbolContextVariable, sc);
        if (sc.symbol) {
          // If we have just a symbol make sure it is in the same section
          // as our address. If it isn't, then we might have just found
          // the last symbol that came before the address that we are
          // looking up that has nothing to do with our address lookup.
          if (sc.symbol->ValueIsAddress() &&
              sc.symbol->GetAddressRef().GetSection() != GetSection())
            sc.symbol = nullptr;
        }
        sc.GetDescription(s, eDescriptionLevelBrief, target);

        if (sc.block) {
          bool can_create = true;
          bool get_parent_variables = true;
          bool stop_if_block_is_inlined_function = false;
          VariableList variable_list;
          sc.block->AppendVariables(can_create, get_parent_variables,
                                    stop_if_block_is_inlined_function,
                                    [](Variable *) { return true; },
                                    &variable_list);

          const size_t num_variables = variable_list.GetSize();
          for (size_t var_idx = 0; var_idx < num_variables; ++var_idx) {
            Variable *var = variable_list.GetVariableAtIndex(var_idx).get();
            if (var && var->LocationIsValidForAddress(*this)) {
              s->Indent();
              s->Printf("   Variable: id = {0x%8.8" PRIx64 "}, name = \"%s\"",
                        var->GetID(), var->GetName().GetCString());
              Type *type = var->GetType();
              if (type)
                s->Printf(", type = \"%s\"", type->GetName().GetCString());
              else
                s->PutCString(", type = <unknown>");
              s->PutCString(", location = ");
              var->DumpLocationForAddress(s, *this);
              s->PutCString(", decl = ");
              var->GetDeclaration().DumpStopContext(s, false);
              s->EOL();
            }
          }
        }
      }
    } else {
      if (fallback_style != DumpStyleInvalid)
        return Dump(s, exe_scope, fallback_style, DumpStyleInvalid, addr_size);
      return false;
    }
    break;

  case DumpStyleResolvedPointerDescription: {
    Process *process = exe_ctx.GetProcessPtr();
    if (process) {
      addr_t load_addr = GetLoadAddress(target);
      if (load_addr != LLDB_INVALID_ADDRESS) {
        Error memory_error;
        addr_t dereferenced_load_addr =
            process->ReadPointerFromMemory(load_addr, memory_error);
        if (dereferenced_load_addr != LLDB_INVALID_ADDRESS) {
          Address dereferenced_addr;
          if (dereferenced_addr.SetLoadAddress(dereferenced_load_addr,
                                               target)) {
            StreamString strm;
            if (dereferenced_addr.Dump(&strm, exe_scope,
                                       DumpStyleResolvedDescription,
                                       DumpStyleInvalid, addr_size)) {
              s->Address(dereferenced_load_addr, addr_size, " -> ", " ");
              s->Write(strm.GetData(), strm.GetSize());
              return true;
            }
          }
        }
      }
    }
    if (fallback_style != DumpStyleInvalid)
      return Dump(s, exe_scope, fallback_style, DumpStyleInvalid, addr_size);
    return false;
  } break;
  }

  return true;
}
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)) {
    // 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;

    Error 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) {
      TypeSP type_sp = class_type_or_name.GetTypeSP();
      // 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_sp) {
        if (ClangASTContext::AreTypesSame(in_value.GetCompilerType(),
                                          type_sp->GetForwardCompilerType())) {
          // 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;
      }
    }
  }

  return class_type_or_name.IsEmpty() == false;
}
Esempio n. 5
0
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 != NULL &&
        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().FindFunctions(RegularExpression{func_to_match}, true, true,
                                   true, 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;
    }