diff --git a/lldb/include/lldb/Core/PluginManager.h b/lldb/include/lldb/Core/PluginManager.h --- a/lldb/include/lldb/Core/PluginManager.h +++ b/lldb/include/lldb/Core/PluginManager.h @@ -444,6 +444,15 @@ static bool CreateSettingForStructuredDataPlugin( Debugger &debugger, const lldb::OptionValuePropertiesSP &properties_sp, ConstString description, bool is_global_property); + + // DWARFEvaluator + static bool RegisterPlugin(ConstString name, const char *description, + DWARFEvaluatorCreateInstance create_callback); + + static bool UnregisterPlugin(DWARFEvaluatorCreateInstance create_callback); + + static DWARFEvaluatorCreateInstance + GetDWARFEvaluatorCreateCallbackAtIndex(uint32_t idx); }; } // namespace lldb_private diff --git a/lldb/include/lldb/Expression/DWARFEvaluator.h b/lldb/include/lldb/Expression/DWARFEvaluator.h new file mode 100644 --- /dev/null +++ b/lldb/include/lldb/Expression/DWARFEvaluator.h @@ -0,0 +1,53 @@ +//===-- DWARFEvaluator.h ----------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_EXPRESSION_DWARFEVALUATOR_H +#define LLDB_EXPRESSION_DWARFEVALUATOR_H + +#include "lldb/Core/PluginInterface.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/lldb-private.h" +#include + +class DWARFUnit; + +namespace lldb_private { + +/// \class DWARFEvaluator DWARFEvaluator.h +/// "lldb/Expression/DWARFEvaluator.h" Evaluates DWARF opcodes. +/// +class DWARFEvaluator : public PluginInterface { +public: + DWARFEvaluator() {} + + static std::unique_ptr + FindPlugin(const lldb::ModuleSP &module_sp); + + // PluginInterface protocol + ConstString GetPluginName() override; + + uint32_t GetPluginVersion() override { return 1; } + + // DWARFEvaluator + virtual bool Evaluate(uint8_t op, ExecutionContext *exe_ctx, + RegisterContext *reg_ctx, Process *process, + StackFrame *frame, std::vector &stack, + lldb::ModuleSP module_sp, const DWARFUnit *dwarf_cu, + const lldb::RegisterKind reg_kind, + const DataExtractor &opcodes, lldb::offset_t &offset, + Value &pieces, uint64_t &op_piece_offset, + const Value *object_address_ptr, Log *log, + Value &result, Status *error_ptr); + +private: + DISALLOW_COPY_AND_ASSIGN(DWARFEvaluator); +}; + +} // namespace lldb_private + +#endif // LLDB_EXPRESSION_DWARFEVALUATOR_H diff --git a/lldb/include/lldb/Expression/DWARFExpression.h b/lldb/include/lldb/Expression/DWARFExpression.h --- a/lldb/include/lldb/Expression/DWARFExpression.h +++ b/lldb/include/lldb/Expression/DWARFExpression.h @@ -227,6 +227,9 @@ bool MatchesOperand(StackFrame &frame, const Instruction::Operand &op); + static lldb::addr_t ReadAddressFromDebugAddrSection(const DWARFUnit *dwarf_cu, + uint32_t index); + private: /// Pretty-prints the location expression to a stream /// diff --git a/lldb/include/lldb/lldb-forward.h b/lldb/include/lldb/lldb-forward.h --- a/lldb/include/lldb/lldb-forward.h +++ b/lldb/include/lldb/lldb-forward.h @@ -74,6 +74,7 @@ class DumpValueObjectOptions; class DynamicCheckerFunctions; class DynamicLoader; +class DWARFEvaluator; class Editline; class EmulateInstruction; class Environment; diff --git a/lldb/include/lldb/lldb-private-interfaces.h b/lldb/include/lldb/lldb-private-interfaces.h --- a/lldb/include/lldb/lldb-private-interfaces.h +++ b/lldb/include/lldb/lldb-private-interfaces.h @@ -104,6 +104,8 @@ const char *repl_options); typedef int (*ComparisonFunction)(const void *, const void *); typedef void (*DebuggerInitializeCallback)(Debugger &debugger); +typedef DWARFEvaluator *(*DWARFEvaluatorCreateInstance)( + const lldb::ModuleSP &module_sp); } // namespace lldb_private diff --git a/lldb/source/Core/PluginManager.cpp b/lldb/source/Core/PluginManager.cpp --- a/lldb/source/Core/PluginManager.cpp +++ b/lldb/source/Core/PluginManager.cpp @@ -1464,3 +1464,30 @@ ConstString("Settings for structured data plug-ins"), properties_sp, description, is_global_property); } + +#pragma mark DWARFEvaluator + +typedef PluginInstance DWARFEvaluatorInstance; +typedef PluginInstances DWARFEvaluatorInstances; + +static DWARFEvaluatorInstances &GetDWARFEvaluatorInstances() { + static DWARFEvaluatorInstances g_instances; + return g_instances; +} + +bool PluginManager::RegisterPlugin( + ConstString name, const char *description, + DWARFEvaluatorCreateInstance create_callback) { + return GetDWARFEvaluatorInstances().RegisterPlugin(name, description, + create_callback); +} + +bool PluginManager::UnregisterPlugin( + DWARFEvaluatorCreateInstance create_callback) { + return GetDWARFEvaluatorInstances().UnregisterPlugin(create_callback); +} + +DWARFEvaluatorCreateInstance +PluginManager::GetDWARFEvaluatorCreateCallbackAtIndex(uint32_t idx) { + return GetDWARFEvaluatorInstances().GetCallbackAtIndex(idx); +} diff --git a/lldb/source/Core/Value.cpp b/lldb/source/Core/Value.cpp --- a/lldb/source/Core/Value.cpp +++ b/lldb/source/Core/Value.cpp @@ -30,6 +30,8 @@ #include "lldb/lldb-forward.h" #include "lldb/lldb-types.h" +#include "Plugins/Process/wasm/WasmProcess.h" + #include #include @@ -563,8 +565,26 @@ Process *process = exe_ctx->GetProcessPtr(); if (process) { - const size_t bytes_read = - process->ReadMemory(address, dst, byte_size, error); + StackFrame *frame = exe_ctx->GetFramePtr(); + size_t bytes_read = 0; + + bool isWasm = + frame + ? (frame->CalculateTarget()->GetArchitecture().GetMachine() == + llvm::Triple::wasm32) + : false; + if (isWasm) { + int frame_index = frame->GetConcreteFrameIndex(); + wasm::WasmProcess *wasm_process = static_cast( + frame->CalculateProcess().get()); + if (wasm_process->WasmReadMemory(frame_index, address, dst, + byte_size)) { + bytes_read = byte_size; + } + } else { + bytes_read = process->ReadMemory(address, dst, byte_size, error); + } + if (bytes_read != byte_size) error.SetErrorStringWithFormat( "read memory from 0x%" PRIx64 " failed (%u of %u bytes read)", diff --git a/lldb/source/Expression/CMakeLists.txt b/lldb/source/Expression/CMakeLists.txt --- a/lldb/source/Expression/CMakeLists.txt +++ b/lldb/source/Expression/CMakeLists.txt @@ -4,6 +4,7 @@ add_lldb_library(lldbExpression DiagnosticManager.cpp + DWARFEvaluator.cpp DWARFExpression.cpp Expression.cpp ExpressionVariable.cpp diff --git a/lldb/source/Expression/DWARFEvaluator.cpp b/lldb/source/Expression/DWARFEvaluator.cpp new file mode 100644 --- /dev/null +++ b/lldb/source/Expression/DWARFEvaluator.cpp @@ -0,0 +1,1907 @@ +//===-- DWARFEvaluator.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 "lldb/Expression/DWARFEvaluator.h" +#include "lldb/Expression/DWARFExpression.h" + +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/dwarf.h" + +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegisterValue.h" + +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StackFrame.h" + +#include "Plugins/SymbolFile/DWARF/DWARFUnit.h" + +using namespace lldb; +using namespace lldb_private; + +// PluginInterface protocol +lldb_private::ConstString DWARFEvaluator::GetPluginName() { + static ConstString g_name("vendor-default"); + return g_name; +} + +// FindPlugin +// +// Platforms can register a callback to use when creating DWARF expression +// evaluators to allow handling platform-specific DWARF codes. +std::unique_ptr +DWARFEvaluator::FindPlugin(const lldb::ModuleSP &module_sp) { + std::unique_ptr instance_up; + DWARFEvaluatorCreateInstance create_callback; + + for (size_t idx = 0; + (create_callback = PluginManager::GetDWARFEvaluatorCreateCallbackAtIndex( + idx)) != nullptr; + ++idx) { + instance_up.reset(create_callback(module_sp)); + + if (instance_up) { + return instance_up; + } + } + + instance_up.reset(new DWARFEvaluator()); + return instance_up; +} + +static bool ReadRegisterValueAsScalar(RegisterContext *reg_ctx, + lldb::RegisterKind reg_kind, + uint32_t reg_num, Status *error_ptr, + Value &value) { + if (reg_ctx == nullptr) { + if (error_ptr) + error_ptr->SetErrorStringWithFormat("No register context in frame.\n"); + } else { + uint32_t native_reg = + reg_ctx->ConvertRegisterKindToRegisterNumber(reg_kind, reg_num); + if (native_reg == LLDB_INVALID_REGNUM) { + if (error_ptr) + error_ptr->SetErrorStringWithFormat("Unable to convert register " + "kind=%u reg_num=%u to a native " + "register number.\n", + reg_kind, reg_num); + } else { + const RegisterInfo *reg_info = + reg_ctx->GetRegisterInfoAtIndex(native_reg); + RegisterValue reg_value; + if (reg_ctx->ReadRegister(reg_info, reg_value)) { + if (reg_value.GetScalarValue(value.GetScalar())) { + value.SetValueType(Value::eValueTypeScalar); + value.SetContext(Value::eContextTypeRegisterInfo, + const_cast(reg_info)); + if (error_ptr) + error_ptr->Clear(); + return true; + } else { + // If we get this error, then we need to implement a value buffer in + // the dwarf expression evaluation function... + if (error_ptr) + error_ptr->SetErrorStringWithFormat( + "register %s can't be converted to a scalar value", + reg_info->name); + } + } else { + if (error_ptr) + error_ptr->SetErrorStringWithFormat("register %s is not available", + reg_info->name); + } + } + } + return false; +} + +static bool Evaluate_DW_OP_entry_value(std::vector &stack, + ExecutionContext *exe_ctx, + RegisterContext *reg_ctx, + const DataExtractor &opcodes, + lldb::offset_t &opcode_offset, + Status *error_ptr, Log *log) { + // DW_OP_entry_value(sub-expr) describes the location a variable had upon + // function entry: this variable location is presumed to be optimized out at + // the current PC value. The caller of the function may have call site + // information that describes an alternate location for the variable (e.g. a + // constant literal, or a spilled stack value) in the parent frame. + // + // Example (this is pseudo-code & pseudo-DWARF, but hopefully illustrative): + // + // void child(int &sink, int x) { + // ... + // /* "x" gets optimized out. */ + // + // /* The location of "x" here is: DW_OP_entry_value($reg2). */ + // ++sink; + // } + // + // void parent() { + // int sink; + // + // /* + // * The callsite information emitted here is: + // * + // * DW_TAG_call_site + // * DW_AT_return_pc ... (for "child(sink, 123);") + // * DW_TAG_call_site_parameter (for "sink") + // * DW_AT_location ($reg1) + // * DW_AT_call_value ($SP - 8) + // * DW_TAG_call_site_parameter (for "x") + // * DW_AT_location ($reg2) + // * DW_AT_call_value ($literal 123) + // * + // * DW_TAG_call_site + // * DW_AT_return_pc ... (for "child(sink, 456);") + // * ... + // */ + // child(sink, 123); + // child(sink, 456); + // } + // + // When the program stops at "++sink" within `child`, the debugger determines + // the call site by analyzing the return address. Once the call site is found, + // the debugger determines which parameter is referenced by DW_OP_entry_value + // and evaluates the corresponding location for that parameter in `parent`. + + // 1. Find the function which pushed the current frame onto the stack. + if ((!exe_ctx || !exe_ctx->HasTargetScope()) || !reg_ctx) { + LLDB_LOG(log, "Evaluate_DW_OP_entry_value: no exe/reg context"); + return false; + } + + StackFrame *current_frame = exe_ctx->GetFramePtr(); + Thread *thread = exe_ctx->GetThreadPtr(); + if (!current_frame || !thread) { + LLDB_LOG(log, "Evaluate_DW_OP_entry_value: no current frame/thread"); + return false; + } + + Target &target = exe_ctx->GetTargetRef(); + StackFrameSP parent_frame = nullptr; + addr_t return_pc = LLDB_INVALID_ADDRESS; + uint32_t current_frame_idx = current_frame->GetFrameIndex(); + uint32_t num_frames = thread->GetStackFrameCount(); + for (uint32_t parent_frame_idx = current_frame_idx + 1; + parent_frame_idx < num_frames; ++parent_frame_idx) { + parent_frame = thread->GetStackFrameAtIndex(parent_frame_idx); + // Require a valid sequence of frames. + if (!parent_frame) + break; + + // Record the first valid return address, even if this is an inlined frame, + // in order to look up the associated call edge in the first non-inlined + // parent frame. + if (return_pc == LLDB_INVALID_ADDRESS) { + return_pc = parent_frame->GetFrameCodeAddress().GetLoadAddress(&target); + LLDB_LOG(log, + "Evaluate_DW_OP_entry_value: immediate ancestor with pc = {0:x}", + return_pc); + } + + // If we've found an inlined frame, skip it (these have no call site + // parameters). + if (parent_frame->IsInlined()) + continue; + + // We've found the first non-inlined parent frame. + break; + } + if (!parent_frame || !parent_frame->GetRegisterContext()) { + LLDB_LOG(log, "Evaluate_DW_OP_entry_value: no parent frame with reg ctx"); + return false; + } + + Function *parent_func = + parent_frame->GetSymbolContext(eSymbolContextFunction).function; + if (!parent_func) { + LLDB_LOG(log, "Evaluate_DW_OP_entry_value: no parent function"); + return false; + } + + // 2. Find the call edge in the parent function responsible for creating the + // current activation. + Function *current_func = + current_frame->GetSymbolContext(eSymbolContextFunction).function; + if (!current_func) { + LLDB_LOG(log, "Evaluate_DW_OP_entry_value: no current function"); + return false; + } + + CallEdge *call_edge = nullptr; + ModuleList &modlist = target.GetImages(); + ExecutionContext parent_exe_ctx = *exe_ctx; + parent_exe_ctx.SetFrameSP(parent_frame); + if (!parent_frame->IsArtificial()) { + // If the parent frame is not artificial, the current activation may be + // produced by an ambiguous tail call. In this case, refuse to proceed. + call_edge = parent_func->GetCallEdgeForReturnAddress(return_pc, target); + if (!call_edge) { + LLDB_LOG(log, + "Evaluate_DW_OP_entry_value: no call edge for retn-pc = {0:x} " + "in parent frame {1}", + return_pc, parent_func->GetName()); + return false; + } + Function *callee_func = call_edge->GetCallee(modlist, parent_exe_ctx); + if (callee_func != current_func) { + LLDB_LOG(log, "Evaluate_DW_OP_entry_value: ambiguous call sequence, " + "can't find real parent frame"); + return false; + } + } else { + // The StackFrameList solver machinery has deduced that an unambiguous tail + // call sequence that produced the current activation. The first edge in + // the parent that points to the current function must be valid. + for (auto &edge : parent_func->GetTailCallingEdges()) { + if (edge->GetCallee(modlist, parent_exe_ctx) == current_func) { + call_edge = edge.get(); + break; + } + } + } + if (!call_edge) { + LLDB_LOG(log, "Evaluate_DW_OP_entry_value: no unambiguous edge from parent " + "to current function"); + return false; + } + + // 3. Attempt to locate the DW_OP_entry_value expression in the set of + // available call site parameters. If found, evaluate the corresponding + // parameter in the context of the parent frame. + const uint32_t subexpr_len = opcodes.GetULEB128(&opcode_offset); + const void *subexpr_data = opcodes.GetData(&opcode_offset, subexpr_len); + if (!subexpr_data) { + LLDB_LOG(log, "Evaluate_DW_OP_entry_value: subexpr could not be read"); + return false; + } + + const CallSiteParameter *matched_param = nullptr; + for (const CallSiteParameter ¶m : call_edge->GetCallSiteParameters()) { + DataExtractor param_subexpr_extractor; + if (!param.LocationInCallee.GetExpressionData(param_subexpr_extractor)) + continue; + lldb::offset_t param_subexpr_offset = 0; + const void *param_subexpr_data = + param_subexpr_extractor.GetData(¶m_subexpr_offset, subexpr_len); + if (!param_subexpr_data || + param_subexpr_extractor.BytesLeft(param_subexpr_offset) != 0) + continue; + + // At this point, the DW_OP_entry_value sub-expression and the callee-side + // expression in the call site parameter are known to have the same length. + // Check whether they are equal. + // + // Note that an equality check is sufficient: the contents of the + // DW_OP_entry_value subexpression are only used to identify the right call + // site parameter in the parent, and do not require any special handling. + if (memcmp(subexpr_data, param_subexpr_data, subexpr_len) == 0) { + matched_param = ¶m; + break; + } + } + if (!matched_param) { + LLDB_LOG(log, + "Evaluate_DW_OP_entry_value: no matching call site param found"); + return false; + } + + // TODO: Add support for DW_OP_push_object_address within a DW_OP_entry_value + // subexpresion whenever llvm does. + Value result; + const DWARFExpression ¶m_expr = matched_param->LocationInCaller; + if (!param_expr.Evaluate(&parent_exe_ctx, + parent_frame->GetRegisterContext().get(), + /*loclist_base_addr=*/LLDB_INVALID_ADDRESS, + /*initial_value_ptr=*/nullptr, + /*object_address_ptr=*/nullptr, result, error_ptr)) { + LLDB_LOG(log, + "Evaluate_DW_OP_entry_value: call site param evaluation failed"); + return false; + } + + stack.push_back(result); + return true; +} + +bool DWARFEvaluator::Evaluate( + const uint8_t op, ExecutionContext *exe_ctx, RegisterContext *reg_ctx, + Process *process, StackFrame *frame, std::vector &stack, + lldb::ModuleSP module_sp, const DWARFUnit *dwarf_cu, + const lldb::RegisterKind reg_kind, const DataExtractor &opcodes, + lldb::offset_t &offset, Value &pieces, uint64_t &op_piece_offset, + const Value *object_address_ptr, Log *log, Value &result, + Status *error_ptr) { + Value tmp; + uint32_t reg_num; + + switch (op) { + case DW_OP_WASM_location: + assert(false); + break; + + // The DW_OP_addr operation has a single operand that encodes a machine + // address and whose size is the size of an address on the target machine. + case DW_OP_addr: + stack.push_back(Scalar(opcodes.GetAddress(&offset))); + stack.back().SetValueType(Value::eValueTypeFileAddress); + // Convert the file address to a load address, so subsequent + // DWARF operators can operate on it. + if (frame) + stack.back().ConvertToLoadAddress(module_sp.get(), + frame->CalculateTarget().get()); + break; + + // The DW_OP_addr_sect_offset4 is used for any location expressions in + // shared libraries that have a location like: + // DW_OP_addr(0x1000) + // If this address resides in a shared library, then this virtual address + // won't make sense when it is evaluated in the context of a running + // process where shared libraries have been slid. To account for this, this + // new address type where we can store the section pointer and a 4 byte + // offset. + // case DW_OP_addr_sect_offset4: + // { + // result_type = eResultTypeFileAddress; + // lldb::Section *sect = (lldb::Section + // *)opcodes.GetMaxU64(&offset, sizeof(void *)); + // lldb::addr_t sect_offset = opcodes.GetU32(&offset); + // + // Address so_addr (sect, sect_offset); + // lldb::addr_t load_addr = so_addr.GetLoadAddress(); + // if (load_addr != LLDB_INVALID_ADDRESS) + // { + // // We successfully resolve a file address to a load + // // address. + // stack.push_back(load_addr); + // break; + // } + // else + // { + // // We were able + // if (error_ptr) + // error_ptr->SetErrorStringWithFormat ("Section %s in + // %s is not currently loaded.\n", + // sect->GetName().AsCString(), + // sect->GetModule()->GetFileSpec().GetFilename().AsCString()); + // return false; + // } + // } + // break; + + // OPCODE: DW_OP_deref + // OPERANDS: none + // DESCRIPTION: Pops the top stack entry and treats it as an address. + // The value retrieved from that address is pushed. The size of the data + // retrieved from the dereferenced address is the size of an address on the + // target machine. + case DW_OP_deref: { + if (stack.empty()) { + if (error_ptr) + error_ptr->SetErrorString("Expression stack empty for DW_OP_deref."); + return false; + } + Value::ValueType value_type = stack.back().GetValueType(); + switch (value_type) { + case Value::eValueTypeHostAddress: { + void *src = (void *)stack.back().GetScalar().ULongLong(); + intptr_t ptr; + ::memcpy(&ptr, src, sizeof(void *)); + stack.back().GetScalar() = ptr; + stack.back().ClearContext(); + } break; + case Value::eValueTypeFileAddress: { + auto file_addr = stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS); + if (!module_sp) { + if (error_ptr) + error_ptr->SetErrorStringWithFormat( + "need module to resolve file address for DW_OP_deref"); + return false; + } + Address so_addr; + if (!module_sp->ResolveFileAddress(file_addr, so_addr)) { + if (error_ptr) + error_ptr->SetErrorStringWithFormat( + "failed to resolve file address in module"); + return false; + } + addr_t load_Addr = so_addr.GetLoadAddress(exe_ctx->GetTargetPtr()); + if (load_Addr == LLDB_INVALID_ADDRESS) { + if (error_ptr) + error_ptr->SetErrorStringWithFormat("failed to resolve load address"); + return false; + } + stack.back().GetScalar() = load_Addr; + stack.back().SetValueType(Value::eValueTypeLoadAddress); + // Fall through to load address code below... + } + LLVM_FALLTHROUGH; + case Value::eValueTypeLoadAddress: + if (exe_ctx) { + if (process) { + lldb::addr_t pointer_addr = + stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS); + Status error; + lldb::addr_t pointer_value = + process->ReadPointerFromMemory(pointer_addr, error); + if (pointer_value != LLDB_INVALID_ADDRESS) { + stack.back().GetScalar() = pointer_value; + stack.back().ClearContext(); + } else { + if (error_ptr) + error_ptr->SetErrorStringWithFormat( + "Failed to dereference pointer from 0x%" PRIx64 + " for DW_OP_deref: %s\n", + pointer_addr, error.AsCString()); + return false; + } + } else { + if (error_ptr) + error_ptr->SetErrorStringWithFormat( + "NULL process for DW_OP_deref.\n"); + return false; + } + } else { + if (error_ptr) + error_ptr->SetErrorStringWithFormat( + "NULL execution context for DW_OP_deref.\n"); + return false; + } + break; + + default: + break; + } + + } break; + + // OPCODE: DW_OP_deref_size + // OPERANDS: 1 + // 1 - uint8_t that specifies the size of the data to dereference. + // DESCRIPTION: Behaves like the DW_OP_deref operation: it pops the top + // stack entry and treats it as an address. The value retrieved from that + // address is pushed. In the DW_OP_deref_size operation, however, the size + // in bytes of the data retrieved from the dereferenced address is + // specified by the single operand. This operand is a 1-byte unsigned + // integral constant whose value may not be larger than the size of an + // address on the target machine. The data retrieved is zero extended to + // the size of an address on the target machine before being pushed on the + // expression stack. + case DW_OP_deref_size: { + if (stack.empty()) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack empty for DW_OP_deref_size."); + return false; + } + uint8_t size = opcodes.GetU8(&offset); + Value::ValueType value_type = stack.back().GetValueType(); + switch (value_type) { + case Value::eValueTypeHostAddress: { + void *src = (void *)stack.back().GetScalar().ULongLong(); + intptr_t ptr; + ::memcpy(&ptr, src, sizeof(void *)); + // I can't decide whether the size operand should apply to the bytes in + // their + // lldb-host endianness or the target endianness.. I doubt this'll ever + // come up but I'll opt for assuming big endian regardless. + switch (size) { + case 1: + ptr = ptr & 0xff; + break; + case 2: + ptr = ptr & 0xffff; + break; + case 3: + ptr = ptr & 0xffffff; + break; + case 4: + ptr = ptr & 0xffffffff; + break; + // the casts are added to work around the case where intptr_t is a 32 + // bit quantity; + // presumably we won't hit the 5..7 cases if (void*) is 32-bits in this + // program. + case 5: + ptr = (intptr_t)ptr & 0xffffffffffULL; + break; + case 6: + ptr = (intptr_t)ptr & 0xffffffffffffULL; + break; + case 7: + ptr = (intptr_t)ptr & 0xffffffffffffffULL; + break; + default: + break; + } + stack.back().GetScalar() = ptr; + stack.back().ClearContext(); + } break; + case Value::eValueTypeLoadAddress: + if (exe_ctx) { + if (process) { + lldb::addr_t pointer_addr = + stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS); + uint8_t addr_bytes[sizeof(lldb::addr_t)]; + Status error; + if (process->ReadMemory(pointer_addr, &addr_bytes, size, error) == + size) { + DataExtractor addr_data(addr_bytes, sizeof(addr_bytes), + process->GetByteOrder(), size); + lldb::offset_t addr_data_offset = 0; + switch (size) { + case 1: + stack.back().GetScalar() = addr_data.GetU8(&addr_data_offset); + break; + case 2: + stack.back().GetScalar() = addr_data.GetU16(&addr_data_offset); + break; + case 4: + stack.back().GetScalar() = addr_data.GetU32(&addr_data_offset); + break; + case 8: + stack.back().GetScalar() = addr_data.GetU64(&addr_data_offset); + break; + default: + stack.back().GetScalar() = + addr_data.GetAddress(&addr_data_offset); + } + stack.back().ClearContext(); + } else { + if (error_ptr) + error_ptr->SetErrorStringWithFormat( + "Failed to dereference pointer from 0x%" PRIx64 + " for DW_OP_deref: %s\n", + pointer_addr, error.AsCString()); + return false; + } + } else { + if (error_ptr) + error_ptr->SetErrorStringWithFormat( + "NULL process for DW_OP_deref.\n"); + return false; + } + } else { + if (error_ptr) + error_ptr->SetErrorStringWithFormat( + "NULL execution context for DW_OP_deref.\n"); + return false; + } + break; + + default: + break; + } + + } break; + + // OPCODE: DW_OP_xderef_size + // OPERANDS: 1 + // 1 - uint8_t that specifies the size of the data to dereference. + // DESCRIPTION: Behaves like the DW_OP_xderef operation: the entry at + // the top of the stack is treated as an address. The second stack entry is + // treated as an "address space identifier" for those architectures that + // support multiple address spaces. The top two stack elements are popped, + // a data item is retrieved through an implementation-defined address + // calculation and pushed as the new stack top. In the DW_OP_xderef_size + // operation, however, the size in bytes of the data retrieved from the + // dereferenced address is specified by the single operand. This operand is + // a 1-byte unsigned integral constant whose value may not be larger than + // the size of an address on the target machine. The data retrieved is zero + // extended to the size of an address on the target machine before being + // pushed on the expression stack. + case DW_OP_xderef_size: + if (error_ptr) + error_ptr->SetErrorString("Unimplemented opcode: DW_OP_xderef_size."); + return false; + // OPCODE: DW_OP_xderef + // OPERANDS: none + // DESCRIPTION: Provides an extended dereference mechanism. The entry at + // the top of the stack is treated as an address. The second stack entry is + // treated as an "address space identifier" for those architectures that + // support multiple address spaces. The top two stack elements are popped, + // a data item is retrieved through an implementation-defined address + // calculation and pushed as the new stack top. The size of the data + // retrieved from the dereferenced address is the size of an address on the + // target machine. + case DW_OP_xderef: + if (error_ptr) + error_ptr->SetErrorString("Unimplemented opcode: DW_OP_xderef."); + return false; + + // All DW_OP_constXXX opcodes have a single operand as noted below: + // + // Opcode Operand 1 + // DW_OP_const1u 1-byte unsigned integer constant DW_OP_const1s + // 1-byte signed integer constant DW_OP_const2u 2-byte unsigned integer + // constant DW_OP_const2s 2-byte signed integer constant DW_OP_const4u + // 4-byte unsigned integer constant DW_OP_const4s 4-byte signed integer + // constant DW_OP_const8u 8-byte unsigned integer constant DW_OP_const8s + // 8-byte signed integer constant DW_OP_constu unsigned LEB128 integer + // constant DW_OP_consts signed LEB128 integer constant + case DW_OP_const1u: + stack.push_back(Scalar((uint8_t)opcodes.GetU8(&offset))); + break; + case DW_OP_const1s: + stack.push_back(Scalar((int8_t)opcodes.GetU8(&offset))); + break; + case DW_OP_const2u: + stack.push_back(Scalar((uint16_t)opcodes.GetU16(&offset))); + break; + case DW_OP_const2s: + stack.push_back(Scalar((int16_t)opcodes.GetU16(&offset))); + break; + case DW_OP_const4u: + stack.push_back(Scalar((uint32_t)opcodes.GetU32(&offset))); + break; + case DW_OP_const4s: + stack.push_back(Scalar((int32_t)opcodes.GetU32(&offset))); + break; + case DW_OP_const8u: + stack.push_back(Scalar((uint64_t)opcodes.GetU64(&offset))); + break; + case DW_OP_const8s: + stack.push_back(Scalar((int64_t)opcodes.GetU64(&offset))); + break; + case DW_OP_constu: + stack.push_back(Scalar(opcodes.GetULEB128(&offset))); + break; + case DW_OP_consts: + stack.push_back(Scalar(opcodes.GetSLEB128(&offset))); + break; + + // OPCODE: DW_OP_dup + // OPERANDS: none + // DESCRIPTION: duplicates the value at the top of the stack + case DW_OP_dup: + if (stack.empty()) { + if (error_ptr) + error_ptr->SetErrorString("Expression stack empty for DW_OP_dup."); + return false; + } else + stack.push_back(stack.back()); + break; + + // OPCODE: DW_OP_drop + // OPERANDS: none + // DESCRIPTION: pops the value at the top of the stack + case DW_OP_drop: + if (stack.empty()) { + if (error_ptr) + error_ptr->SetErrorString("Expression stack empty for DW_OP_drop."); + return false; + } else + stack.pop_back(); + break; + + // OPCODE: DW_OP_over + // OPERANDS: none + // DESCRIPTION: Duplicates the entry currently second in the stack at + // the top of the stack. + case DW_OP_over: + if (stack.size() < 2) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 2 items for DW_OP_over."); + return false; + } else + stack.push_back(stack[stack.size() - 2]); + break; + + // OPCODE: DW_OP_pick + // OPERANDS: uint8_t index into the current stack + // DESCRIPTION: The stack entry with the specified index (0 through 255, + // inclusive) is pushed on the stack + case DW_OP_pick: { + uint8_t pick_idx = opcodes.GetU8(&offset); + if (pick_idx < stack.size()) + stack.push_back(stack[stack.size() - 1 - pick_idx]); + else { + if (error_ptr) + error_ptr->SetErrorStringWithFormat( + "Index %u out of range for DW_OP_pick.\n", pick_idx); + return false; + } + } break; + + // OPCODE: DW_OP_swap + // OPERANDS: none + // DESCRIPTION: swaps the top two stack entries. The entry at the top + // of the stack becomes the second stack entry, and the second entry + // becomes the top of the stack + case DW_OP_swap: + if (stack.size() < 2) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 2 items for DW_OP_swap."); + return false; + } else { + tmp = stack.back(); + stack.back() = stack[stack.size() - 2]; + stack[stack.size() - 2] = tmp; + } + break; + + // OPCODE: DW_OP_rot + // OPERANDS: none + // DESCRIPTION: Rotates the first three stack entries. The entry at + // the top of the stack becomes the third stack entry, the second entry + // becomes the top of the stack, and the third entry becomes the second + // entry. + case DW_OP_rot: + if (stack.size() < 3) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 3 items for DW_OP_rot."); + return false; + } else { + size_t last_idx = stack.size() - 1; + Value old_top = stack[last_idx]; + stack[last_idx] = stack[last_idx - 1]; + stack[last_idx - 1] = stack[last_idx - 2]; + stack[last_idx - 2] = old_top; + } + break; + + // OPCODE: DW_OP_abs + // OPERANDS: none + // DESCRIPTION: pops the top stack entry, interprets it as a signed + // value and pushes its absolute value. If the absolute value can not be + // represented, the result is undefined. + case DW_OP_abs: + if (stack.empty()) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 1 item for DW_OP_abs."); + return false; + } else if (!stack.back().ResolveValue(exe_ctx).AbsoluteValue()) { + if (error_ptr) + error_ptr->SetErrorString( + "Failed to take the absolute value of the first stack item."); + return false; + } + break; + + // OPCODE: DW_OP_and + // OPERANDS: none + // DESCRIPTION: pops the top two stack values, performs a bitwise and + // operation on the two, and pushes the result. + case DW_OP_and: + if (stack.size() < 2) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 2 items for DW_OP_and."); + return false; + } else { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = + stack.back().ResolveValue(exe_ctx) & tmp.ResolveValue(exe_ctx); + } + break; + + // OPCODE: DW_OP_div + // OPERANDS: none + // DESCRIPTION: pops the top two stack values, divides the former second + // entry by the former top of the stack using signed division, and pushes + // the result. + case DW_OP_div: + if (stack.size() < 2) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 2 items for DW_OP_div."); + return false; + } else { + tmp = stack.back(); + if (tmp.ResolveValue(exe_ctx).IsZero()) { + if (error_ptr) + error_ptr->SetErrorString("Divide by zero."); + return false; + } else { + stack.pop_back(); + stack.back() = + stack.back().ResolveValue(exe_ctx) / tmp.ResolveValue(exe_ctx); + if (!stack.back().ResolveValue(exe_ctx).IsValid()) { + if (error_ptr) + error_ptr->SetErrorString("Divide failed."); + return false; + } + } + } + break; + + // OPCODE: DW_OP_minus + // OPERANDS: none + // DESCRIPTION: pops the top two stack values, subtracts the former top + // of the stack from the former second entry, and pushes the result. + case DW_OP_minus: + if (stack.size() < 2) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 2 items for DW_OP_minus."); + return false; + } else { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = + stack.back().ResolveValue(exe_ctx) - tmp.ResolveValue(exe_ctx); + } + break; + + // OPCODE: DW_OP_mod + // OPERANDS: none + // DESCRIPTION: pops the top two stack values and pushes the result of + // the calculation: former second stack entry modulo the former top of the + // stack. + case DW_OP_mod: + if (stack.size() < 2) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 2 items for DW_OP_mod."); + return false; + } else { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = + stack.back().ResolveValue(exe_ctx) % tmp.ResolveValue(exe_ctx); + } + break; + + // OPCODE: DW_OP_mul + // OPERANDS: none + // DESCRIPTION: pops the top two stack entries, multiplies them + // together, and pushes the result. + case DW_OP_mul: + if (stack.size() < 2) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 2 items for DW_OP_mul."); + return false; + } else { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = + stack.back().ResolveValue(exe_ctx) * tmp.ResolveValue(exe_ctx); + } + break; + + // OPCODE: DW_OP_neg + // OPERANDS: none + // DESCRIPTION: pops the top stack entry, and pushes its negation. + case DW_OP_neg: + if (stack.empty()) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 1 item for DW_OP_neg."); + return false; + } else { + if (!stack.back().ResolveValue(exe_ctx).UnaryNegate()) { + if (error_ptr) + error_ptr->SetErrorString("Unary negate failed."); + return false; + } + } + break; + + // OPCODE: DW_OP_not + // OPERANDS: none + // DESCRIPTION: pops the top stack entry, and pushes its bitwise + // complement + case DW_OP_not: + if (stack.empty()) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 1 item for DW_OP_not."); + return false; + } else { + if (!stack.back().ResolveValue(exe_ctx).OnesComplement()) { + if (error_ptr) + error_ptr->SetErrorString("Logical NOT failed."); + return false; + } + } + break; + + // OPCODE: DW_OP_or + // OPERANDS: none + // DESCRIPTION: pops the top two stack entries, performs a bitwise or + // operation on the two, and pushes the result. + case DW_OP_or: + if (stack.size() < 2) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 2 items for DW_OP_or."); + return false; + } else { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = + stack.back().ResolveValue(exe_ctx) | tmp.ResolveValue(exe_ctx); + } + break; + + // OPCODE: DW_OP_plus + // OPERANDS: none + // DESCRIPTION: pops the top two stack entries, adds them together, and + // pushes the result. + case DW_OP_plus: + if (stack.size() < 2) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 2 items for DW_OP_plus."); + return false; + } else { + tmp = stack.back(); + stack.pop_back(); + stack.back().GetScalar() += tmp.GetScalar(); + } + break; + + // OPCODE: DW_OP_plus_uconst + // OPERANDS: none + // DESCRIPTION: pops the top stack entry, adds it to the unsigned LEB128 + // constant operand and pushes the result. + case DW_OP_plus_uconst: + if (stack.empty()) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 1 item for DW_OP_plus_uconst."); + return false; + } else { + const uint64_t uconst_value = opcodes.GetULEB128(&offset); + // Implicit conversion from a UINT to a Scalar... + stack.back().GetScalar() += uconst_value; + if (!stack.back().GetScalar().IsValid()) { + if (error_ptr) + error_ptr->SetErrorString("DW_OP_plus_uconst failed."); + return false; + } + } + break; + + // OPCODE: DW_OP_shl + // OPERANDS: none + // DESCRIPTION: pops the top two stack entries, shifts the former + // second entry left by the number of bits specified by the former top of + // the stack, and pushes the result. + case DW_OP_shl: + if (stack.size() < 2) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 2 items for DW_OP_shl."); + return false; + } else { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) <<= tmp.ResolveValue(exe_ctx); + } + break; + + // OPCODE: DW_OP_shr + // OPERANDS: none + // DESCRIPTION: pops the top two stack entries, shifts the former second + // entry right logically (filling with zero bits) by the number of bits + // specified by the former top of the stack, and pushes the result. + case DW_OP_shr: + if (stack.size() < 2) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 2 items for DW_OP_shr."); + return false; + } else { + tmp = stack.back(); + stack.pop_back(); + if (!stack.back().ResolveValue(exe_ctx).ShiftRightLogical( + tmp.ResolveValue(exe_ctx))) { + if (error_ptr) + error_ptr->SetErrorString("DW_OP_shr failed."); + return false; + } + } + break; + + // OPCODE: DW_OP_shra + // OPERANDS: none + // DESCRIPTION: pops the top two stack entries, shifts the former second + // entry right arithmetically (divide the magnitude by 2, keep the same + // sign for the result) by the number of bits specified by the former top + // of the stack, and pushes the result. + case DW_OP_shra: + if (stack.size() < 2) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 2 items for DW_OP_shra."); + return false; + } else { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) >>= tmp.ResolveValue(exe_ctx); + } + break; + + // OPCODE: DW_OP_xor + // OPERANDS: none + // DESCRIPTION: pops the top two stack entries, performs the bitwise + // exclusive-or operation on the two, and pushes the result. + case DW_OP_xor: + if (stack.size() < 2) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 2 items for DW_OP_xor."); + return false; + } else { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = + stack.back().ResolveValue(exe_ctx) ^ tmp.ResolveValue(exe_ctx); + } + break; + + // OPCODE: DW_OP_skip + // OPERANDS: int16_t + // DESCRIPTION: An unconditional branch. Its single operand is a 2-byte + // signed integer constant. The 2-byte constant is the number of bytes of + // the DWARF expression to skip forward or backward from the current + // operation, beginning after the 2-byte constant. + case DW_OP_skip: { + int16_t skip_offset = (int16_t)opcodes.GetU16(&offset); + lldb::offset_t new_offset = offset + skip_offset; + if (opcodes.ValidOffset(new_offset)) + offset = new_offset; + else { + if (error_ptr) + error_ptr->SetErrorString("Invalid opcode offset in DW_OP_skip."); + return false; + } + } break; + + // OPCODE: DW_OP_bra + // OPERANDS: int16_t + // DESCRIPTION: A conditional branch. Its single operand is a 2-byte + // signed integer constant. This operation pops the top of stack. If the + // value popped is not the constant 0, the 2-byte constant operand is the + // number of bytes of the DWARF expression to skip forward or backward from + // the current operation, beginning after the 2-byte constant. + case DW_OP_bra: + if (stack.empty()) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 1 item for DW_OP_bra."); + return false; + } else { + tmp = stack.back(); + stack.pop_back(); + int16_t bra_offset = (int16_t)opcodes.GetU16(&offset); + Scalar zero(0); + if (tmp.ResolveValue(exe_ctx) != zero) { + lldb::offset_t new_offset = offset + bra_offset; + if (opcodes.ValidOffset(new_offset)) + offset = new_offset; + else { + if (error_ptr) + error_ptr->SetErrorString("Invalid opcode offset in DW_OP_bra."); + return false; + } + } + } + break; + + // OPCODE: DW_OP_eq + // OPERANDS: none + // DESCRIPTION: pops the top two stack values, compares using the + // equals (==) operator. + // STACK RESULT: push the constant value 1 onto the stack if the result + // of the operation is true or the constant value 0 if the result of the + // operation is false. + case DW_OP_eq: + if (stack.size() < 2) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 2 items for DW_OP_eq."); + return false; + } else { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = + stack.back().ResolveValue(exe_ctx) == tmp.ResolveValue(exe_ctx); + } + break; + + // OPCODE: DW_OP_ge + // OPERANDS: none + // DESCRIPTION: pops the top two stack values, compares using the + // greater than or equal to (>=) operator. + // STACK RESULT: push the constant value 1 onto the stack if the result + // of the operation is true or the constant value 0 if the result of the + // operation is false. + case DW_OP_ge: + if (stack.size() < 2) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 2 items for DW_OP_ge."); + return false; + } else { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = + stack.back().ResolveValue(exe_ctx) >= tmp.ResolveValue(exe_ctx); + } + break; + + // OPCODE: DW_OP_gt + // OPERANDS: none + // DESCRIPTION: pops the top two stack values, compares using the + // greater than (>) operator. + // STACK RESULT: push the constant value 1 onto the stack if the result + // of the operation is true or the constant value 0 if the result of the + // operation is false. + case DW_OP_gt: + if (stack.size() < 2) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 2 items for DW_OP_gt."); + return false; + } else { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = + stack.back().ResolveValue(exe_ctx) > tmp.ResolveValue(exe_ctx); + } + break; + + // OPCODE: DW_OP_le + // OPERANDS: none + // DESCRIPTION: pops the top two stack values, compares using the + // less than or equal to (<=) operator. + // STACK RESULT: push the constant value 1 onto the stack if the result + // of the operation is true or the constant value 0 if the result of the + // operation is false. + case DW_OP_le: + if (stack.size() < 2) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 2 items for DW_OP_le."); + return false; + } else { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = + stack.back().ResolveValue(exe_ctx) <= tmp.ResolveValue(exe_ctx); + } + break; + + // OPCODE: DW_OP_lt + // OPERANDS: none + // DESCRIPTION: pops the top two stack values, compares using the + // less than (<) operator. + // STACK RESULT: push the constant value 1 onto the stack if the result + // of the operation is true or the constant value 0 if the result of the + // operation is false. + case DW_OP_lt: + if (stack.size() < 2) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 2 items for DW_OP_lt."); + return false; + } else { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = + stack.back().ResolveValue(exe_ctx) < tmp.ResolveValue(exe_ctx); + } + break; + + // OPCODE: DW_OP_ne + // OPERANDS: none + // DESCRIPTION: pops the top two stack values, compares using the + // not equal (!=) operator. + // STACK RESULT: push the constant value 1 onto the stack if the result + // of the operation is true or the constant value 0 if the result of the + // operation is false. + case DW_OP_ne: + if (stack.size() < 2) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 2 items for DW_OP_ne."); + return false; + } else { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = + stack.back().ResolveValue(exe_ctx) != tmp.ResolveValue(exe_ctx); + } + break; + + // OPCODE: DW_OP_litn + // OPERANDS: none + // DESCRIPTION: encode the unsigned literal values from 0 through 31. + // STACK RESULT: push the unsigned literal constant value onto the top + // of the stack. + case DW_OP_lit0: + case DW_OP_lit1: + case DW_OP_lit2: + case DW_OP_lit3: + case DW_OP_lit4: + case DW_OP_lit5: + case DW_OP_lit6: + case DW_OP_lit7: + case DW_OP_lit8: + case DW_OP_lit9: + case DW_OP_lit10: + case DW_OP_lit11: + case DW_OP_lit12: + case DW_OP_lit13: + case DW_OP_lit14: + case DW_OP_lit15: + case DW_OP_lit16: + case DW_OP_lit17: + case DW_OP_lit18: + case DW_OP_lit19: + case DW_OP_lit20: + case DW_OP_lit21: + case DW_OP_lit22: + case DW_OP_lit23: + case DW_OP_lit24: + case DW_OP_lit25: + case DW_OP_lit26: + case DW_OP_lit27: + case DW_OP_lit28: + case DW_OP_lit29: + case DW_OP_lit30: + case DW_OP_lit31: + stack.push_back(Scalar((uint64_t)(op - DW_OP_lit0))); + break; + + // OPCODE: DW_OP_regN + // OPERANDS: none + // DESCRIPTION: Push the value in register n on the top of the stack. + case DW_OP_reg0: + case DW_OP_reg1: + case DW_OP_reg2: + case DW_OP_reg3: + case DW_OP_reg4: + case DW_OP_reg5: + case DW_OP_reg6: + case DW_OP_reg7: + case DW_OP_reg8: + case DW_OP_reg9: + case DW_OP_reg10: + case DW_OP_reg11: + case DW_OP_reg12: + case DW_OP_reg13: + case DW_OP_reg14: + case DW_OP_reg15: + case DW_OP_reg16: + case DW_OP_reg17: + case DW_OP_reg18: + case DW_OP_reg19: + case DW_OP_reg20: + case DW_OP_reg21: + case DW_OP_reg22: + case DW_OP_reg23: + case DW_OP_reg24: + case DW_OP_reg25: + case DW_OP_reg26: + case DW_OP_reg27: + case DW_OP_reg28: + case DW_OP_reg29: + case DW_OP_reg30: + case DW_OP_reg31: { + reg_num = op - DW_OP_reg0; + + if (ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, error_ptr, tmp)) + stack.push_back(tmp); + else + return false; + } break; + // OPCODE: DW_OP_regx + // OPERANDS: + // ULEB128 literal operand that encodes the register. + // DESCRIPTION: Push the value in register on the top of the stack. + case DW_OP_regx: { + reg_num = opcodes.GetULEB128(&offset); + if (ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, error_ptr, tmp)) + stack.push_back(tmp); + else + return false; + } break; + + // OPCODE: DW_OP_bregN + // OPERANDS: + // SLEB128 offset from register N + // DESCRIPTION: Value is in memory at the address specified by register + // N plus an offset. + case DW_OP_breg0: + case DW_OP_breg1: + case DW_OP_breg2: + case DW_OP_breg3: + case DW_OP_breg4: + case DW_OP_breg5: + case DW_OP_breg6: + case DW_OP_breg7: + case DW_OP_breg8: + case DW_OP_breg9: + case DW_OP_breg10: + case DW_OP_breg11: + case DW_OP_breg12: + case DW_OP_breg13: + case DW_OP_breg14: + case DW_OP_breg15: + case DW_OP_breg16: + case DW_OP_breg17: + case DW_OP_breg18: + case DW_OP_breg19: + case DW_OP_breg20: + case DW_OP_breg21: + case DW_OP_breg22: + case DW_OP_breg23: + case DW_OP_breg24: + case DW_OP_breg25: + case DW_OP_breg26: + case DW_OP_breg27: + case DW_OP_breg28: + case DW_OP_breg29: + case DW_OP_breg30: + case DW_OP_breg31: { + reg_num = op - DW_OP_breg0; + + if (ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, error_ptr, tmp)) { + int64_t breg_offset = opcodes.GetSLEB128(&offset); + tmp.ResolveValue(exe_ctx) += (uint64_t)breg_offset; + tmp.ClearContext(); + stack.push_back(tmp); + stack.back().SetValueType(Value::eValueTypeLoadAddress); + } else + return false; + } break; + // OPCODE: DW_OP_bregx + // OPERANDS: 2 + // ULEB128 literal operand that encodes the register. + // SLEB128 offset from register N + // DESCRIPTION: Value is in memory at the address specified by register + // N plus an offset. + case DW_OP_bregx: { + reg_num = opcodes.GetULEB128(&offset); + + if (ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, error_ptr, tmp)) { + int64_t breg_offset = opcodes.GetSLEB128(&offset); + tmp.ResolveValue(exe_ctx) += (uint64_t)breg_offset; + tmp.ClearContext(); + stack.push_back(tmp); + stack.back().SetValueType(Value::eValueTypeLoadAddress); + } else + return false; + } break; + + case DW_OP_fbreg: + if (exe_ctx) { + if (frame) { + Scalar value; + if (frame->GetFrameBaseValue(value, error_ptr)) { + int64_t fbreg_offset = opcodes.GetSLEB128(&offset); + value += fbreg_offset; + stack.push_back(value); + stack.back().SetValueType(Value::eValueTypeLoadAddress); + } else + return false; + } else { + if (error_ptr) + error_ptr->SetErrorString( + "Invalid stack frame in context for DW_OP_fbreg opcode."); + return false; + } + } else { + if (error_ptr) + error_ptr->SetErrorStringWithFormat( + "NULL execution context for DW_OP_fbreg.\n"); + return false; + } + + break; + + // OPCODE: DW_OP_nop + // OPERANDS: none + // DESCRIPTION: A place holder. It has no effect on the location stack + // or any of its values. + case DW_OP_nop: + break; + + // OPCODE: DW_OP_piece + // OPERANDS: 1 + // ULEB128: byte size of the piece + // DESCRIPTION: The operand describes the size in bytes of the piece of + // the object referenced by the DWARF expression whose result is at the top + // of the stack. If the piece is located in a register, but does not occupy + // the entire register, the placement of the piece within that register is + // defined by the ABI. + // + // Many compilers store a single variable in sets of registers, or store a + // variable partially in memory and partially in registers. DW_OP_piece + // provides a way of describing how large a part of a variable a particular + // DWARF expression refers to. + case DW_OP_piece: { + const uint64_t piece_byte_size = opcodes.GetULEB128(&offset); + + if (piece_byte_size > 0) { + Value curr_piece; + + if (stack.empty()) { + // In a multi-piece expression, this means that the current piece is + // not available. Fill with zeros for now by resizing the data and + // appending it + curr_piece.ResizeData(piece_byte_size); + // Note that "0" is not a correct value for the unknown bits. + // It would be better to also return a mask of valid bits together + // with the expression result, so the debugger can print missing + // members as "" or something. + ::memset(curr_piece.GetBuffer().GetBytes(), 0, piece_byte_size); + pieces.AppendDataToHostBuffer(curr_piece); + } else { + Status error; + // Extract the current piece into "curr_piece" + Value curr_piece_source_value(stack.back()); + stack.pop_back(); + + const Value::ValueType curr_piece_source_value_type = + curr_piece_source_value.GetValueType(); + switch (curr_piece_source_value_type) { + case Value::eValueTypeLoadAddress: + if (process) { + if (curr_piece.ResizeData(piece_byte_size) == piece_byte_size) { + lldb::addr_t load_addr = + curr_piece_source_value.GetScalar().ULongLong( + LLDB_INVALID_ADDRESS); + if (process->ReadMemory( + load_addr, curr_piece.GetBuffer().GetBytes(), + piece_byte_size, error) != piece_byte_size) { + if (error_ptr) + error_ptr->SetErrorStringWithFormat( + "failed to read memory DW_OP_piece(%" PRIu64 + ") from 0x%" PRIx64, + piece_byte_size, load_addr); + return false; + } + } else { + if (error_ptr) + error_ptr->SetErrorStringWithFormat( + "failed to resize the piece memory buffer for " + "DW_OP_piece(%" PRIu64 ")", + piece_byte_size); + return false; + } + } + break; + + case Value::eValueTypeFileAddress: + case Value::eValueTypeHostAddress: + if (error_ptr) { + lldb::addr_t addr = curr_piece_source_value.GetScalar().ULongLong( + LLDB_INVALID_ADDRESS); + error_ptr->SetErrorStringWithFormat( + "failed to read memory DW_OP_piece(%" PRIu64 + ") from %s address 0x%" PRIx64, + piece_byte_size, + curr_piece_source_value.GetValueType() == + Value::eValueTypeFileAddress + ? "file" + : "host", + addr); + } + return false; + + case Value::eValueTypeScalar: { + uint32_t bit_size = piece_byte_size * 8; + uint32_t bit_offset = 0; + Scalar &scalar = curr_piece_source_value.GetScalar(); + if (!scalar.ExtractBitfield(bit_size, bit_offset)) { + if (error_ptr) + error_ptr->SetErrorStringWithFormat( + "unable to extract %" PRIu64 " bytes from a %" PRIu64 + " byte scalar value.", + piece_byte_size, + (uint64_t)curr_piece_source_value.GetScalar().GetByteSize()); + return false; + } + // Create curr_piece with bit_size. By default Scalar + // grows to the nearest host integer type. + llvm::APInt fail_value(1, 0, false); + llvm::APInt ap_int = scalar.UInt128(fail_value); + assert(ap_int.getBitWidth() >= bit_size); + llvm::ArrayRef buf{ap_int.getRawData(), + ap_int.getNumWords()}; + curr_piece.GetScalar() = Scalar(llvm::APInt(bit_size, buf)); + } break; + + case Value::eValueTypeVector: { + if (curr_piece_source_value.GetVector().length >= piece_byte_size) + curr_piece_source_value.GetVector().length = piece_byte_size; + else { + if (error_ptr) + error_ptr->SetErrorStringWithFormat( + "unable to extract %" PRIu64 " bytes from a %" PRIu64 + " byte vector value.", + piece_byte_size, + (uint64_t)curr_piece_source_value.GetVector().length); + return false; + } + } break; + } + + // Check if this is the first piece? + if (op_piece_offset == 0) { + // This is the first piece, we should push it back onto the stack + // so subsequent pieces will be able to access this piece and add + // to it. + if (pieces.AppendDataToHostBuffer(curr_piece) == 0) { + if (error_ptr) + error_ptr->SetErrorString("failed to append piece data"); + return false; + } + } else { + // If this is the second or later piece there should be a value on + // the stack. + if (pieces.GetBuffer().GetByteSize() != op_piece_offset) { + if (error_ptr) + error_ptr->SetErrorStringWithFormat( + "DW_OP_piece for offset %" PRIu64 + " but top of stack is of size %" PRIu64, + op_piece_offset, pieces.GetBuffer().GetByteSize()); + return false; + } + + if (pieces.AppendDataToHostBuffer(curr_piece) == 0) { + if (error_ptr) + error_ptr->SetErrorString("failed to append piece data"); + return false; + } + } + } + op_piece_offset += piece_byte_size; + } + } break; + + case DW_OP_bit_piece: // 0x9d ULEB128 bit size, ULEB128 bit offset (DWARF3); + if (stack.size() < 1) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 1 item for DW_OP_bit_piece."); + return false; + } else { + const uint64_t piece_bit_size = opcodes.GetULEB128(&offset); + const uint64_t piece_bit_offset = opcodes.GetULEB128(&offset); + switch (stack.back().GetValueType()) { + case Value::eValueTypeScalar: { + if (!stack.back().GetScalar().ExtractBitfield(piece_bit_size, + piece_bit_offset)) { + if (error_ptr) + error_ptr->SetErrorStringWithFormat( + "unable to extract %" PRIu64 " bit value with %" PRIu64 + " bit offset from a %" PRIu64 " bit scalar value.", + piece_bit_size, piece_bit_offset, + (uint64_t)(stack.back().GetScalar().GetByteSize() * 8)); + return false; + } + } break; + + case Value::eValueTypeFileAddress: + case Value::eValueTypeLoadAddress: + case Value::eValueTypeHostAddress: + if (error_ptr) { + error_ptr->SetErrorStringWithFormat( + "unable to extract DW_OP_bit_piece(bit_size = %" PRIu64 + ", bit_offset = %" PRIu64 ") from an address value.", + piece_bit_size, piece_bit_offset); + } + return false; + + case Value::eValueTypeVector: + if (error_ptr) { + error_ptr->SetErrorStringWithFormat( + "unable to extract DW_OP_bit_piece(bit_size = %" PRIu64 + ", bit_offset = %" PRIu64 ") from a vector value.", + piece_bit_size, piece_bit_offset); + } + return false; + } + } + break; + + // OPCODE: DW_OP_push_object_address + // OPERANDS: none + // DESCRIPTION: Pushes the address of the object currently being + // evaluated as part of evaluation of a user presented expression. This + // object may correspond to an independent variable described by its own + // DIE or it may be a component of an array, structure, or class whose + // address has been dynamically determined by an earlier step during user + // expression evaluation. + case DW_OP_push_object_address: + if (object_address_ptr) + stack.push_back(*object_address_ptr); + else { + if (error_ptr) + error_ptr->SetErrorString("DW_OP_push_object_address used without " + "specifying an object address"); + return false; + } + break; + + // OPCODE: DW_OP_call2 + // OPERANDS: + // uint16_t compile unit relative offset of a DIE + // DESCRIPTION: Performs subroutine calls during evaluation + // of a DWARF expression. The operand is the 2-byte unsigned offset of a + // debugging information entry in the current compilation unit. + // + // Operand interpretation is exactly like that for DW_FORM_ref2. + // + // This operation transfers control of DWARF expression evaluation to the + // DW_AT_location attribute of the referenced DIE. If there is no such + // attribute, then there is no effect. Execution of the DWARF expression of + // a DW_AT_location attribute may add to and/or remove from values on the + // stack. Execution returns to the point following the call when the end of + // the attribute is reached. Values on the stack at the time of the call + // may be used as parameters by the called expression and values left on + // the stack by the called expression may be used as return values by prior + // agreement between the calling and called expressions. + case DW_OP_call2: + if (error_ptr) + error_ptr->SetErrorString("Unimplemented opcode DW_OP_call2."); + return false; + // OPCODE: DW_OP_call4 + // OPERANDS: 1 + // uint32_t compile unit relative offset of a DIE + // DESCRIPTION: Performs a subroutine call during evaluation of a DWARF + // expression. For DW_OP_call4, the operand is a 4-byte unsigned offset of + // a debugging information entry in the current compilation unit. + // + // Operand interpretation DW_OP_call4 is exactly like that for + // DW_FORM_ref4. + // + // This operation transfers control of DWARF expression evaluation to the + // DW_AT_location attribute of the referenced DIE. If there is no such + // attribute, then there is no effect. Execution of the DWARF expression of + // a DW_AT_location attribute may add to and/or remove from values on the + // stack. Execution returns to the point following the call when the end of + // the attribute is reached. Values on the stack at the time of the call + // may be used as parameters by the called expression and values left on + // the stack by the called expression may be used as return values by prior + // agreement between the calling and called expressions. + case DW_OP_call4: + if (error_ptr) + error_ptr->SetErrorString("Unimplemented opcode DW_OP_call4."); + return false; + + // OPCODE: DW_OP_stack_value + // OPERANDS: None + // DESCRIPTION: Specifies that the object does not exist in memory but + // rather is a constant value. The value from the top of the stack is the + // value to be used. This is the actual object value and not the location. + case DW_OP_stack_value: + if (stack.empty()) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 1 item for DW_OP_stack_value."); + return false; + } + stack.back().SetValueType(Value::eValueTypeScalar); + break; + + // OPCODE: DW_OP_convert + // OPERANDS: 1 + // A ULEB128 that is either a DIE offset of a + // DW_TAG_base_type or 0 for the generic (pointer-sized) type. + // + // DESCRIPTION: Pop the top stack element, convert it to a + // different type, and push the result. + case DW_OP_convert: { + if (stack.size() < 1) { + if (error_ptr) + error_ptr->SetErrorString( + "Expression stack needs at least 1 item for DW_OP_convert."); + return false; + } + const uint64_t die_offset = opcodes.GetULEB128(&offset); + Scalar::Type type = Scalar::e_void; + uint64_t bit_size; + if (die_offset == 0) { + // The generic type has the size of an address on the target + // machine and an unspecified signedness. Scalar has no + // "unspecified signedness", so we use unsigned types. + if (!module_sp) { + if (error_ptr) + error_ptr->SetErrorString("No module"); + return false; + } + bit_size = module_sp->GetArchitecture().GetAddressByteSize() * 8; + if (!bit_size) { + if (error_ptr) + error_ptr->SetErrorString("unspecified architecture"); + return false; + } + type = Scalar::GetBestTypeForBitSize(bit_size, false); + } else { + // Retrieve the type DIE that the value is being converted to. + // FIXME: the constness has annoying ripple effects. + DWARFDIE die = const_cast(dwarf_cu)->GetDIE(die_offset); + if (!die) { + if (error_ptr) + error_ptr->SetErrorString("Cannot resolve DW_OP_convert type DIE"); + return false; + } + uint64_t encoding = + die.GetAttributeValueAsUnsigned(DW_AT_encoding, DW_ATE_hi_user); + bit_size = die.GetAttributeValueAsUnsigned(DW_AT_byte_size, 0) * 8; + if (!bit_size) + bit_size = die.GetAttributeValueAsUnsigned(DW_AT_bit_size, 0); + if (!bit_size) { + if (error_ptr) + error_ptr->SetErrorString("Unsupported type size in DW_OP_convert"); + return false; + } + switch (encoding) { + case DW_ATE_signed: + case DW_ATE_signed_char: + type = Scalar::GetBestTypeForBitSize(bit_size, true); + break; + case DW_ATE_unsigned: + case DW_ATE_unsigned_char: + type = Scalar::GetBestTypeForBitSize(bit_size, false); + break; + default: + if (error_ptr) + error_ptr->SetErrorString("Unsupported encoding in DW_OP_convert"); + return false; + } + } + if (type == Scalar::e_void) { + if (error_ptr) + error_ptr->SetErrorString("Unsupported pointer size"); + return false; + } + Scalar &top = stack.back().ResolveValue(exe_ctx); + top.TruncOrExtendTo(type, bit_size); + break; + } + + // OPCODE: DW_OP_call_frame_cfa + // OPERANDS: None + // DESCRIPTION: Specifies a DWARF expression that pushes the value of + // the canonical frame address consistent with the call frame information + // located in .debug_frame (or in the FDEs of the eh_frame section). + case DW_OP_call_frame_cfa: + if (frame) { + // Note that we don't have to parse FDEs because this DWARF expression + // is commonly evaluated with a valid stack frame. + StackID id = frame->GetStackID(); + addr_t cfa = id.GetCallFrameAddress(); + if (cfa != LLDB_INVALID_ADDRESS) { + stack.push_back(Scalar(cfa)); + stack.back().SetValueType(Value::eValueTypeLoadAddress); + } else if (error_ptr) + error_ptr->SetErrorString("Stack frame does not include a canonical " + "frame address for DW_OP_call_frame_cfa " + "opcode."); + } else { + if (error_ptr) + error_ptr->SetErrorString("Invalid stack frame in context for " + "DW_OP_call_frame_cfa opcode."); + return false; + } + break; + + // OPCODE: DW_OP_form_tls_address (or the old pre-DWARFv3 vendor extension + // opcode, DW_OP_GNU_push_tls_address) + // OPERANDS: none + // DESCRIPTION: Pops a TLS offset from the stack, converts it to + // an address in the current thread's thread-local storage block, and + // pushes it on the stack. + case DW_OP_form_tls_address: + case DW_OP_GNU_push_tls_address: { + if (stack.size() < 1) { + if (error_ptr) { + if (op == DW_OP_form_tls_address) + error_ptr->SetErrorString( + "DW_OP_form_tls_address needs an argument."); + else + error_ptr->SetErrorString( + "DW_OP_GNU_push_tls_address needs an argument."); + } + return false; + } + + if (!exe_ctx || !module_sp) { + if (error_ptr) + error_ptr->SetErrorString("No context to evaluate TLS within."); + return false; + } + + Thread *thread = exe_ctx->GetThreadPtr(); + if (!thread) { + if (error_ptr) + error_ptr->SetErrorString("No thread to evaluate TLS within."); + return false; + } + + // Lookup the TLS block address for this thread and module. + const addr_t tls_file_addr = + stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS); + const addr_t tls_load_addr = + thread->GetThreadLocalData(module_sp, tls_file_addr); + + if (tls_load_addr == LLDB_INVALID_ADDRESS) { + if (error_ptr) + error_ptr->SetErrorString( + "No TLS data currently exists for this thread."); + return false; + } + + stack.back().GetScalar() = tls_load_addr; + stack.back().SetValueType(Value::eValueTypeLoadAddress); + } break; + + // OPCODE: DW_OP_addrx (DW_OP_GNU_addr_index is the legacy name.) + // OPERANDS: 1 + // ULEB128: index to the .debug_addr section + // DESCRIPTION: Pushes an address to the stack from the .debug_addr + // section with the base address specified by the DW_AT_addr_base attribute + // and the 0 based index is the ULEB128 encoded index. + case DW_OP_addrx: + case DW_OP_GNU_addr_index: { + if (!dwarf_cu) { + if (error_ptr) + error_ptr->SetErrorString("DW_OP_GNU_addr_index found without a " + "compile unit being specified"); + return false; + } + uint64_t index = opcodes.GetULEB128(&offset); + lldb::addr_t value = + DWARFExpression::ReadAddressFromDebugAddrSection(dwarf_cu, index); + stack.push_back(Scalar(value)); + stack.back().SetValueType(Value::eValueTypeFileAddress); + } break; + + // OPCODE: DW_OP_GNU_const_index + // OPERANDS: 1 + // ULEB128: index to the .debug_addr section + // DESCRIPTION: Pushes an constant with the size of a machine address to + // the stack from the .debug_addr section with the base address specified + // by the DW_AT_addr_base attribute and the 0 based index is the ULEB128 + // encoded index. + case DW_OP_GNU_const_index: { + if (!dwarf_cu) { + if (error_ptr) + error_ptr->SetErrorString("DW_OP_GNU_const_index found without a " + "compile unit being specified"); + return false; + } + uint64_t index = opcodes.GetULEB128(&offset); + lldb::addr_t value = + DWARFExpression::ReadAddressFromDebugAddrSection(dwarf_cu, index); + stack.push_back(Scalar(value)); + } break; + + case DW_OP_entry_value: { + if (!Evaluate_DW_OP_entry_value(stack, exe_ctx, reg_ctx, opcodes, offset, + error_ptr, log)) { + LLDB_ERRORF(error_ptr, "Could not evaluate %s.", DW_OP_value_to_name(op)); + return false; + } + break; + } + + default: + LLDB_LOGF(log, "Unhandled opcode %s in DWARFExpression.", + DW_OP_value_to_name(op)); + break; + } + + return true; +} diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp --- a/lldb/source/Expression/DWARFExpression.cpp +++ b/lldb/source/Expression/DWARFExpression.cpp @@ -8,31 +8,18 @@ #include "lldb/Expression/DWARFExpression.h" -#include - -#include - #include "lldb/Core/Module.h" #include "lldb/Core/Value.h" #include "lldb/Core/dwarf.h" +#include "lldb/Expression/DWARFEvaluator.h" #include "lldb/Utility/DataEncoder.h" #include "lldb/Utility/Log.h" -#include "lldb/Utility/RegisterValue.h" -#include "lldb/Utility/Scalar.h" -#include "lldb/Utility/StreamString.h" -#include "lldb/Utility/VMRange.h" - -#include "lldb/Host/Host.h" -#include "lldb/Utility/Endian.h" #include "lldb/Symbol/Function.h" #include "lldb/Target/ABI.h" -#include "lldb/Target/ExecutionContext.h" -#include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/StackFrame.h" -#include "lldb/Target/StackID.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" @@ -41,9 +28,9 @@ using namespace lldb; using namespace lldb_private; -static lldb::addr_t -ReadAddressFromDebugAddrSection(const DWARFUnit *dwarf_cu, - uint32_t index) { +lldb::addr_t +DWARFExpression::ReadAddressFromDebugAddrSection(const DWARFUnit *dwarf_cu, + uint32_t index) { uint32_t index_size = dwarf_cu->GetAddressByteSize(); dw_offset_t addr_base = dwarf_cu->GetAddrBase(); lldb::offset_t offset = addr_base + index * index_size; @@ -152,52 +139,6 @@ } } -static bool ReadRegisterValueAsScalar(RegisterContext *reg_ctx, - lldb::RegisterKind reg_kind, - uint32_t reg_num, Status *error_ptr, - Value &value) { - if (reg_ctx == nullptr) { - if (error_ptr) - error_ptr->SetErrorStringWithFormat("No register context in frame.\n"); - } else { - uint32_t native_reg = - reg_ctx->ConvertRegisterKindToRegisterNumber(reg_kind, reg_num); - if (native_reg == LLDB_INVALID_REGNUM) { - if (error_ptr) - error_ptr->SetErrorStringWithFormat("Unable to convert register " - "kind=%u reg_num=%u to a native " - "register number.\n", - reg_kind, reg_num); - } else { - const RegisterInfo *reg_info = - reg_ctx->GetRegisterInfoAtIndex(native_reg); - RegisterValue reg_value; - if (reg_ctx->ReadRegister(reg_info, reg_value)) { - if (reg_value.GetScalarValue(value.GetScalar())) { - value.SetValueType(Value::eValueTypeScalar); - value.SetContext(Value::eContextTypeRegisterInfo, - const_cast(reg_info)); - if (error_ptr) - error_ptr->Clear(); - return true; - } else { - // If we get this error, then we need to implement a value buffer in - // the dwarf expression evaluation function... - if (error_ptr) - error_ptr->SetErrorStringWithFormat( - "register %s can't be converted to a scalar value", - reg_info->name); - } - } else { - if (error_ptr) - error_ptr->SetErrorStringWithFormat("register %s is not available", - reg_info->name); - } - } - } - return false; -} - /// Return the length in bytes of the set of operands for \p op. No guarantees /// are made on the state of \p data after this call. static offset_t GetOpcodeDataSize(const DataExtractor &data, @@ -932,8 +873,6 @@ stack.push_back(*initial_value_ptr); lldb::offset_t offset = 0; - Value tmp; - uint32_t reg_num; /// Insertion point for evaluating multi-piece expression. uint64_t op_piece_offset = 0; @@ -959,1583 +898,14 @@ DW_OP_value_to_name(op)); } - switch (op) { - // The DW_OP_addr operation has a single operand that encodes a machine - // address and whose size is the size of an address on the target machine. - case DW_OP_addr: - stack.push_back(Scalar(opcodes.GetAddress(&offset))); - stack.back().SetValueType(Value::eValueTypeFileAddress); - // Convert the file address to a load address, so subsequent - // DWARF operators can operate on it. - if (frame) - stack.back().ConvertToLoadAddress(module_sp.get(), - frame->CalculateTarget().get()); - break; - - // The DW_OP_addr_sect_offset4 is used for any location expressions in - // shared libraries that have a location like: - // DW_OP_addr(0x1000) - // If this address resides in a shared library, then this virtual address - // won't make sense when it is evaluated in the context of a running - // process where shared libraries have been slid. To account for this, this - // new address type where we can store the section pointer and a 4 byte - // offset. - // case DW_OP_addr_sect_offset4: - // { - // result_type = eResultTypeFileAddress; - // lldb::Section *sect = (lldb::Section - // *)opcodes.GetMaxU64(&offset, sizeof(void *)); - // lldb::addr_t sect_offset = opcodes.GetU32(&offset); - // - // Address so_addr (sect, sect_offset); - // lldb::addr_t load_addr = so_addr.GetLoadAddress(); - // if (load_addr != LLDB_INVALID_ADDRESS) - // { - // // We successfully resolve a file address to a load - // // address. - // stack.push_back(load_addr); - // break; - // } - // else - // { - // // We were able - // if (error_ptr) - // error_ptr->SetErrorStringWithFormat ("Section %s in - // %s is not currently loaded.\n", - // sect->GetName().AsCString(), - // sect->GetModule()->GetFileSpec().GetFilename().AsCString()); - // return false; - // } - // } - // break; - - // OPCODE: DW_OP_deref - // OPERANDS: none - // DESCRIPTION: Pops the top stack entry and treats it as an address. - // The value retrieved from that address is pushed. The size of the data - // retrieved from the dereferenced address is the size of an address on the - // target machine. - case DW_OP_deref: { - if (stack.empty()) { - if (error_ptr) - error_ptr->SetErrorString("Expression stack empty for DW_OP_deref."); - return false; - } - Value::ValueType value_type = stack.back().GetValueType(); - switch (value_type) { - case Value::eValueTypeHostAddress: { - void *src = (void *)stack.back().GetScalar().ULongLong(); - intptr_t ptr; - ::memcpy(&ptr, src, sizeof(void *)); - stack.back().GetScalar() = ptr; - stack.back().ClearContext(); - } break; - case Value::eValueTypeFileAddress: { - auto file_addr = stack.back().GetScalar().ULongLong( - LLDB_INVALID_ADDRESS); - if (!module_sp) { - if (error_ptr) - error_ptr->SetErrorStringWithFormat( - "need module to resolve file address for DW_OP_deref"); - return false; - } - Address so_addr; - if (!module_sp->ResolveFileAddress(file_addr, so_addr)) { - if (error_ptr) - error_ptr->SetErrorStringWithFormat( - "failed to resolve file address in module"); - return false; - } - addr_t load_Addr = so_addr.GetLoadAddress(exe_ctx->GetTargetPtr()); - if (load_Addr == LLDB_INVALID_ADDRESS) { - if (error_ptr) - error_ptr->SetErrorStringWithFormat( - "failed to resolve load address"); - return false; - } - stack.back().GetScalar() = load_Addr; - stack.back().SetValueType(Value::eValueTypeLoadAddress); - // Fall through to load address code below... - } LLVM_FALLTHROUGH; - case Value::eValueTypeLoadAddress: - if (exe_ctx) { - if (process) { - lldb::addr_t pointer_addr = - stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS); - Status error; - lldb::addr_t pointer_value = - process->ReadPointerFromMemory(pointer_addr, error); - if (pointer_value != LLDB_INVALID_ADDRESS) { - stack.back().GetScalar() = pointer_value; - stack.back().ClearContext(); - } else { - if (error_ptr) - error_ptr->SetErrorStringWithFormat( - "Failed to dereference pointer from 0x%" PRIx64 - " for DW_OP_deref: %s\n", - pointer_addr, error.AsCString()); - return false; - } - } else { - if (error_ptr) - error_ptr->SetErrorStringWithFormat( - "NULL process for DW_OP_deref.\n"); - return false; - } - } else { - if (error_ptr) - error_ptr->SetErrorStringWithFormat( - "NULL execution context for DW_OP_deref.\n"); - return false; - } - break; - - default: - break; - } - - } break; - - // OPCODE: DW_OP_deref_size - // OPERANDS: 1 - // 1 - uint8_t that specifies the size of the data to dereference. - // DESCRIPTION: Behaves like the DW_OP_deref operation: it pops the top - // stack entry and treats it as an address. The value retrieved from that - // address is pushed. In the DW_OP_deref_size operation, however, the size - // in bytes of the data retrieved from the dereferenced address is - // specified by the single operand. This operand is a 1-byte unsigned - // integral constant whose value may not be larger than the size of an - // address on the target machine. The data retrieved is zero extended to - // the size of an address on the target machine before being pushed on the - // expression stack. - case DW_OP_deref_size: { - if (stack.empty()) { - if (error_ptr) - error_ptr->SetErrorString( - "Expression stack empty for DW_OP_deref_size."); - return false; - } - uint8_t size = opcodes.GetU8(&offset); - Value::ValueType value_type = stack.back().GetValueType(); - switch (value_type) { - case Value::eValueTypeHostAddress: { - void *src = (void *)stack.back().GetScalar().ULongLong(); - intptr_t ptr; - ::memcpy(&ptr, src, sizeof(void *)); - // I can't decide whether the size operand should apply to the bytes in - // their - // lldb-host endianness or the target endianness.. I doubt this'll ever - // come up but I'll opt for assuming big endian regardless. - switch (size) { - case 1: - ptr = ptr & 0xff; - break; - case 2: - ptr = ptr & 0xffff; - break; - case 3: - ptr = ptr & 0xffffff; - break; - case 4: - ptr = ptr & 0xffffffff; - break; - // the casts are added to work around the case where intptr_t is a 32 - // bit quantity; - // presumably we won't hit the 5..7 cases if (void*) is 32-bits in this - // program. - case 5: - ptr = (intptr_t)ptr & 0xffffffffffULL; - break; - case 6: - ptr = (intptr_t)ptr & 0xffffffffffffULL; - break; - case 7: - ptr = (intptr_t)ptr & 0xffffffffffffffULL; - break; - default: - break; - } - stack.back().GetScalar() = ptr; - stack.back().ClearContext(); - } break; - case Value::eValueTypeLoadAddress: - if (exe_ctx) { - if (process) { - lldb::addr_t pointer_addr = - stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS); - uint8_t addr_bytes[sizeof(lldb::addr_t)]; - Status error; - if (process->ReadMemory(pointer_addr, &addr_bytes, size, error) == - size) { - DataExtractor addr_data(addr_bytes, sizeof(addr_bytes), - process->GetByteOrder(), size); - lldb::offset_t addr_data_offset = 0; - switch (size) { - case 1: - stack.back().GetScalar() = addr_data.GetU8(&addr_data_offset); - break; - case 2: - stack.back().GetScalar() = addr_data.GetU16(&addr_data_offset); - break; - case 4: - stack.back().GetScalar() = addr_data.GetU32(&addr_data_offset); - break; - case 8: - stack.back().GetScalar() = addr_data.GetU64(&addr_data_offset); - break; - default: - stack.back().GetScalar() = - addr_data.GetAddress(&addr_data_offset); - } - stack.back().ClearContext(); - } else { - if (error_ptr) - error_ptr->SetErrorStringWithFormat( - "Failed to dereference pointer from 0x%" PRIx64 - " for DW_OP_deref: %s\n", - pointer_addr, error.AsCString()); - return false; - } - } else { - if (error_ptr) - error_ptr->SetErrorStringWithFormat( - "NULL process for DW_OP_deref.\n"); - return false; - } - } else { - if (error_ptr) - error_ptr->SetErrorStringWithFormat( - "NULL execution context for DW_OP_deref.\n"); - return false; - } - break; - - default: - break; - } - - } break; - - // OPCODE: DW_OP_xderef_size - // OPERANDS: 1 - // 1 - uint8_t that specifies the size of the data to dereference. - // DESCRIPTION: Behaves like the DW_OP_xderef operation: the entry at - // the top of the stack is treated as an address. The second stack entry is - // treated as an "address space identifier" for those architectures that - // support multiple address spaces. The top two stack elements are popped, - // a data item is retrieved through an implementation-defined address - // calculation and pushed as the new stack top. In the DW_OP_xderef_size - // operation, however, the size in bytes of the data retrieved from the - // dereferenced address is specified by the single operand. This operand is - // a 1-byte unsigned integral constant whose value may not be larger than - // the size of an address on the target machine. The data retrieved is zero - // extended to the size of an address on the target machine before being - // pushed on the expression stack. - case DW_OP_xderef_size: - if (error_ptr) - error_ptr->SetErrorString("Unimplemented opcode: DW_OP_xderef_size."); - return false; - // OPCODE: DW_OP_xderef - // OPERANDS: none - // DESCRIPTION: Provides an extended dereference mechanism. The entry at - // the top of the stack is treated as an address. The second stack entry is - // treated as an "address space identifier" for those architectures that - // support multiple address spaces. The top two stack elements are popped, - // a data item is retrieved through an implementation-defined address - // calculation and pushed as the new stack top. The size of the data - // retrieved from the dereferenced address is the size of an address on the - // target machine. - case DW_OP_xderef: - if (error_ptr) - error_ptr->SetErrorString("Unimplemented opcode: DW_OP_xderef."); + // TODO(paolosev): Cache evaluator in Module? + std::unique_ptr evaluator = + DWARFEvaluator::FindPlugin(module_sp); + if (!evaluator->Evaluate(op, exe_ctx, reg_ctx, process, frame, stack, + module_sp, dwarf_cu, reg_kind, opcodes, offset, + pieces, op_piece_offset, object_address_ptr, log, + result, error_ptr)) { return false; - - // All DW_OP_constXXX opcodes have a single operand as noted below: - // - // Opcode Operand 1 - // DW_OP_const1u 1-byte unsigned integer constant DW_OP_const1s - // 1-byte signed integer constant DW_OP_const2u 2-byte unsigned integer - // constant DW_OP_const2s 2-byte signed integer constant DW_OP_const4u - // 4-byte unsigned integer constant DW_OP_const4s 4-byte signed integer - // constant DW_OP_const8u 8-byte unsigned integer constant DW_OP_const8s - // 8-byte signed integer constant DW_OP_constu unsigned LEB128 integer - // constant DW_OP_consts signed LEB128 integer constant - case DW_OP_const1u: - stack.push_back(Scalar((uint8_t)opcodes.GetU8(&offset))); - break; - case DW_OP_const1s: - stack.push_back(Scalar((int8_t)opcodes.GetU8(&offset))); - break; - case DW_OP_const2u: - stack.push_back(Scalar((uint16_t)opcodes.GetU16(&offset))); - break; - case DW_OP_const2s: - stack.push_back(Scalar((int16_t)opcodes.GetU16(&offset))); - break; - case DW_OP_const4u: - stack.push_back(Scalar((uint32_t)opcodes.GetU32(&offset))); - break; - case DW_OP_const4s: - stack.push_back(Scalar((int32_t)opcodes.GetU32(&offset))); - break; - case DW_OP_const8u: - stack.push_back(Scalar((uint64_t)opcodes.GetU64(&offset))); - break; - case DW_OP_const8s: - stack.push_back(Scalar((int64_t)opcodes.GetU64(&offset))); - break; - case DW_OP_constu: - stack.push_back(Scalar(opcodes.GetULEB128(&offset))); - break; - case DW_OP_consts: - stack.push_back(Scalar(opcodes.GetSLEB128(&offset))); - break; - - // OPCODE: DW_OP_dup - // OPERANDS: none - // DESCRIPTION: duplicates the value at the top of the stack - case DW_OP_dup: - if (stack.empty()) { - if (error_ptr) - error_ptr->SetErrorString("Expression stack empty for DW_OP_dup."); - return false; - } else - stack.push_back(stack.back()); - break; - - // OPCODE: DW_OP_drop - // OPERANDS: none - // DESCRIPTION: pops the value at the top of the stack - case DW_OP_drop: - if (stack.empty()) { - if (error_ptr) - error_ptr->SetErrorString("Expression stack empty for DW_OP_drop."); - return false; - } else - stack.pop_back(); - break; - - // OPCODE: DW_OP_over - // OPERANDS: none - // DESCRIPTION: Duplicates the entry currently second in the stack at - // the top of the stack. - case DW_OP_over: - if (stack.size() < 2) { - if (error_ptr) - error_ptr->SetErrorString( - "Expression stack needs at least 2 items for DW_OP_over."); - return false; - } else - stack.push_back(stack[stack.size() - 2]); - break; - - // OPCODE: DW_OP_pick - // OPERANDS: uint8_t index into the current stack - // DESCRIPTION: The stack entry with the specified index (0 through 255, - // inclusive) is pushed on the stack - case DW_OP_pick: { - uint8_t pick_idx = opcodes.GetU8(&offset); - if (pick_idx < stack.size()) - stack.push_back(stack[stack.size() - 1 - pick_idx]); - else { - if (error_ptr) - error_ptr->SetErrorStringWithFormat( - "Index %u out of range for DW_OP_pick.\n", pick_idx); - return false; - } - } break; - - // OPCODE: DW_OP_swap - // OPERANDS: none - // DESCRIPTION: swaps the top two stack entries. The entry at the top - // of the stack becomes the second stack entry, and the second entry - // becomes the top of the stack - case DW_OP_swap: - if (stack.size() < 2) { - if (error_ptr) - error_ptr->SetErrorString( - "Expression stack needs at least 2 items for DW_OP_swap."); - return false; - } else { - tmp = stack.back(); - stack.back() = stack[stack.size() - 2]; - stack[stack.size() - 2] = tmp; - } - break; - - // OPCODE: DW_OP_rot - // OPERANDS: none - // DESCRIPTION: Rotates the first three stack entries. The entry at - // the top of the stack becomes the third stack entry, the second entry - // becomes the top of the stack, and the third entry becomes the second - // entry. - case DW_OP_rot: - if (stack.size() < 3) { - if (error_ptr) - error_ptr->SetErrorString( - "Expression stack needs at least 3 items for DW_OP_rot."); - return false; - } else { - size_t last_idx = stack.size() - 1; - Value old_top = stack[last_idx]; - stack[last_idx] = stack[last_idx - 1]; - stack[last_idx - 1] = stack[last_idx - 2]; - stack[last_idx - 2] = old_top; - } - break; - - // OPCODE: DW_OP_abs - // OPERANDS: none - // DESCRIPTION: pops the top stack entry, interprets it as a signed - // value and pushes its absolute value. If the absolute value can not be - // represented, the result is undefined. - case DW_OP_abs: - if (stack.empty()) { - if (error_ptr) - error_ptr->SetErrorString( - "Expression stack needs at least 1 item for DW_OP_abs."); - return false; - } else if (!stack.back().ResolveValue(exe_ctx).AbsoluteValue()) { - if (error_ptr) - error_ptr->SetErrorString( - "Failed to take the absolute value of the first stack item."); - return false; - } - break; - - // OPCODE: DW_OP_and - // OPERANDS: none - // DESCRIPTION: pops the top two stack values, performs a bitwise and - // operation on the two, and pushes the result. - case DW_OP_and: - if (stack.size() < 2) { - if (error_ptr) - error_ptr->SetErrorString( - "Expression stack needs at least 2 items for DW_OP_and."); - return false; - } else { - tmp = stack.back(); - stack.pop_back(); - stack.back().ResolveValue(exe_ctx) = - stack.back().ResolveValue(exe_ctx) & tmp.ResolveValue(exe_ctx); - } - break; - - // OPCODE: DW_OP_div - // OPERANDS: none - // DESCRIPTION: pops the top two stack values, divides the former second - // entry by the former top of the stack using signed division, and pushes - // the result. - case DW_OP_div: - if (stack.size() < 2) { - if (error_ptr) - error_ptr->SetErrorString( - "Expression stack needs at least 2 items for DW_OP_div."); - return false; - } else { - tmp = stack.back(); - if (tmp.ResolveValue(exe_ctx).IsZero()) { - if (error_ptr) - error_ptr->SetErrorString("Divide by zero."); - return false; - } else { - stack.pop_back(); - stack.back() = - stack.back().ResolveValue(exe_ctx) / tmp.ResolveValue(exe_ctx); - if (!stack.back().ResolveValue(exe_ctx).IsValid()) { - if (error_ptr) - error_ptr->SetErrorString("Divide failed."); - return false; - } - } - } - break; - - // OPCODE: DW_OP_minus - // OPERANDS: none - // DESCRIPTION: pops the top two stack values, subtracts the former top - // of the stack from the former second entry, and pushes the result. - case DW_OP_minus: - if (stack.size() < 2) { - if (error_ptr) - error_ptr->SetErrorString( - "Expression stack needs at least 2 items for DW_OP_minus."); - return false; - } else { - tmp = stack.back(); - stack.pop_back(); - stack.back().ResolveValue(exe_ctx) = - stack.back().ResolveValue(exe_ctx) - tmp.ResolveValue(exe_ctx); - } - break; - - // OPCODE: DW_OP_mod - // OPERANDS: none - // DESCRIPTION: pops the top two stack values and pushes the result of - // the calculation: former second stack entry modulo the former top of the - // stack. - case DW_OP_mod: - if (stack.size() < 2) { - if (error_ptr) - error_ptr->SetErrorString( - "Expression stack needs at least 2 items for DW_OP_mod."); - return false; - } else { - tmp = stack.back(); - stack.pop_back(); - stack.back().ResolveValue(exe_ctx) = - stack.back().ResolveValue(exe_ctx) % tmp.ResolveValue(exe_ctx); - } - break; - - // OPCODE: DW_OP_mul - // OPERANDS: none - // DESCRIPTION: pops the top two stack entries, multiplies them - // together, and pushes the result. - case DW_OP_mul: - if (stack.size() < 2) { - if (error_ptr) - error_ptr->SetErrorString( - "Expression stack needs at least 2 items for DW_OP_mul."); - return false; - } else { - tmp = stack.back(); - stack.pop_back(); - stack.back().ResolveValue(exe_ctx) = - stack.back().ResolveValue(exe_ctx) * tmp.ResolveValue(exe_ctx); - } - break; - - // OPCODE: DW_OP_neg - // OPERANDS: none - // DESCRIPTION: pops the top stack entry, and pushes its negation. - case DW_OP_neg: - if (stack.empty()) { - if (error_ptr) - error_ptr->SetErrorString( - "Expression stack needs at least 1 item for DW_OP_neg."); - return false; - } else { - if (!stack.back().ResolveValue(exe_ctx).UnaryNegate()) { - if (error_ptr) - error_ptr->SetErrorString("Unary negate failed."); - return false; - } - } - break; - - // OPCODE: DW_OP_not - // OPERANDS: none - // DESCRIPTION: pops the top stack entry, and pushes its bitwise - // complement - case DW_OP_not: - if (stack.empty()) { - if (error_ptr) - error_ptr->SetErrorString( - "Expression stack needs at least 1 item for DW_OP_not."); - return false; - } else { - if (!stack.back().ResolveValue(exe_ctx).OnesComplement()) { - if (error_ptr) - error_ptr->SetErrorString("Logical NOT failed."); - return false; - } - } - break; - - // OPCODE: DW_OP_or - // OPERANDS: none - // DESCRIPTION: pops the top two stack entries, performs a bitwise or - // operation on the two, and pushes the result. - case DW_OP_or: - if (stack.size() < 2) { - if (error_ptr) - error_ptr->SetErrorString( - "Expression stack needs at least 2 items for DW_OP_or."); - return false; - } else { - tmp = stack.back(); - stack.pop_back(); - stack.back().ResolveValue(exe_ctx) = - stack.back().ResolveValue(exe_ctx) | tmp.ResolveValue(exe_ctx); - } - break; - - // OPCODE: DW_OP_plus - // OPERANDS: none - // DESCRIPTION: pops the top two stack entries, adds them together, and - // pushes the result. - case DW_OP_plus: - if (stack.size() < 2) { - if (error_ptr) - error_ptr->SetErrorString( - "Expression stack needs at least 2 items for DW_OP_plus."); - return false; - } else { - tmp = stack.back(); - stack.pop_back(); - stack.back().GetScalar() += tmp.GetScalar(); - } - break; - - // OPCODE: DW_OP_plus_uconst - // OPERANDS: none - // DESCRIPTION: pops the top stack entry, adds it to the unsigned LEB128 - // constant operand and pushes the result. - case DW_OP_plus_uconst: - if (stack.empty()) { - if (error_ptr) - error_ptr->SetErrorString( - "Expression stack needs at least 1 item for DW_OP_plus_uconst."); - return false; - } else { - const uint64_t uconst_value = opcodes.GetULEB128(&offset); - // Implicit conversion from a UINT to a Scalar... - stack.back().GetScalar() += uconst_value; - if (!stack.back().GetScalar().IsValid()) { - if (error_ptr) - error_ptr->SetErrorString("DW_OP_plus_uconst failed."); - return false; - } - } - break; - - // OPCODE: DW_OP_shl - // OPERANDS: none - // DESCRIPTION: pops the top two stack entries, shifts the former - // second entry left by the number of bits specified by the former top of - // the stack, and pushes the result. - case DW_OP_shl: - if (stack.size() < 2) { - if (error_ptr) - error_ptr->SetErrorString( - "Expression stack needs at least 2 items for DW_OP_shl."); - return false; - } else { - tmp = stack.back(); - stack.pop_back(); - stack.back().ResolveValue(exe_ctx) <<= tmp.ResolveValue(exe_ctx); - } - break; - - // OPCODE: DW_OP_shr - // OPERANDS: none - // DESCRIPTION: pops the top two stack entries, shifts the former second - // entry right logically (filling with zero bits) by the number of bits - // specified by the former top of the stack, and pushes the result. - case DW_OP_shr: - if (stack.size() < 2) { - if (error_ptr) - error_ptr->SetErrorString( - "Expression stack needs at least 2 items for DW_OP_shr."); - return false; - } else { - tmp = stack.back(); - stack.pop_back(); - if (!stack.back().ResolveValue(exe_ctx).ShiftRightLogical( - tmp.ResolveValue(exe_ctx))) { - if (error_ptr) - error_ptr->SetErrorString("DW_OP_shr failed."); - return false; - } - } - break; - - // OPCODE: DW_OP_shra - // OPERANDS: none - // DESCRIPTION: pops the top two stack entries, shifts the former second - // entry right arithmetically (divide the magnitude by 2, keep the same - // sign for the result) by the number of bits specified by the former top - // of the stack, and pushes the result. - case DW_OP_shra: - if (stack.size() < 2) { - if (error_ptr) - error_ptr->SetErrorString( - "Expression stack needs at least 2 items for DW_OP_shra."); - return false; - } else { - tmp = stack.back(); - stack.pop_back(); - stack.back().ResolveValue(exe_ctx) >>= tmp.ResolveValue(exe_ctx); - } - break; - - // OPCODE: DW_OP_xor - // OPERANDS: none - // DESCRIPTION: pops the top two stack entries, performs the bitwise - // exclusive-or operation on the two, and pushes the result. - case DW_OP_xor: - if (stack.size() < 2) { - if (error_ptr) - error_ptr->SetErrorString( - "Expression stack needs at least 2 items for DW_OP_xor."); - return false; - } else { - tmp = stack.back(); - stack.pop_back(); - stack.back().ResolveValue(exe_ctx) = - stack.back().ResolveValue(exe_ctx) ^ tmp.ResolveValue(exe_ctx); - } - break; - - // OPCODE: DW_OP_skip - // OPERANDS: int16_t - // DESCRIPTION: An unconditional branch. Its single operand is a 2-byte - // signed integer constant. The 2-byte constant is the number of bytes of - // the DWARF expression to skip forward or backward from the current - // operation, beginning after the 2-byte constant. - case DW_OP_skip: { - int16_t skip_offset = (int16_t)opcodes.GetU16(&offset); - lldb::offset_t new_offset = offset + skip_offset; - if (opcodes.ValidOffset(new_offset)) - offset = new_offset; - else { - if (error_ptr) - error_ptr->SetErrorString("Invalid opcode offset in DW_OP_skip."); - return false; - } - } break; - - // OPCODE: DW_OP_bra - // OPERANDS: int16_t - // DESCRIPTION: A conditional branch. Its single operand is a 2-byte - // signed integer constant. This operation pops the top of stack. If the - // value popped is not the constant 0, the 2-byte constant operand is the - // number of bytes of the DWARF expression to skip forward or backward from - // the current operation, beginning after the 2-byte constant. - case DW_OP_bra: - if (stack.empty()) { - if (error_ptr) - error_ptr->SetErrorString( - "Expression stack needs at least 1 item for DW_OP_bra."); - return false; - } else { - tmp = stack.back(); - stack.pop_back(); - int16_t bra_offset = (int16_t)opcodes.GetU16(&offset); - Scalar zero(0); - if (tmp.ResolveValue(exe_ctx) != zero) { - lldb::offset_t new_offset = offset + bra_offset; - if (opcodes.ValidOffset(new_offset)) - offset = new_offset; - else { - if (error_ptr) - error_ptr->SetErrorString("Invalid opcode offset in DW_OP_bra."); - return false; - } - } - } - break; - - // OPCODE: DW_OP_eq - // OPERANDS: none - // DESCRIPTION: pops the top two stack values, compares using the - // equals (==) operator. - // STACK RESULT: push the constant value 1 onto the stack if the result - // of the operation is true or the constant value 0 if the result of the - // operation is false. - case DW_OP_eq: - if (stack.size() < 2) { - if (error_ptr) - error_ptr->SetErrorString( - "Expression stack needs at least 2 items for DW_OP_eq."); - return false; - } else { - tmp = stack.back(); - stack.pop_back(); - stack.back().ResolveValue(exe_ctx) = - stack.back().ResolveValue(exe_ctx) == tmp.ResolveValue(exe_ctx); - } - break; - - // OPCODE: DW_OP_ge - // OPERANDS: none - // DESCRIPTION: pops the top two stack values, compares using the - // greater than or equal to (>=) operator. - // STACK RESULT: push the constant value 1 onto the stack if the result - // of the operation is true or the constant value 0 if the result of the - // operation is false. - case DW_OP_ge: - if (stack.size() < 2) { - if (error_ptr) - error_ptr->SetErrorString( - "Expression stack needs at least 2 items for DW_OP_ge."); - return false; - } else { - tmp = stack.back(); - stack.pop_back(); - stack.back().ResolveValue(exe_ctx) = - stack.back().ResolveValue(exe_ctx) >= tmp.ResolveValue(exe_ctx); - } - break; - - // OPCODE: DW_OP_gt - // OPERANDS: none - // DESCRIPTION: pops the top two stack values, compares using the - // greater than (>) operator. - // STACK RESULT: push the constant value 1 onto the stack if the result - // of the operation is true or the constant value 0 if the result of the - // operation is false. - case DW_OP_gt: - if (stack.size() < 2) { - if (error_ptr) - error_ptr->SetErrorString( - "Expression stack needs at least 2 items for DW_OP_gt."); - return false; - } else { - tmp = stack.back(); - stack.pop_back(); - stack.back().ResolveValue(exe_ctx) = - stack.back().ResolveValue(exe_ctx) > tmp.ResolveValue(exe_ctx); - } - break; - - // OPCODE: DW_OP_le - // OPERANDS: none - // DESCRIPTION: pops the top two stack values, compares using the - // less than or equal to (<=) operator. - // STACK RESULT: push the constant value 1 onto the stack if the result - // of the operation is true or the constant value 0 if the result of the - // operation is false. - case DW_OP_le: - if (stack.size() < 2) { - if (error_ptr) - error_ptr->SetErrorString( - "Expression stack needs at least 2 items for DW_OP_le."); - return false; - } else { - tmp = stack.back(); - stack.pop_back(); - stack.back().ResolveValue(exe_ctx) = - stack.back().ResolveValue(exe_ctx) <= tmp.ResolveValue(exe_ctx); - } - break; - - // OPCODE: DW_OP_lt - // OPERANDS: none - // DESCRIPTION: pops the top two stack values, compares using the - // less than (<) operator. - // STACK RESULT: push the constant value 1 onto the stack if the result - // of the operation is true or the constant value 0 if the result of the - // operation is false. - case DW_OP_lt: - if (stack.size() < 2) { - if (error_ptr) - error_ptr->SetErrorString( - "Expression stack needs at least 2 items for DW_OP_lt."); - return false; - } else { - tmp = stack.back(); - stack.pop_back(); - stack.back().ResolveValue(exe_ctx) = - stack.back().ResolveValue(exe_ctx) < tmp.ResolveValue(exe_ctx); - } - break; - - // OPCODE: DW_OP_ne - // OPERANDS: none - // DESCRIPTION: pops the top two stack values, compares using the - // not equal (!=) operator. - // STACK RESULT: push the constant value 1 onto the stack if the result - // of the operation is true or the constant value 0 if the result of the - // operation is false. - case DW_OP_ne: - if (stack.size() < 2) { - if (error_ptr) - error_ptr->SetErrorString( - "Expression stack needs at least 2 items for DW_OP_ne."); - return false; - } else { - tmp = stack.back(); - stack.pop_back(); - stack.back().ResolveValue(exe_ctx) = - stack.back().ResolveValue(exe_ctx) != tmp.ResolveValue(exe_ctx); - } - break; - - // OPCODE: DW_OP_litn - // OPERANDS: none - // DESCRIPTION: encode the unsigned literal values from 0 through 31. - // STACK RESULT: push the unsigned literal constant value onto the top - // of the stack. - case DW_OP_lit0: - case DW_OP_lit1: - case DW_OP_lit2: - case DW_OP_lit3: - case DW_OP_lit4: - case DW_OP_lit5: - case DW_OP_lit6: - case DW_OP_lit7: - case DW_OP_lit8: - case DW_OP_lit9: - case DW_OP_lit10: - case DW_OP_lit11: - case DW_OP_lit12: - case DW_OP_lit13: - case DW_OP_lit14: - case DW_OP_lit15: - case DW_OP_lit16: - case DW_OP_lit17: - case DW_OP_lit18: - case DW_OP_lit19: - case DW_OP_lit20: - case DW_OP_lit21: - case DW_OP_lit22: - case DW_OP_lit23: - case DW_OP_lit24: - case DW_OP_lit25: - case DW_OP_lit26: - case DW_OP_lit27: - case DW_OP_lit28: - case DW_OP_lit29: - case DW_OP_lit30: - case DW_OP_lit31: - stack.push_back(Scalar((uint64_t)(op - DW_OP_lit0))); - break; - - // OPCODE: DW_OP_regN - // OPERANDS: none - // DESCRIPTION: Push the value in register n on the top of the stack. - case DW_OP_reg0: - case DW_OP_reg1: - case DW_OP_reg2: - case DW_OP_reg3: - case DW_OP_reg4: - case DW_OP_reg5: - case DW_OP_reg6: - case DW_OP_reg7: - case DW_OP_reg8: - case DW_OP_reg9: - case DW_OP_reg10: - case DW_OP_reg11: - case DW_OP_reg12: - case DW_OP_reg13: - case DW_OP_reg14: - case DW_OP_reg15: - case DW_OP_reg16: - case DW_OP_reg17: - case DW_OP_reg18: - case DW_OP_reg19: - case DW_OP_reg20: - case DW_OP_reg21: - case DW_OP_reg22: - case DW_OP_reg23: - case DW_OP_reg24: - case DW_OP_reg25: - case DW_OP_reg26: - case DW_OP_reg27: - case DW_OP_reg28: - case DW_OP_reg29: - case DW_OP_reg30: - case DW_OP_reg31: { - reg_num = op - DW_OP_reg0; - - if (ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, error_ptr, tmp)) - stack.push_back(tmp); - else - return false; - } break; - // OPCODE: DW_OP_regx - // OPERANDS: - // ULEB128 literal operand that encodes the register. - // DESCRIPTION: Push the value in register on the top of the stack. - case DW_OP_regx: { - reg_num = opcodes.GetULEB128(&offset); - if (ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, error_ptr, tmp)) - stack.push_back(tmp); - else - return false; - } break; - - // OPCODE: DW_OP_bregN - // OPERANDS: - // SLEB128 offset from register N - // DESCRIPTION: Value is in memory at the address specified by register - // N plus an offset. - case DW_OP_breg0: - case DW_OP_breg1: - case DW_OP_breg2: - case DW_OP_breg3: - case DW_OP_breg4: - case DW_OP_breg5: - case DW_OP_breg6: - case DW_OP_breg7: - case DW_OP_breg8: - case DW_OP_breg9: - case DW_OP_breg10: - case DW_OP_breg11: - case DW_OP_breg12: - case DW_OP_breg13: - case DW_OP_breg14: - case DW_OP_breg15: - case DW_OP_breg16: - case DW_OP_breg17: - case DW_OP_breg18: - case DW_OP_breg19: - case DW_OP_breg20: - case DW_OP_breg21: - case DW_OP_breg22: - case DW_OP_breg23: - case DW_OP_breg24: - case DW_OP_breg25: - case DW_OP_breg26: - case DW_OP_breg27: - case DW_OP_breg28: - case DW_OP_breg29: - case DW_OP_breg30: - case DW_OP_breg31: { - reg_num = op - DW_OP_breg0; - - if (ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, error_ptr, - tmp)) { - int64_t breg_offset = opcodes.GetSLEB128(&offset); - tmp.ResolveValue(exe_ctx) += (uint64_t)breg_offset; - tmp.ClearContext(); - stack.push_back(tmp); - stack.back().SetValueType(Value::eValueTypeLoadAddress); - } else - return false; - } break; - // OPCODE: DW_OP_bregx - // OPERANDS: 2 - // ULEB128 literal operand that encodes the register. - // SLEB128 offset from register N - // DESCRIPTION: Value is in memory at the address specified by register - // N plus an offset. - case DW_OP_bregx: { - reg_num = opcodes.GetULEB128(&offset); - - if (ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, error_ptr, - tmp)) { - int64_t breg_offset = opcodes.GetSLEB128(&offset); - tmp.ResolveValue(exe_ctx) += (uint64_t)breg_offset; - tmp.ClearContext(); - stack.push_back(tmp); - stack.back().SetValueType(Value::eValueTypeLoadAddress); - } else - return false; - } break; - - case DW_OP_fbreg: - if (exe_ctx) { - if (frame) { - Scalar value; - if (frame->GetFrameBaseValue(value, error_ptr)) { - int64_t fbreg_offset = opcodes.GetSLEB128(&offset); - value += fbreg_offset; - stack.push_back(value); - stack.back().SetValueType(Value::eValueTypeLoadAddress); - } else - return false; - } else { - if (error_ptr) - error_ptr->SetErrorString( - "Invalid stack frame in context for DW_OP_fbreg opcode."); - return false; - } - } else { - if (error_ptr) - error_ptr->SetErrorStringWithFormat( - "NULL execution context for DW_OP_fbreg.\n"); - return false; - } - - break; - - // OPCODE: DW_OP_nop - // OPERANDS: none - // DESCRIPTION: A place holder. It has no effect on the location stack - // or any of its values. - case DW_OP_nop: - break; - - // OPCODE: DW_OP_piece - // OPERANDS: 1 - // ULEB128: byte size of the piece - // DESCRIPTION: The operand describes the size in bytes of the piece of - // the object referenced by the DWARF expression whose result is at the top - // of the stack. If the piece is located in a register, but does not occupy - // the entire register, the placement of the piece within that register is - // defined by the ABI. - // - // Many compilers store a single variable in sets of registers, or store a - // variable partially in memory and partially in registers. DW_OP_piece - // provides a way of describing how large a part of a variable a particular - // DWARF expression refers to. - case DW_OP_piece: { - const uint64_t piece_byte_size = opcodes.GetULEB128(&offset); - - if (piece_byte_size > 0) { - Value curr_piece; - - if (stack.empty()) { - // In a multi-piece expression, this means that the current piece is - // not available. Fill with zeros for now by resizing the data and - // appending it - curr_piece.ResizeData(piece_byte_size); - // Note that "0" is not a correct value for the unknown bits. - // It would be better to also return a mask of valid bits together - // with the expression result, so the debugger can print missing - // members as "" or something. - ::memset(curr_piece.GetBuffer().GetBytes(), 0, piece_byte_size); - pieces.AppendDataToHostBuffer(curr_piece); - } else { - Status error; - // Extract the current piece into "curr_piece" - Value curr_piece_source_value(stack.back()); - stack.pop_back(); - - const Value::ValueType curr_piece_source_value_type = - curr_piece_source_value.GetValueType(); - switch (curr_piece_source_value_type) { - case Value::eValueTypeLoadAddress: - if (process) { - if (curr_piece.ResizeData(piece_byte_size) == piece_byte_size) { - lldb::addr_t load_addr = - curr_piece_source_value.GetScalar().ULongLong( - LLDB_INVALID_ADDRESS); - if (process->ReadMemory( - load_addr, curr_piece.GetBuffer().GetBytes(), - piece_byte_size, error) != piece_byte_size) { - if (error_ptr) - error_ptr->SetErrorStringWithFormat( - "failed to read memory DW_OP_piece(%" PRIu64 - ") from 0x%" PRIx64, - piece_byte_size, load_addr); - return false; - } - } else { - if (error_ptr) - error_ptr->SetErrorStringWithFormat( - "failed to resize the piece memory buffer for " - "DW_OP_piece(%" PRIu64 ")", - piece_byte_size); - return false; - } - } - break; - - case Value::eValueTypeFileAddress: - case Value::eValueTypeHostAddress: - if (error_ptr) { - lldb::addr_t addr = curr_piece_source_value.GetScalar().ULongLong( - LLDB_INVALID_ADDRESS); - error_ptr->SetErrorStringWithFormat( - "failed to read memory DW_OP_piece(%" PRIu64 - ") from %s address 0x%" PRIx64, - piece_byte_size, curr_piece_source_value.GetValueType() == - Value::eValueTypeFileAddress - ? "file" - : "host", - addr); - } - return false; - - case Value::eValueTypeScalar: { - uint32_t bit_size = piece_byte_size * 8; - uint32_t bit_offset = 0; - Scalar &scalar = curr_piece_source_value.GetScalar(); - if (!scalar.ExtractBitfield( - bit_size, bit_offset)) { - if (error_ptr) - error_ptr->SetErrorStringWithFormat( - "unable to extract %" PRIu64 " bytes from a %" PRIu64 - " byte scalar value.", - piece_byte_size, - (uint64_t)curr_piece_source_value.GetScalar() - .GetByteSize()); - return false; - } - // Create curr_piece with bit_size. By default Scalar - // grows to the nearest host integer type. - llvm::APInt fail_value(1, 0, false); - llvm::APInt ap_int = scalar.UInt128(fail_value); - assert(ap_int.getBitWidth() >= bit_size); - llvm::ArrayRef buf{ap_int.getRawData(), - ap_int.getNumWords()}; - curr_piece.GetScalar() = Scalar(llvm::APInt(bit_size, buf)); - } break; - - case Value::eValueTypeVector: { - if (curr_piece_source_value.GetVector().length >= piece_byte_size) - curr_piece_source_value.GetVector().length = piece_byte_size; - else { - if (error_ptr) - error_ptr->SetErrorStringWithFormat( - "unable to extract %" PRIu64 " bytes from a %" PRIu64 - " byte vector value.", - piece_byte_size, - (uint64_t)curr_piece_source_value.GetVector().length); - return false; - } - } break; - } - - // Check if this is the first piece? - if (op_piece_offset == 0) { - // This is the first piece, we should push it back onto the stack - // so subsequent pieces will be able to access this piece and add - // to it. - if (pieces.AppendDataToHostBuffer(curr_piece) == 0) { - if (error_ptr) - error_ptr->SetErrorString("failed to append piece data"); - return false; - } - } else { - // If this is the second or later piece there should be a value on - // the stack. - if (pieces.GetBuffer().GetByteSize() != op_piece_offset) { - if (error_ptr) - error_ptr->SetErrorStringWithFormat( - "DW_OP_piece for offset %" PRIu64 - " but top of stack is of size %" PRIu64, - op_piece_offset, pieces.GetBuffer().GetByteSize()); - return false; - } - - if (pieces.AppendDataToHostBuffer(curr_piece) == 0) { - if (error_ptr) - error_ptr->SetErrorString("failed to append piece data"); - return false; - } - } - } - op_piece_offset += piece_byte_size; - } - } break; - - case DW_OP_bit_piece: // 0x9d ULEB128 bit size, ULEB128 bit offset (DWARF3); - if (stack.size() < 1) { - if (error_ptr) - error_ptr->SetErrorString( - "Expression stack needs at least 1 item for DW_OP_bit_piece."); - return false; - } else { - const uint64_t piece_bit_size = opcodes.GetULEB128(&offset); - const uint64_t piece_bit_offset = opcodes.GetULEB128(&offset); - switch (stack.back().GetValueType()) { - case Value::eValueTypeScalar: { - if (!stack.back().GetScalar().ExtractBitfield(piece_bit_size, - piece_bit_offset)) { - if (error_ptr) - error_ptr->SetErrorStringWithFormat( - "unable to extract %" PRIu64 " bit value with %" PRIu64 - " bit offset from a %" PRIu64 " bit scalar value.", - piece_bit_size, piece_bit_offset, - (uint64_t)(stack.back().GetScalar().GetByteSize() * 8)); - return false; - } - } break; - - case Value::eValueTypeFileAddress: - case Value::eValueTypeLoadAddress: - case Value::eValueTypeHostAddress: - if (error_ptr) { - error_ptr->SetErrorStringWithFormat( - "unable to extract DW_OP_bit_piece(bit_size = %" PRIu64 - ", bit_offset = %" PRIu64 ") from an address value.", - piece_bit_size, piece_bit_offset); - } - return false; - - case Value::eValueTypeVector: - if (error_ptr) { - error_ptr->SetErrorStringWithFormat( - "unable to extract DW_OP_bit_piece(bit_size = %" PRIu64 - ", bit_offset = %" PRIu64 ") from a vector value.", - piece_bit_size, piece_bit_offset); - } - return false; - } - } - break; - - // OPCODE: DW_OP_push_object_address - // OPERANDS: none - // DESCRIPTION: Pushes the address of the object currently being - // evaluated as part of evaluation of a user presented expression. This - // object may correspond to an independent variable described by its own - // DIE or it may be a component of an array, structure, or class whose - // address has been dynamically determined by an earlier step during user - // expression evaluation. - case DW_OP_push_object_address: - if (object_address_ptr) - stack.push_back(*object_address_ptr); - else { - if (error_ptr) - error_ptr->SetErrorString("DW_OP_push_object_address used without " - "specifying an object address"); - return false; - } - break; - - // OPCODE: DW_OP_call2 - // OPERANDS: - // uint16_t compile unit relative offset of a DIE - // DESCRIPTION: Performs subroutine calls during evaluation - // of a DWARF expression. The operand is the 2-byte unsigned offset of a - // debugging information entry in the current compilation unit. - // - // Operand interpretation is exactly like that for DW_FORM_ref2. - // - // This operation transfers control of DWARF expression evaluation to the - // DW_AT_location attribute of the referenced DIE. If there is no such - // attribute, then there is no effect. Execution of the DWARF expression of - // a DW_AT_location attribute may add to and/or remove from values on the - // stack. Execution returns to the point following the call when the end of - // the attribute is reached. Values on the stack at the time of the call - // may be used as parameters by the called expression and values left on - // the stack by the called expression may be used as return values by prior - // agreement between the calling and called expressions. - case DW_OP_call2: - if (error_ptr) - error_ptr->SetErrorString("Unimplemented opcode DW_OP_call2."); - return false; - // OPCODE: DW_OP_call4 - // OPERANDS: 1 - // uint32_t compile unit relative offset of a DIE - // DESCRIPTION: Performs a subroutine call during evaluation of a DWARF - // expression. For DW_OP_call4, the operand is a 4-byte unsigned offset of - // a debugging information entry in the current compilation unit. - // - // Operand interpretation DW_OP_call4 is exactly like that for - // DW_FORM_ref4. - // - // This operation transfers control of DWARF expression evaluation to the - // DW_AT_location attribute of the referenced DIE. If there is no such - // attribute, then there is no effect. Execution of the DWARF expression of - // a DW_AT_location attribute may add to and/or remove from values on the - // stack. Execution returns to the point following the call when the end of - // the attribute is reached. Values on the stack at the time of the call - // may be used as parameters by the called expression and values left on - // the stack by the called expression may be used as return values by prior - // agreement between the calling and called expressions. - case DW_OP_call4: - if (error_ptr) - error_ptr->SetErrorString("Unimplemented opcode DW_OP_call4."); - return false; - - // OPCODE: DW_OP_stack_value - // OPERANDS: None - // DESCRIPTION: Specifies that the object does not exist in memory but - // rather is a constant value. The value from the top of the stack is the - // value to be used. This is the actual object value and not the location. - case DW_OP_stack_value: - if (stack.empty()) { - if (error_ptr) - error_ptr->SetErrorString( - "Expression stack needs at least 1 item for DW_OP_stack_value."); - return false; - } - stack.back().SetValueType(Value::eValueTypeScalar); - break; - - // OPCODE: DW_OP_convert - // OPERANDS: 1 - // A ULEB128 that is either a DIE offset of a - // DW_TAG_base_type or 0 for the generic (pointer-sized) type. - // - // DESCRIPTION: Pop the top stack element, convert it to a - // different type, and push the result. - case DW_OP_convert: { - if (stack.size() < 1) { - if (error_ptr) - error_ptr->SetErrorString( - "Expression stack needs at least 1 item for DW_OP_convert."); - return false; - } - const uint64_t die_offset = opcodes.GetULEB128(&offset); - Scalar::Type type = Scalar::e_void; - uint64_t bit_size; - if (die_offset == 0) { - // The generic type has the size of an address on the target - // machine and an unspecified signedness. Scalar has no - // "unspecified signedness", so we use unsigned types. - if (!module_sp) { - if (error_ptr) - error_ptr->SetErrorString("No module"); - return false; - } - bit_size = module_sp->GetArchitecture().GetAddressByteSize() * 8; - if (!bit_size) { - if (error_ptr) - error_ptr->SetErrorString("unspecified architecture"); - return false; - } - type = Scalar::GetBestTypeForBitSize(bit_size, false); - } else { - // Retrieve the type DIE that the value is being converted to. - // FIXME: the constness has annoying ripple effects. - DWARFDIE die = const_cast(dwarf_cu)->GetDIE(die_offset); - if (!die) { - if (error_ptr) - error_ptr->SetErrorString("Cannot resolve DW_OP_convert type DIE"); - return false; - } - uint64_t encoding = - die.GetAttributeValueAsUnsigned(DW_AT_encoding, DW_ATE_hi_user); - bit_size = die.GetAttributeValueAsUnsigned(DW_AT_byte_size, 0) * 8; - if (!bit_size) - bit_size = die.GetAttributeValueAsUnsigned(DW_AT_bit_size, 0); - if (!bit_size) { - if (error_ptr) - error_ptr->SetErrorString("Unsupported type size in DW_OP_convert"); - return false; - } - switch (encoding) { - case DW_ATE_signed: - case DW_ATE_signed_char: - type = Scalar::GetBestTypeForBitSize(bit_size, true); - break; - case DW_ATE_unsigned: - case DW_ATE_unsigned_char: - type = Scalar::GetBestTypeForBitSize(bit_size, false); - break; - default: - if (error_ptr) - error_ptr->SetErrorString("Unsupported encoding in DW_OP_convert"); - return false; - } - } - if (type == Scalar::e_void) { - if (error_ptr) - error_ptr->SetErrorString("Unsupported pointer size"); - return false; - } - Scalar &top = stack.back().ResolveValue(exe_ctx); - top.TruncOrExtendTo(type, bit_size); - break; - } - - // OPCODE: DW_OP_call_frame_cfa - // OPERANDS: None - // DESCRIPTION: Specifies a DWARF expression that pushes the value of - // the canonical frame address consistent with the call frame information - // located in .debug_frame (or in the FDEs of the eh_frame section). - case DW_OP_call_frame_cfa: - if (frame) { - // Note that we don't have to parse FDEs because this DWARF expression - // is commonly evaluated with a valid stack frame. - StackID id = frame->GetStackID(); - addr_t cfa = id.GetCallFrameAddress(); - if (cfa != LLDB_INVALID_ADDRESS) { - stack.push_back(Scalar(cfa)); - stack.back().SetValueType(Value::eValueTypeLoadAddress); - } else if (error_ptr) - error_ptr->SetErrorString("Stack frame does not include a canonical " - "frame address for DW_OP_call_frame_cfa " - "opcode."); - } else { - if (error_ptr) - error_ptr->SetErrorString("Invalid stack frame in context for " - "DW_OP_call_frame_cfa opcode."); - return false; - } - break; - - // OPCODE: DW_OP_form_tls_address (or the old pre-DWARFv3 vendor extension - // opcode, DW_OP_GNU_push_tls_address) - // OPERANDS: none - // DESCRIPTION: Pops a TLS offset from the stack, converts it to - // an address in the current thread's thread-local storage block, and - // pushes it on the stack. - case DW_OP_form_tls_address: - case DW_OP_GNU_push_tls_address: { - if (stack.size() < 1) { - if (error_ptr) { - if (op == DW_OP_form_tls_address) - error_ptr->SetErrorString( - "DW_OP_form_tls_address needs an argument."); - else - error_ptr->SetErrorString( - "DW_OP_GNU_push_tls_address needs an argument."); - } - return false; - } - - if (!exe_ctx || !module_sp) { - if (error_ptr) - error_ptr->SetErrorString("No context to evaluate TLS within."); - return false; - } - - Thread *thread = exe_ctx->GetThreadPtr(); - if (!thread) { - if (error_ptr) - error_ptr->SetErrorString("No thread to evaluate TLS within."); - return false; - } - - // Lookup the TLS block address for this thread and module. - const addr_t tls_file_addr = - stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS); - const addr_t tls_load_addr = - thread->GetThreadLocalData(module_sp, tls_file_addr); - - if (tls_load_addr == LLDB_INVALID_ADDRESS) { - if (error_ptr) - error_ptr->SetErrorString( - "No TLS data currently exists for this thread."); - return false; - } - - stack.back().GetScalar() = tls_load_addr; - stack.back().SetValueType(Value::eValueTypeLoadAddress); - } break; - - // OPCODE: DW_OP_addrx (DW_OP_GNU_addr_index is the legacy name.) - // OPERANDS: 1 - // ULEB128: index to the .debug_addr section - // DESCRIPTION: Pushes an address to the stack from the .debug_addr - // section with the base address specified by the DW_AT_addr_base attribute - // and the 0 based index is the ULEB128 encoded index. - case DW_OP_addrx: - case DW_OP_GNU_addr_index: { - if (!dwarf_cu) { - if (error_ptr) - error_ptr->SetErrorString("DW_OP_GNU_addr_index found without a " - "compile unit being specified"); - return false; - } - uint64_t index = opcodes.GetULEB128(&offset); - lldb::addr_t value = ReadAddressFromDebugAddrSection(dwarf_cu, index); - stack.push_back(Scalar(value)); - stack.back().SetValueType(Value::eValueTypeFileAddress); - } break; - - // OPCODE: DW_OP_GNU_const_index - // OPERANDS: 1 - // ULEB128: index to the .debug_addr section - // DESCRIPTION: Pushes an constant with the size of a machine address to - // the stack from the .debug_addr section with the base address specified - // by the DW_AT_addr_base attribute and the 0 based index is the ULEB128 - // encoded index. - case DW_OP_GNU_const_index: { - if (!dwarf_cu) { - if (error_ptr) - error_ptr->SetErrorString("DW_OP_GNU_const_index found without a " - "compile unit being specified"); - return false; - } - uint64_t index = opcodes.GetULEB128(&offset); - lldb::addr_t value = ReadAddressFromDebugAddrSection(dwarf_cu, index); - stack.push_back(Scalar(value)); - } break; - - case DW_OP_entry_value: { - if (!Evaluate_DW_OP_entry_value(stack, exe_ctx, reg_ctx, opcodes, offset, - error_ptr, log)) { - LLDB_ERRORF(error_ptr, "Could not evaluate %s.", - DW_OP_value_to_name(op)); - return false; - } - break; - } - - default: - LLDB_LOGF(log, "Unhandled opcode %s in DWARFExpression.", - DW_OP_value_to_name(op)); - break; } } @@ -2942,4 +1312,3 @@ return MatchRegOp(*reg)(operand); } } - diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp --- a/lldb/source/Interpreter/CommandInterpreter.cpp +++ b/lldb/source/Interpreter/CommandInterpreter.cpp @@ -735,6 +735,24 @@ } } + std::unique_ptr connect_wasm_cmd_up( + new CommandObjectRegexCommand( + *this, "wasm", + "Connect to a WebAssembly process via remote GDB server. " + "If no host is specifed, localhost is assumed.", + "wasm [:]", 2, 0, false)); + if (connect_wasm_cmd_up) { + if (connect_wasm_cmd_up->AddRegexCommand( + "^([^:]+|\\[[0-9a-fA-F:]+.*\\]):([0-9]+)$", + "process connect --plugin wasm connect://%1:%2") && + connect_wasm_cmd_up->AddRegexCommand( + "^([[:digit:]]+)$", + "process connect --plugin wasm connect://localhost:%1")) { + CommandObjectSP command_sp(connect_wasm_cmd_up.release()); + m_command_dict[std::string(command_sp->GetCommandName())] = command_sp; + } + } + std::unique_ptr connect_kdp_remote_cmd_up( new CommandObjectRegexCommand( *this, "kdp-remote", diff --git a/lldb/source/Plugins/CMakeLists.txt b/lldb/source/Plugins/CMakeLists.txt --- a/lldb/source/Plugins/CMakeLists.txt +++ b/lldb/source/Plugins/CMakeLists.txt @@ -2,6 +2,7 @@ add_subdirectory(Architecture) add_subdirectory(Disassembler) add_subdirectory(DynamicLoader) +add_subdirectory(DWARFEvaluator) add_subdirectory(ExpressionParser) add_subdirectory(Instruction) add_subdirectory(InstrumentationRuntime) @@ -30,6 +31,7 @@ # FIXME: ProcessWindowsCommon needs to be initialized after all other process # plugins but before ProcessGDBRemote. set(LLDB_PROCESS_WINDOWS_PLUGIN "") +set(LLDB_PROCESS_WASM_PLUGIN "") set(LLDB_PROCESS_GDB_PLUGIN "") foreach(p ${LLDB_ALL_PLUGINS}) @@ -41,6 +43,8 @@ set(LLDB_PROCESS_WINDOWS_PLUGIN "LLDB_PLUGIN(${pStripped})\n") elseif(${pStripped} STREQUAL "ProcessGDBRemote") set(LLDB_PROCESS_GDB_PLUGIN "LLDB_PLUGIN(${pStripped})\n") + elseif(${pStripped} STREQUAL "ProcessWasm") + set(LLDB_PROCESS_WASM_PLUGIN "LLDB_PLUGIN(WasmProcess)\n") else() set(LLDB_ENUM_PLUGINS "${LLDB_ENUM_PLUGINS}LLDB_PLUGIN(${pStripped})\n") endif() diff --git a/lldb/source/Plugins/DWARFEvaluator/CMakeLists.txt b/lldb/source/Plugins/DWARFEvaluator/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/DWARFEvaluator/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(wasm) diff --git a/lldb/source/Plugins/DWARFEvaluator/wasm/CMakeLists.txt b/lldb/source/Plugins/DWARFEvaluator/wasm/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/DWARFEvaluator/wasm/CMakeLists.txt @@ -0,0 +1,9 @@ +add_lldb_library(lldbPluginWasmDWARFEvaluator PLUGIN + WasmDWARFEvaluator.cpp + + LINK_LIBS + lldbCore + lldbHost + lldbSymbol + lldbPluginObjectFileWasm + ) diff --git a/lldb/source/Plugins/DWARFEvaluator/wasm/WasmDWARFEvaluator.h b/lldb/source/Plugins/DWARFEvaluator/wasm/WasmDWARFEvaluator.h new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/DWARFEvaluator/wasm/WasmDWARFEvaluator.h @@ -0,0 +1,52 @@ +//===-- WasmDWARFEvaluator.h ------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_DWARFEVALUATOR_WASM_WASMDWARFEVALUATOR_H +#define LLDB_SOURCE_PLUGINS_DWARFEVALUATOR_WASM_WASMDWARFEVALUATOR_H + +#include "lldb/Expression/DWARFEvaluator.h" +#include "lldb/lldb-private.h" + +namespace lldb_private { +namespace wasm { + +class WasmDWARFEvaluator : public DWARFEvaluator { +public: + static void Initialize(); + static void Terminate(); + static lldb_private::ConstString GetPluginNameStatic(); + static const char *GetPluginDescriptionStatic(); + + static lldb_private::DWARFEvaluator * + CreateInstance(const lldb::ModuleSP &module_sp); + + /// PluginInterface protocol. + /// \{ + lldb_private::ConstString GetPluginName() override { + return GetPluginNameStatic(); + } + uint32_t GetPluginVersion() override { return 1; } + /// \} + + bool Evaluate(uint8_t op, ExecutionContext *exe_ctx, RegisterContext *reg_ctx, + Process *process, StackFrame *frame, std::vector &stack, + lldb::ModuleSP module_sp, const DWARFUnit *dwarf_cu, + const lldb::RegisterKind reg_kind, const DataExtractor &opcodes, + lldb::offset_t &offset, Value &pieces, + uint64_t &op_piece_offset, const Value *object_address_ptr, + Log *log, Value &result, Status *error_ptr) override; + +private: + WasmDWARFEvaluator() {} + DISALLOW_COPY_AND_ASSIGN(WasmDWARFEvaluator); +}; + +} // namespace wasm +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_DWARFEVALUATOR_WASM_WASMDWARFEVALUATOR_H diff --git a/lldb/source/Plugins/DWARFEvaluator/wasm/WasmDWARFEvaluator.cpp b/lldb/source/Plugins/DWARFEvaluator/wasm/WasmDWARFEvaluator.cpp new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/DWARFEvaluator/wasm/WasmDWARFEvaluator.cpp @@ -0,0 +1,132 @@ +//===-- WasmDWARFEvaluator.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 "WasmDWARFEvaluator.h" + +#include "Plugins/ObjectFile/wasm/ObjectFileWasm.h" +#include "Plugins/Process/wasm/WasmProcess.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/dwarf.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::wasm; + +LLDB_PLUGIN_DEFINE(WasmDWARFEvaluator) + +void WasmDWARFEvaluator::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance); +} + +void WasmDWARFEvaluator::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +lldb_private::ConstString WasmDWARFEvaluator::GetPluginNameStatic() { + static ConstString g_name("WASM"); + return g_name; +} + +const char *WasmDWARFEvaluator::GetPluginDescriptionStatic() { + return "DWARF expression evaluator for WASM."; +} + +// CreateInstance +// +// Platforms can register a callback to use when creating DWARF expression +// evaluators to allow handling platform-specific DWARF codes. +DWARFEvaluator * +WasmDWARFEvaluator::CreateInstance(const lldb::ModuleSP &module_sp) { + if (!module_sp) + return nullptr; + + ObjectFileWasm *obj_file = + llvm::dyn_cast_or_null(module_sp->GetObjectFile()); + if (!obj_file) + return nullptr; + + return new WasmDWARFEvaluator(); +} + +bool WasmDWARFEvaluator::Evaluate( + uint8_t op, ExecutionContext *exe_ctx, RegisterContext *reg_ctx, + Process *process, StackFrame *frame, std::vector &stack, + lldb::ModuleSP module_sp, const DWARFUnit *dwarf_cu, + const lldb::RegisterKind reg_kind, const DataExtractor &opcodes, + lldb::offset_t &offset, Value &pieces, uint64_t &op_piece_offset, + const Value *object_address_ptr, Log *log, Value &result, + Status *error_ptr) { + switch (op) { + case DW_OP_WASM_location: { + if (frame) { + const llvm::Triple::ArchType machine = + frame->CalculateTarget()->GetArchitecture().GetMachine(); + if (machine != llvm::Triple::wasm32) { + if (error_ptr) + error_ptr->SetErrorString("Invalid target architecture for " + "DW_OP_WASM_location opcode."); + return false; + } + + WasmProcess *wasm_process = + static_cast(frame->CalculateProcess().get()); + int frame_index = frame->GetConcreteFrameIndex(); + uint64_t wasm_op = opcodes.GetULEB128(&offset); + uint64_t index = opcodes.GetULEB128(&offset); + uint8_t buf[16]; + size_t size = 0; + switch (wasm_op) { + case 0: // Local + if (!wasm_process->GetWasmLocal(frame_index, index, buf, 16, size)) { + return false; + } + break; + case 1: // Global + if (!wasm_process->GetWasmGlobal(frame_index, index, buf, 16, size)) { + return false; + } + break; + case 2: // Operand Stack + if (!wasm_process->GetWasmStackValue(frame_index, index, buf, 16, + size)) { + return false; + } + break; + default: + return false; + } + + if (size == sizeof(uint32_t)) { + uint32_t value; + memcpy(&value, buf, size); + stack.push_back(Scalar(value)); + } else if (size == sizeof(uint64_t)) { + uint64_t value; + memcpy(&value, buf, size); + stack.push_back(Scalar(value)); + } else + return false; + } else { + if (error_ptr) + error_ptr->SetErrorString("Invalid stack frame in context for " + "DW_OP_WASM_location opcode."); + return false; + } + } break; + + default: + return DWARFEvaluator::Evaluate(op, exe_ctx, reg_ctx, process, frame, stack, + module_sp, dwarf_cu, reg_kind, opcodes, + offset, pieces, op_piece_offset, + object_address_ptr, log, result, error_ptr); + } + return true; +} diff --git a/lldb/source/Plugins/Plugins.def.in b/lldb/source/Plugins/Plugins.def.in --- a/lldb/source/Plugins/Plugins.def.in +++ b/lldb/source/Plugins/Plugins.def.in @@ -31,6 +31,7 @@ @LLDB_ENUM_PLUGINS@ @LLDB_PROCESS_WINDOWS_PLUGIN@ +@LLDB_PROCESS_WASM_PLUGIN@ @LLDB_PROCESS_GDB_PLUGIN@ #undef LLDB_PLUGIN diff --git a/lldb/source/Plugins/Process/CMakeLists.txt b/lldb/source/Plugins/Process/CMakeLists.txt --- a/lldb/source/Plugins/Process/CMakeLists.txt +++ b/lldb/source/Plugins/Process/CMakeLists.txt @@ -17,3 +17,4 @@ add_subdirectory(elf-core) add_subdirectory(mach-core) add_subdirectory(minidump) +add_subdirectory(wasm) diff --git a/lldb/source/Plugins/Process/wasm/CMakeLists.txt b/lldb/source/Plugins/Process/wasm/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/Process/wasm/CMakeLists.txt @@ -0,0 +1,11 @@ + +add_lldb_library(lldbPluginProcessWasm PLUGIN + WasmProcess.cpp + UnwindWasm.cpp + + LINK_LIBS + lldbCore + ${LLDB_PLUGINS} + LINK_COMPONENTS + Support + ) diff --git a/lldb/source/Plugins/Process/wasm/UnwindWasm.h b/lldb/source/Plugins/Process/wasm/UnwindWasm.h new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/Process/wasm/UnwindWasm.h @@ -0,0 +1,49 @@ +//===-- UnwindWasm.h --------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_UnwindWasm_h_ +#define lldb_UnwindWasm_h_ + +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Unwind.h" +#include + +namespace lldb_private { +namespace wasm { + +class UnwindWasm : public lldb_private::Unwind { +public: + UnwindWasm(lldb_private::Thread &thread) + : Unwind(thread), m_frames(), m_unwind_complete(false) {} + ~UnwindWasm() override = default; + +protected: + void DoClear() override { + m_frames.clear(); + m_unwind_complete = false; + } + + uint32_t DoGetFrameCount() override; + + bool DoGetFrameInfoAtIndex(uint32_t frame_idx, lldb::addr_t &cfa, + lldb::addr_t &pc, + bool &behaves_like_zeroth_frame) override; + lldb::RegisterContextSP + DoCreateRegisterContextForFrame(lldb_private::StackFrame *frame) override; + +private: + std::vector m_frames; + bool m_unwind_complete; + + DISALLOW_COPY_AND_ASSIGN(UnwindWasm); +}; + +} // namespace wasm +} // namespace lldb_private + +#endif // lldb_UnwindWasm_h_ diff --git a/lldb/source/Plugins/Process/wasm/UnwindWasm.cpp b/lldb/source/Plugins/Process/wasm/UnwindWasm.cpp new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/Process/wasm/UnwindWasm.cpp @@ -0,0 +1,75 @@ +//===-- UnwindWasm.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 "UnwindWasm.h" +#include "Plugins/Process/gdb-remote/ThreadGDBRemote.h" +#include "Plugins/Process/wasm/WasmProcess.h" + +using namespace lldb; +using namespace lldb_private; +using namespace process_gdb_remote; +using namespace wasm; + +class WasmGDBRemoteRegisterContext : public GDBRemoteRegisterContext { +public: + WasmGDBRemoteRegisterContext(ThreadGDBRemote &thread, + uint32_t concrete_frame_idx, + GDBRemoteDynamicRegisterInfo ®_info, + uint64_t pc) + : GDBRemoteRegisterContext(thread, concrete_frame_idx, reg_info, false, + false) { + PrivateSetRegisterValue(0, pc); + } +}; + +lldb::RegisterContextSP +UnwindWasm::DoCreateRegisterContextForFrame(lldb_private::StackFrame *frame) { + if (m_frames.size() <= frame->GetFrameIndex()) { + return lldb::RegisterContextSP(); + } + + ThreadSP thread = frame->GetThread(); + ThreadGDBRemote *gdb_thread = static_cast(thread.get()); + WasmProcess *wasm_process = + static_cast(thread->GetProcess().get()); + std::shared_ptr reg_ctx_sp = + std::make_shared( + *gdb_thread, frame->GetConcreteFrameIndex(), + wasm_process->GetRegisterInfo(), m_frames[frame->GetFrameIndex()]); + return reg_ctx_sp; +} + +uint32_t UnwindWasm::DoGetFrameCount() { + if (!m_unwind_complete) { + m_unwind_complete = true; + m_frames.clear(); + + WasmProcess *wasm_process = + static_cast(GetThread().GetProcess().get()); + if (wasm_process) + if (!wasm_process->GetWasmCallStack(m_frames)) + m_frames.clear(); + } + return m_frames.size(); +} + +bool UnwindWasm::DoGetFrameInfoAtIndex(uint32_t frame_idx, lldb::addr_t &cfa, + lldb::addr_t &pc, + bool &behaves_like_zeroth_frame) { + if (m_frames.size() == 0) { + DoGetFrameCount(); + } + + if (frame_idx < m_frames.size()) { + behaves_like_zeroth_frame = (frame_idx == 0); + cfa = 0; + pc = m_frames[frame_idx]; + return true; + } + return false; +} \ No newline at end of file diff --git a/lldb/source/Plugins/Process/wasm/WasmProcess.h b/lldb/source/Plugins/Process/wasm/WasmProcess.h new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/Process/wasm/WasmProcess.h @@ -0,0 +1,76 @@ +//===-- WasmProcess.h -------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_WASM_PROCESSWASM_H +#define LLDB_SOURCE_PLUGINS_PROCESS_WASM_PROCESSWASM_H + +#include "Plugins/Process/gdb-remote/ProcessGDBRemote.h" +#include "lldb/Target/RegisterContext.h" + +namespace lldb_private { +namespace wasm { + +// WasmProcess provides the access to the Wasm program state +// retrieved from the Wasm engine. +class WasmProcess : public process_gdb_remote::ProcessGDBRemote { +public: + WasmProcess(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp); + + ~WasmProcess() override; + + static lldb::ProcessSP CreateInstance(lldb::TargetSP target_sp, + lldb::ListenerSP listener_sp, + const FileSpec *crash_file_path); + + static void Initialize(); + + static void DebuggerInitialize(Debugger &debugger); + + static void Terminate(); + + static ConstString GetPluginNameStatic(); + + static const char *GetPluginDescriptionStatic(); + + // PluginInterface protocol + ConstString GetPluginName() override; + + uint32_t GetPluginVersion() override; + + // Process + size_t ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, + Status &error) override; + + // WasmProcess + bool GetWasmLocal(int frame_index, int index, void *buf, size_t buffer_size, + size_t &size); + + bool GetWasmGlobal(int frame_index, int index, void *buf, size_t buffer_size, + size_t &size); + + bool GetWasmStackValue(int frame_index, int index, void *buf, + size_t buffer_size, size_t &size); + + bool WasmReadMemory(int frame_index, lldb::addr_t addr, void *buf, + size_t buffer_size); + + bool GetWasmCallStack(std::vector &call_stack_pcs); + +private: + friend class UnwindWasm; + process_gdb_remote::GDBRemoteDynamicRegisterInfo &GetRegisterInfo() { + return m_register_info; + } + + DISALLOW_COPY_AND_ASSIGN(WasmProcess); +}; + +} // namespace wasm +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_WASM_PROCESSWASM_H diff --git a/lldb/source/Plugins/Process/wasm/WasmProcess.cpp b/lldb/source/Plugins/Process/wasm/WasmProcess.cpp new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/Process/wasm/WasmProcess.cpp @@ -0,0 +1,211 @@ +//===-- WasmProcess.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 "WasmProcess.h" +#include "Plugins/Process/gdb-remote/ThreadGDBRemote.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Utility/DataBufferHeap.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_gdb_remote; +using namespace lldb_private::wasm; + +LLDB_PLUGIN_DEFINE(WasmProcess) + +// ProcessGDBRemote constructor +WasmProcess::WasmProcess(lldb::TargetSP target_sp, ListenerSP listener_sp) + : ProcessGDBRemote(target_sp, listener_sp) {} + +// Destructor +WasmProcess::~WasmProcess() {} + +void WasmProcess::Initialize() { + static llvm::once_flag g_once_flag; + + llvm::call_once(g_once_flag, []() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance, + DebuggerInitialize); + }); +} + +void WasmProcess::DebuggerInitialize(Debugger &debugger) { + ProcessGDBRemote::DebuggerInitialize(debugger); +} + +// PluginInterface +ConstString WasmProcess::GetPluginName() { return GetPluginNameStatic(); } + +uint32_t WasmProcess::GetPluginVersion() { return 1; } + +ConstString WasmProcess::GetPluginNameStatic() { + static ConstString g_name("wasm"); + return g_name; +} + +const char *WasmProcess::GetPluginDescriptionStatic() { + return "GDB Remote protocol based WebAssembly debugging plug-in."; +} + +void WasmProcess::Terminate() { + PluginManager::UnregisterPlugin(WasmProcess::CreateInstance); +} + +lldb::ProcessSP WasmProcess::CreateInstance(lldb::TargetSP target_sp, + ListenerSP listener_sp, + const FileSpec *crash_file_path) { + lldb::ProcessSP process_sp; + if (crash_file_path == nullptr) + process_sp = std::make_shared(target_sp, listener_sp); + return process_sp; +} + +size_t WasmProcess::ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, + Status &error) { + if (vm_addr < 0x100000000) { + if (WasmReadMemory(0 /*frame_index*/, vm_addr, buf, size)) { + return size; + } + return 0; + } else + return ProcessGDBRemote::ReadMemory(vm_addr, buf, size, error); +} + +bool WasmProcess::GetWasmLocal(int frame_index, int index, void *buf, + size_t buffer_size, size_t &size) { + StreamString packet; + packet.Printf("qWasmLocal:"); + packet.Printf("%d;%d", frame_index, index); + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response, + false) != + GDBRemoteCommunication::PacketResult::Success) { + return false; + } + + if (!response.IsNormalResponse()) { + return false; + } + + DataBufferSP buffer_sp( + new DataBufferHeap(response.GetStringRef().size() / 2, 0)); + response.GetHexBytes(buffer_sp->GetData(), '\xcc'); + size = buffer_sp->GetByteSize(); + if (size <= buffer_size) { + memcpy(buf, buffer_sp->GetBytes(), size); + return true; + } + + return false; +} + +bool WasmProcess::GetWasmGlobal(int frame_index, int index, void *buf, + size_t buffer_size, size_t &size) { + StreamString packet; + packet.PutCString("qWasmGlobal:"); + packet.Printf("%d;%d", frame_index, index); + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response, + false) != + GDBRemoteCommunication::PacketResult::Success) { + return false; + } + + if (!response.IsNormalResponse()) { + return false; + } + + DataBufferSP buffer_sp( + new DataBufferHeap(response.GetStringRef().size() / 2, 0)); + response.GetHexBytes(buffer_sp->GetData(), '\xcc'); + size = buffer_sp->GetByteSize(); + if (size <= buffer_size) { + memcpy(buf, buffer_sp->GetBytes(), size); + return true; + } + + return false; +} + +bool WasmProcess::GetWasmStackValue(int frame_index, int index, void *buf, + size_t buffer_size, size_t &size) { + StreamString packet; + packet.PutCString("qWasmStackValue:"); + packet.Printf("%d;%d", frame_index, index); + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response, + false) != + GDBRemoteCommunication::PacketResult::Success) { + return false; + } + + if (!response.IsNormalResponse()) { + return false; + } + + DataBufferSP buffer_sp( + new DataBufferHeap(response.GetStringRef().size() / 2, 0)); + response.GetHexBytes(buffer_sp->GetData(), '\xcc'); + size = buffer_sp->GetByteSize(); + if (size <= buffer_size) { + memcpy(buf, buffer_sp->GetBytes(), size); + return true; + } + + return false; +} + +bool WasmProcess::WasmReadMemory(int frame_index, lldb::addr_t addr, void *buf, + size_t buffer_size) { + char packet[64]; + int packet_len = ::snprintf( + packet, sizeof(packet), "qWasmMem:%d;%" PRIx64 ";%" PRIx64, frame_index, + static_cast(addr), static_cast(buffer_size)); + assert(packet_len + 1 < (int)sizeof(packet)); + UNUSED_IF_ASSERT_DISABLED(packet_len); + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse(packet, response, true) == + GDBRemoteCommunication::PacketResult::Success) { + if (response.IsNormalResponse()) { + return buffer_size == + response.GetHexBytes(llvm::MutableArrayRef( + static_cast(buf), buffer_size), + '\xdd'); + } + } + return false; +} + +bool WasmProcess::GetWasmCallStack(std::vector &call_stack_pcs) { + call_stack_pcs.clear(); + StreamString packet; + packet.Printf("qWasmCallStack"); + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response, + false) != + GDBRemoteCommunication::PacketResult::Success) { + return false; + } + + if (!response.IsNormalResponse()) { + return false; + } + + addr_t buf[1024 / sizeof(addr_t)]; + size_t bytes = response.GetHexBytes( + llvm::MutableArrayRef((uint8_t *)buf, sizeof(buf)), '\xdd'); + if (bytes == 0) { + return false; + } + + for (size_t i = 0; i < bytes / sizeof(addr_t); i++) { + call_stack_pcs.push_back(buf[i]); + } + return true; +} diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp --- a/lldb/source/Target/Thread.cpp +++ b/lldb/source/Target/Thread.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "lldb/Target/Thread.h" +#include "Plugins/Process/wasm/UnwindWasm.h" #include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/FormatEntity.h" @@ -1853,8 +1854,13 @@ } Unwind &Thread::GetUnwinder() { - if (!m_unwinder_up) - m_unwinder_up.reset(new UnwindLLDB(*this)); + if (!m_unwinder_up) { + if (CalculateTarget()->GetArchitecture().GetMachine() == + llvm::Triple::wasm32) + m_unwinder_up.reset(new wasm::UnwindWasm(*this)); + else + m_unwinder_up.reset(new UnwindLLDB(*this)); + } return *m_unwinder_up; }