Path: blob/main/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp
39642 views
//===-- CPPLanguageRuntime.cpp---------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include <cstring>910#include <memory>1112#include "CPPLanguageRuntime.h"1314#include "llvm/ADT/StringRef.h"1516#include "lldb/Symbol/Block.h"17#include "lldb/Symbol/Variable.h"18#include "lldb/Symbol/VariableList.h"1920#include "lldb/Core/PluginManager.h"21#include "lldb/Core/UniqueCStringMap.h"22#include "lldb/Symbol/CompileUnit.h"23#include "lldb/Target/ABI.h"24#include "lldb/Target/ExecutionContext.h"25#include "lldb/Target/RegisterContext.h"26#include "lldb/Target/SectionLoadList.h"27#include "lldb/Target/StackFrame.h"28#include "lldb/Target/ThreadPlanRunToAddress.h"29#include "lldb/Target/ThreadPlanStepInRange.h"30#include "lldb/Utility/Timer.h"3132using namespace lldb;33using namespace lldb_private;3435static ConstString g_this = ConstString("this");36// Artificial coroutine-related variables emitted by clang.37static ConstString g_promise = ConstString("__promise");38static ConstString g_coro_frame = ConstString("__coro_frame");3940char CPPLanguageRuntime::ID = 0;4142CPPLanguageRuntime::CPPLanguageRuntime(Process *process)43: LanguageRuntime(process) {}4445bool CPPLanguageRuntime::IsAllowedRuntimeValue(ConstString name) {46return name == g_this || name == g_promise || name == g_coro_frame;47}4849llvm::Error CPPLanguageRuntime::GetObjectDescription(Stream &str,50ValueObject &object) {51// C++ has no generic way to do this.52return llvm::createStringError("C++ does not support object descriptions");53}5455llvm::Error56CPPLanguageRuntime::GetObjectDescription(Stream &str, Value &value,57ExecutionContextScope *exe_scope) {58// C++ has no generic way to do this.59return llvm::createStringError("C++ does not support object descriptions");60}6162bool contains_lambda_identifier(llvm::StringRef &str_ref) {63return str_ref.contains("$_") || str_ref.contains("'lambda'");64}6566CPPLanguageRuntime::LibCppStdFunctionCallableInfo67line_entry_helper(Target &target, const SymbolContext &sc, Symbol *symbol,68llvm::StringRef first_template_param_sref,69bool has_invoke) {7071CPPLanguageRuntime::LibCppStdFunctionCallableInfo optional_info;7273AddressRange range;74sc.GetAddressRange(eSymbolContextEverything, 0, false, range);7576Address address = range.GetBaseAddress();7778Address addr;79if (target.ResolveLoadAddress(address.GetCallableLoadAddress(&target),80addr)) {81LineEntry line_entry;82addr.CalculateSymbolContextLineEntry(line_entry);8384if (contains_lambda_identifier(first_template_param_sref) || has_invoke) {85// Case 1 and 286optional_info.callable_case = lldb_private::CPPLanguageRuntime::87LibCppStdFunctionCallableCase::Lambda;88} else {89// Case 390optional_info.callable_case = lldb_private::CPPLanguageRuntime::91LibCppStdFunctionCallableCase::CallableObject;92}9394optional_info.callable_symbol = *symbol;95optional_info.callable_line_entry = line_entry;96optional_info.callable_address = addr;97}9899return optional_info;100}101102CPPLanguageRuntime::LibCppStdFunctionCallableInfo103CPPLanguageRuntime::FindLibCppStdFunctionCallableInfo(104lldb::ValueObjectSP &valobj_sp) {105LLDB_SCOPED_TIMER();106107LibCppStdFunctionCallableInfo optional_info;108109if (!valobj_sp)110return optional_info;111112// Member __f_ has type __base*, the contents of which will hold:113// 1) a vtable entry which may hold type information needed to discover the114// lambda being called115// 2) possibly hold a pointer to the callable object116// e.g.117//118// (lldb) frame var -R f_display119// (std::__1::function<void (int)>) f_display = {120// __buf_ = {121// …122// }123// __f_ = 0x00007ffeefbffa00124// }125// (lldb) memory read -fA 0x00007ffeefbffa00126// 0x7ffeefbffa00: ... `vtable for std::__1::__function::__func<void (*) ...127// 0x7ffeefbffa08: ... `print_num(int) at std_function_cppreference_exam ...128//129// We will be handling five cases below, std::function is wrapping:130//131// 1) a lambda we know at compile time. We will obtain the name of the lambda132// from the first template pameter from __func's vtable. We will look up133// the lambda's operator()() and obtain the line table entry.134// 2) a lambda we know at runtime. A pointer to the lambdas __invoke method135// will be stored after the vtable. We will obtain the lambdas name from136// this entry and lookup operator()() and obtain the line table entry.137// 3) a callable object via operator()(). We will obtain the name of the138// object from the first template parameter from __func's vtable. We will139// look up the objects operator()() and obtain the line table entry.140// 4) a member function. A pointer to the function will stored after the141// we will obtain the name from this pointer.142// 5) a free function. A pointer to the function will stored after the vtable143// we will obtain the name from this pointer.144ValueObjectSP member_f_(valobj_sp->GetChildMemberWithName("__f_"));145146if (member_f_) {147ValueObjectSP sub_member_f_(member_f_->GetChildMemberWithName("__f_"));148149if (sub_member_f_)150member_f_ = sub_member_f_;151}152153if (!member_f_)154return optional_info;155156lldb::addr_t member_f_pointer_value = member_f_->GetValueAsUnsigned(0);157158optional_info.member_f_pointer_value = member_f_pointer_value;159160if (!member_f_pointer_value)161return optional_info;162163ExecutionContext exe_ctx(valobj_sp->GetExecutionContextRef());164Process *process = exe_ctx.GetProcessPtr();165166if (process == nullptr)167return optional_info;168169uint32_t address_size = process->GetAddressByteSize();170Status status;171172// First item pointed to by __f_ should be the pointer to the vtable for173// a __base object.174lldb::addr_t vtable_address =175process->ReadPointerFromMemory(member_f_pointer_value, status);176177if (status.Fail())178return optional_info;179180lldb::addr_t vtable_address_first_entry =181process->ReadPointerFromMemory(vtable_address + address_size, status);182183if (status.Fail())184return optional_info;185186lldb::addr_t address_after_vtable = member_f_pointer_value + address_size;187// As commented above we may not have a function pointer but if we do we will188// need it.189lldb::addr_t possible_function_address =190process->ReadPointerFromMemory(address_after_vtable, status);191192if (status.Fail())193return optional_info;194195Target &target = process->GetTarget();196197if (target.GetSectionLoadList().IsEmpty())198return optional_info;199200Address vtable_first_entry_resolved;201202if (!target.GetSectionLoadList().ResolveLoadAddress(203vtable_address_first_entry, vtable_first_entry_resolved))204return optional_info;205206Address vtable_addr_resolved;207SymbolContext sc;208Symbol *symbol = nullptr;209210if (!target.GetSectionLoadList().ResolveLoadAddress(vtable_address,211vtable_addr_resolved))212return optional_info;213214target.GetImages().ResolveSymbolContextForAddress(215vtable_addr_resolved, eSymbolContextEverything, sc);216symbol = sc.symbol;217218if (symbol == nullptr)219return optional_info;220221llvm::StringRef vtable_name(symbol->GetName().GetStringRef());222bool found_expected_start_string =223vtable_name.starts_with("vtable for std::__1::__function::__func<");224225if (!found_expected_start_string)226return optional_info;227228// Given case 1 or 3 we have a vtable name, we are want to extract the first229// template parameter230//231// ... __func<main::$_0, std::__1::allocator<main::$_0> ...232// ^^^^^^^^^233//234// We could see names such as:235// main::$_0236// Bar::add_num2(int)::'lambda'(int)237// Bar238//239// We do this by find the first < and , and extracting in between.240//241// This covers the case of the lambda known at compile time.242size_t first_open_angle_bracket = vtable_name.find('<') + 1;243size_t first_comma = vtable_name.find(',');244245llvm::StringRef first_template_parameter =246vtable_name.slice(first_open_angle_bracket, first_comma);247248Address function_address_resolved;249250// Setup for cases 2, 4 and 5 we have a pointer to a function after the251// vtable. We will use a process of elimination to drop through each case252// and obtain the data we need.253if (target.GetSectionLoadList().ResolveLoadAddress(254possible_function_address, function_address_resolved)) {255target.GetImages().ResolveSymbolContextForAddress(256function_address_resolved, eSymbolContextEverything, sc);257symbol = sc.symbol;258}259260// These conditions are used several times to simplify statements later on.261bool has_invoke =262(symbol ? symbol->GetName().GetStringRef().contains("__invoke") : false);263auto calculate_symbol_context_helper = [](auto &t,264SymbolContextList &sc_list) {265SymbolContext sc;266t->CalculateSymbolContext(&sc);267sc_list.Append(sc);268};269270// Case 2271if (has_invoke) {272SymbolContextList scl;273calculate_symbol_context_helper(symbol, scl);274275return line_entry_helper(target, scl[0], symbol, first_template_parameter,276has_invoke);277}278279// Case 4 or 5280if (symbol && !symbol->GetName().GetStringRef().starts_with("vtable for") &&281!contains_lambda_identifier(first_template_parameter) && !has_invoke) {282optional_info.callable_case =283LibCppStdFunctionCallableCase::FreeOrMemberFunction;284optional_info.callable_address = function_address_resolved;285optional_info.callable_symbol = *symbol;286287return optional_info;288}289290std::string func_to_match = first_template_parameter.str();291292auto it = CallableLookupCache.find(func_to_match);293if (it != CallableLookupCache.end())294return it->second;295296SymbolContextList scl;297298CompileUnit *vtable_cu =299vtable_first_entry_resolved.CalculateSymbolContextCompileUnit();300llvm::StringRef name_to_use = func_to_match;301302// Case 3, we have a callable object instead of a lambda303//304// TODO305// We currently don't support this case a callable object may have multiple306// operator()() varying on const/non-const and number of arguments and we307// don't have a way to currently distinguish them so we will bail out now.308if (!contains_lambda_identifier(name_to_use))309return optional_info;310311if (vtable_cu && !has_invoke) {312lldb::FunctionSP func_sp =313vtable_cu->FindFunction([name_to_use](const FunctionSP &f) {314auto name = f->GetName().GetStringRef();315if (name.starts_with(name_to_use) && name.contains("operator"))316return true;317318return false;319});320321if (func_sp) {322calculate_symbol_context_helper(func_sp, scl);323}324}325326if (symbol == nullptr)327return optional_info;328329// Case 1 or 3330if (scl.GetSize() >= 1) {331optional_info = line_entry_helper(target, scl[0], symbol,332first_template_parameter, has_invoke);333}334335CallableLookupCache[func_to_match] = optional_info;336337return optional_info;338}339340lldb::ThreadPlanSP341CPPLanguageRuntime::GetStepThroughTrampolinePlan(Thread &thread,342bool stop_others) {343ThreadPlanSP ret_plan_sp;344345lldb::addr_t curr_pc = thread.GetRegisterContext()->GetPC();346347TargetSP target_sp(thread.CalculateTarget());348349if (target_sp->GetSectionLoadList().IsEmpty())350return ret_plan_sp;351352Address pc_addr_resolved;353SymbolContext sc;354Symbol *symbol;355356if (!target_sp->GetSectionLoadList().ResolveLoadAddress(curr_pc,357pc_addr_resolved))358return ret_plan_sp;359360target_sp->GetImages().ResolveSymbolContextForAddress(361pc_addr_resolved, eSymbolContextEverything, sc);362symbol = sc.symbol;363364if (symbol == nullptr)365return ret_plan_sp;366367llvm::StringRef function_name(symbol->GetName().GetCString());368369// Handling the case where we are attempting to step into std::function.370// The behavior will be that we will attempt to obtain the wrapped371// callable via FindLibCppStdFunctionCallableInfo() and if we find it we372// will return a ThreadPlanRunToAddress to the callable. Therefore we will373// step into the wrapped callable.374//375bool found_expected_start_string =376function_name.starts_with("std::__1::function<");377378if (!found_expected_start_string)379return ret_plan_sp;380381AddressRange range_of_curr_func;382sc.GetAddressRange(eSymbolContextEverything, 0, false, range_of_curr_func);383384StackFrameSP frame = thread.GetStackFrameAtIndex(0);385386if (frame) {387ValueObjectSP value_sp = frame->FindVariable(g_this);388389CPPLanguageRuntime::LibCppStdFunctionCallableInfo callable_info =390FindLibCppStdFunctionCallableInfo(value_sp);391392if (callable_info.callable_case != LibCppStdFunctionCallableCase::Invalid &&393value_sp->GetValueIsValid()) {394// We found the std::function wrapped callable and we have its address.395// We now create a ThreadPlan to run to the callable.396ret_plan_sp = std::make_shared<ThreadPlanRunToAddress>(397thread, callable_info.callable_address, stop_others);398return ret_plan_sp;399} else {400// We are in std::function but we could not obtain the callable.401// We create a ThreadPlan to keep stepping through using the address range402// of the current function.403ret_plan_sp = std::make_shared<ThreadPlanStepInRange>(404thread, range_of_curr_func, sc, nullptr, eOnlyThisThread,405eLazyBoolYes, eLazyBoolYes);406return ret_plan_sp;407}408}409410return ret_plan_sp;411}412413414