| //===-- CPPLanguageRuntime.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 "lldb/Target/CPPLanguageRuntime.h" |
| |
| #include <string.h> |
| |
| #include <memory> |
| |
| #include "llvm/ADT/StringRef.h" |
| |
| #include "lldb/Symbol/Block.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; |
| |
| //---------------------------------------------------------------------- |
| // Destructor |
| //---------------------------------------------------------------------- |
| CPPLanguageRuntime::~CPPLanguageRuntime() {} |
| |
| CPPLanguageRuntime::CPPLanguageRuntime(Process *process) |
| : LanguageRuntime(process) {} |
| |
| 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_first_of(','); |
| |
| 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; |
| } |
| } |
| |
| // Case 4 or 5 |
| if (!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(ConstString("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; |
| } |