diff options
author | 2020-08-03 14:33:06 +0000 | |
---|---|---|
committer | 2020-08-03 14:33:06 +0000 | |
commit | 061da546b983eb767bad15e67af1174fb0bcf31c (patch) | |
tree | 83c78b820819d70aa40c36d90447978b300078c5 /gnu/llvm/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp | |
parent | Import LLVM 10.0.0 release including clang, lld and lldb. (diff) | |
download | wireguard-openbsd-061da546b983eb767bad15e67af1174fb0bcf31c.tar.xz wireguard-openbsd-061da546b983eb767bad15e67af1174fb0bcf31c.zip |
Import LLVM 10.0.0 release including clang, lld and lldb.
ok hackroom
tested by plenty
Diffstat (limited to 'gnu/llvm/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp')
-rw-r--r-- | gnu/llvm/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp | 410 |
1 files changed, 410 insertions, 0 deletions
diff --git a/gnu/llvm/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp b/gnu/llvm/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp new file mode 100644 index 00000000000..d556aae1c45 --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp @@ -0,0 +1,410 @@ +//===-- 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/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::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; +} + +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; +} |