|  | //===-- 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/CompileUnit.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" | 
|  | #include "lldb/Utility/Timer.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::IsAllowedRuntimeValue(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; | 
|  | } | 
|  |  | 
|  | bool contains_lambda_identifier(llvm::StringRef &str_ref) { | 
|  | return str_ref.contains("$_") || str_ref.contains("'lambda'"); | 
|  | } | 
|  |  | 
|  | CPPLanguageRuntime::LibCppStdFunctionCallableInfo | 
|  | line_entry_helper(Target &target, const SymbolContext &sc, Symbol *symbol, | 
|  | llvm::StringRef first_template_param_sref, | 
|  | bool has___invoke) { | 
|  |  | 
|  | CPPLanguageRuntime::LibCppStdFunctionCallableInfo optional_info; | 
|  |  | 
|  | AddressRange range; | 
|  | sc.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 (contains_lambda_identifier(first_template_param_sref) || has___invoke) { | 
|  | // Case 1 and 2 | 
|  | optional_info.callable_case = lldb_private::CPPLanguageRuntime:: | 
|  | LibCppStdFunctionCallableCase::Lambda; | 
|  | } else { | 
|  | // Case 3 | 
|  | optional_info.callable_case = lldb_private::CPPLanguageRuntime:: | 
|  | LibCppStdFunctionCallableCase::CallableObject; | 
|  | } | 
|  |  | 
|  | optional_info.callable_symbol = *symbol; | 
|  | optional_info.callable_line_entry = line_entry; | 
|  | optional_info.callable_address = addr; | 
|  | } | 
|  |  | 
|  | return optional_info; | 
|  | } | 
|  |  | 
|  | CPPLanguageRuntime::LibCppStdFunctionCallableInfo | 
|  | CPPLanguageRuntime::FindLibCppStdFunctionCallableInfo( | 
|  | lldb::ValueObjectSP &valobj_sp) { | 
|  | static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); | 
|  | Timer scoped_timer(func_cat, | 
|  | "CPPLanguageRuntime::FindLibCppStdFunctionCallableInfo"); | 
|  |  | 
|  | 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 objects 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; | 
|  |  | 
|  | if (!member__f_pointer_value) | 
|  | return optional_info; | 
|  |  | 
|  | 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 vtable_address_first_entry = | 
|  | process->ReadPointerFromMemory(vtable_address + address_size, status); | 
|  |  | 
|  | if (status.Fail()) | 
|  | return optional_info; | 
|  |  | 
|  | lldb::addr_t address_after_vtable = member__f_pointer_value + address_size; | 
|  | // As commented 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_first_entry_resolved; | 
|  |  | 
|  | if (!target.GetSectionLoadList().ResolveLoadAddress( | 
|  | vtable_address_first_entry, vtable_first_entry_resolved)) | 
|  | return optional_info; | 
|  |  | 
|  | Address vtable_addr_resolved; | 
|  | SymbolContext sc; | 
|  | Symbol *symbol = nullptr; | 
|  |  | 
|  | 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().GetStringRef()); | 
|  | 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 could see names such as: | 
|  | //    main::$_0 | 
|  | //    Bar::add_num2(int)::'lambda'(int) | 
|  | //    Bar | 
|  | // | 
|  | // 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; | 
|  | } | 
|  |  | 
|  | // These conditions are used several times to simplify statements later on. | 
|  | bool has___invoke = | 
|  | (symbol ? symbol->GetName().GetStringRef().contains("__invoke") : false); | 
|  | auto calculate_symbol_context_helper = [](auto &t, | 
|  | SymbolContextList &sc_list) { | 
|  | SymbolContext sc; | 
|  | t->CalculateSymbolContext(&sc); | 
|  | sc_list.Append(sc); | 
|  | }; | 
|  |  | 
|  | // Case 2 | 
|  | if (has___invoke) { | 
|  | SymbolContextList scl; | 
|  | calculate_symbol_context_helper(symbol, scl); | 
|  |  | 
|  | return line_entry_helper(target, scl[0], symbol, first_template_parameter, | 
|  | has___invoke); | 
|  | } | 
|  |  | 
|  | // Case 4 or 5 | 
|  | if (symbol && !symbol->GetName().GetStringRef().startswith("vtable for") && | 
|  | !contains_lambda_identifier(first_template_parameter) && !has___invoke) { | 
|  | optional_info.callable_case = | 
|  | LibCppStdFunctionCallableCase::FreeOrMemberFunction; | 
|  | optional_info.callable_address = function_address_resolved; | 
|  | optional_info.callable_symbol = *symbol; | 
|  |  | 
|  | return optional_info; | 
|  | } | 
|  |  | 
|  | std::string func_to_match = first_template_parameter.str(); | 
|  |  | 
|  | auto it = CallableLookupCache.find(func_to_match); | 
|  | if (it != CallableLookupCache.end()) | 
|  | return it->second; | 
|  |  | 
|  | SymbolContextList scl; | 
|  |  | 
|  | CompileUnit *vtable_cu = | 
|  | vtable_first_entry_resolved.CalculateSymbolContextCompileUnit(); | 
|  | llvm::StringRef name_to_use = func_to_match; | 
|  |  | 
|  | // Case 3, we have a callable object instead of a lambda | 
|  | // | 
|  | // TODO | 
|  | // We currently don't support this case a callable object may have multiple | 
|  | // operator()() varying on const/non-const and number of arguments and we | 
|  | // don't have a way to currently distinguish them so we will bail out now. | 
|  | if (!contains_lambda_identifier(name_to_use)) | 
|  | return optional_info; | 
|  |  | 
|  | if (vtable_cu && !has___invoke) { | 
|  | lldb::FunctionSP func_sp = | 
|  | vtable_cu->FindFunction([name_to_use](const FunctionSP &f) { | 
|  | auto name = f->GetName().GetStringRef(); | 
|  | if (name.startswith(name_to_use) && name.contains("operator")) | 
|  | return true; | 
|  |  | 
|  | return false; | 
|  | }); | 
|  |  | 
|  | if (func_sp) { | 
|  | calculate_symbol_context_helper(func_sp, scl); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Case 1 or 3 | 
|  | if (scl.GetSize() >= 1) { | 
|  | optional_info = line_entry_helper(target, scl[0], symbol, | 
|  | first_template_parameter, has___invoke); | 
|  | } | 
|  |  | 
|  | CallableLookupCache[func_to_match] = 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; | 
|  | } |