| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/developer/debug/zxdb/symbols/dwarf_expr_eval.h" |
| |
| #include <inttypes.h> |
| #include <lib/syslog/cpp/macros.h> |
| #include <stdlib.h> |
| |
| #include <utility> |
| |
| #include "llvm/BinaryFormat/Dwarf.h" |
| #include "llvm/Support/DataExtractor.h" |
| #include "src/developer/debug/shared/message_loop.h" |
| #include "src/developer/debug/zxdb/symbols/arch.h" |
| #include "src/developer/debug/zxdb/symbols/symbol_data_provider.h" |
| #include "src/lib/fxl/strings/string_printf.h" |
| |
| namespace zxdb { |
| |
| DwarfExprEval::DwarfExprEval() |
| : symbol_context_(SymbolContext::ForRelativeAddresses()), weak_factory_(this) {} |
| |
| DwarfExprEval::~DwarfExprEval() { |
| // This assertion verifies that this class was not accidentally deleted from |
| // within the completion callback. This class is not set up to handle this |
| // case. |
| FX_CHECK(!in_completion_callback_); |
| } |
| |
| void DwarfExprEval::Push(StackEntry value) { stack_.push_back(value); } |
| |
| DwarfExprEval::ResultType DwarfExprEval::GetResultType() const { |
| FX_DCHECK(is_complete_); |
| FX_DCHECK(is_success_); |
| |
| if (!result_data_.empty()) |
| return ResultType::kData; |
| return result_type_; |
| } |
| |
| DwarfExprEval::StackEntry DwarfExprEval::GetResult() const { |
| FX_DCHECK(is_complete_); |
| FX_DCHECK(is_success_); |
| return stack_.back(); |
| } |
| |
| DwarfExprEval::Completion DwarfExprEval::Eval(fxl::RefPtr<SymbolDataProvider> data_provider, |
| const SymbolContext& symbol_context, Expression expr, |
| CompletionCallback cb) { |
| is_complete_ = false; |
| data_provider_ = std::move(data_provider); |
| symbol_context_ = symbol_context; |
| expr_ = std::move(expr); |
| expr_index_ = 0; |
| completion_callback_ = std::move(cb); |
| |
| if (!expr_.empty()) { |
| // Assume little-endian. |
| data_extractor_ = std::make_unique<llvm::DataExtractor>( |
| llvm::StringRef(reinterpret_cast<const char*>(&expr_[0]), expr_.size()), true, |
| kTargetPointerSize); |
| } |
| |
| // Note: ContinueEval() may call callback, which may delete |this| |
| return ContinueEval() ? Completion::kSync : Completion::kAsync; |
| } |
| |
| bool DwarfExprEval::ContinueEval() { |
| // To allow interruption, only a certain number of instructions will be |
| // executed in sequence without posting back to the message loop. This |
| // gives calling code the chance to cancel long or hung executions. Since |
| // most programs are 1-4 instructions, the threshold can be low. |
| constexpr int kMaxInstructionsAtOnce = 32; |
| int instruction_count = 0; |
| |
| do { |
| // Check for successfully reaching the end of the stream. |
| if (!is_complete_ && expr_index_ == expr_.size()) { |
| data_provider_.reset(); |
| is_complete_ = true; |
| Err err; |
| if (stack_.empty() && result_data_.empty()) { |
| // Failure to compute any values. |
| err = Err("DWARF expression produced no results."); |
| is_success_ = false; |
| } else { |
| is_success_ = true; |
| } |
| |
| in_completion_callback_ = true; |
| completion_callback_(this, err); |
| completion_callback_ = {}; |
| in_completion_callback_ = false; |
| return is_complete_; |
| } |
| |
| if (instruction_count == kMaxInstructionsAtOnce) { |
| // Enough instructions have run at once. Schedule a callback to continue |
| // execution in the message loop. |
| debug_ipc::MessageLoop::Current()->PostTask(FROM_HERE, |
| [weak_eval = weak_factory_.GetWeakPtr()]() { |
| if (weak_eval) |
| weak_eval->ContinueEval(); |
| }); |
| return is_complete_; |
| } |
| instruction_count++; |
| } while (!is_complete_ && EvalOneOp() == Completion::kSync); |
| return is_complete_; |
| } |
| |
| DwarfExprEval::Completion DwarfExprEval::EvalOneOp() { |
| FX_DCHECK(!is_complete_); |
| FX_DCHECK(expr_index_ < expr_.size()); |
| |
| // Clear any current register information. See current_register_id_ declaration for more. |
| current_register_id_ = debug_ipc::RegisterID::kUnknown; |
| |
| // Opcode is next byte in the data buffer. Consume it. |
| uint8_t op = expr_[expr_index_]; |
| expr_index_++; |
| |
| // Literals 0-31. |
| if (op >= llvm::dwarf::DW_OP_lit0 && op <= llvm::dwarf::DW_OP_lit31) { |
| Push(op - llvm::dwarf::DW_OP_lit0); |
| return Completion::kSync; |
| } |
| |
| // Registers 0-31. |
| if (op >= llvm::dwarf::DW_OP_reg0 && op <= llvm::dwarf::DW_OP_reg31) { |
| result_type_ = ResultType::kValue; |
| return PushRegisterWithOffset(op - llvm::dwarf::DW_OP_reg0, 0); |
| } |
| |
| // Base register with SLEB128 offset. |
| if (op >= llvm::dwarf::DW_OP_breg0 && op <= llvm::dwarf::DW_OP_breg31) |
| return OpBreg(op); |
| |
| switch (op) { |
| case llvm::dwarf::DW_OP_addr: |
| return OpAddr(); |
| case llvm::dwarf::DW_OP_const1u: |
| return OpPushUnsigned(1); |
| case llvm::dwarf::DW_OP_const1s: |
| return OpPushSigned(1); |
| case llvm::dwarf::DW_OP_const2u: |
| return OpPushUnsigned(2); |
| case llvm::dwarf::DW_OP_const2s: |
| return OpPushSigned(2); |
| case llvm::dwarf::DW_OP_const4u: |
| return OpPushUnsigned(4); |
| case llvm::dwarf::DW_OP_const4s: |
| return OpPushSigned(4); |
| case llvm::dwarf::DW_OP_const8u: |
| return OpPushUnsigned(8); |
| case llvm::dwarf::DW_OP_const8s: |
| return OpPushSigned(8); |
| case llvm::dwarf::DW_OP_constu: |
| return OpPushLEBUnsigned(); |
| case llvm::dwarf::DW_OP_consts: |
| return OpPushLEBSigned(); |
| case llvm::dwarf::DW_OP_dup: |
| return OpDup(); |
| case llvm::dwarf::DW_OP_drop: |
| return OpDrop(); |
| case llvm::dwarf::DW_OP_over: |
| return OpOver(); |
| case llvm::dwarf::DW_OP_pick: |
| return OpPick(); |
| case llvm::dwarf::DW_OP_swap: |
| return OpSwap(); |
| case llvm::dwarf::DW_OP_rot: |
| return OpRot(); |
| case llvm::dwarf::DW_OP_xderef: |
| // We don't have multiple address spaces. |
| ReportUnimplementedOpcode(op); |
| return Completion::kSync; |
| case llvm::dwarf::DW_OP_abs: |
| return OpUnary( |
| [](StackEntry a) { return static_cast<StackEntry>(llabs(static_cast<long long>(a))); }); |
| case llvm::dwarf::DW_OP_and: |
| return OpBinary([](StackEntry a, StackEntry b) { return a & b; }); |
| case llvm::dwarf::DW_OP_div: |
| return OpDiv(); |
| case llvm::dwarf::DW_OP_minus: |
| return OpBinary([](StackEntry a, StackEntry b) { return a - b; }); |
| case llvm::dwarf::DW_OP_mod: |
| return OpMod(); |
| case llvm::dwarf::DW_OP_mul: |
| return OpBinary([](StackEntry a, StackEntry b) { return a * b; }); |
| case llvm::dwarf::DW_OP_neg: |
| return OpUnary( |
| [](StackEntry a) { return static_cast<StackEntry>(-static_cast<SignedStackEntry>(a)); }); |
| case llvm::dwarf::DW_OP_not: |
| return OpUnary([](StackEntry a) { return ~a; }); |
| case llvm::dwarf::DW_OP_or: |
| return OpBinary([](StackEntry a, StackEntry b) { return a | b; }); |
| case llvm::dwarf::DW_OP_plus: |
| return OpBinary([](StackEntry a, StackEntry b) { return a + b; }); |
| case llvm::dwarf::DW_OP_plus_uconst: |
| return OpPlusUconst(); |
| case llvm::dwarf::DW_OP_shl: |
| return OpBinary([](StackEntry a, StackEntry b) { return a << b; }); |
| case llvm::dwarf::DW_OP_shr: |
| return OpBinary([](StackEntry a, StackEntry b) { return a >> b; }); |
| case llvm::dwarf::DW_OP_shra: |
| return OpBinary([](StackEntry a, StackEntry b) { |
| return static_cast<StackEntry>(static_cast<SignedStackEntry>(a) >> |
| static_cast<SignedStackEntry>(b)); |
| }); |
| case llvm::dwarf::DW_OP_xor: |
| return OpBinary([](StackEntry a, StackEntry b) { return a ^ b; }); |
| case llvm::dwarf::DW_OP_skip: |
| return OpSkip(); |
| case llvm::dwarf::DW_OP_bra: |
| return OpBra(); |
| case llvm::dwarf::DW_OP_eq: |
| return OpBinary([](StackEntry a, StackEntry b) { return static_cast<StackEntry>(a == b); }); |
| case llvm::dwarf::DW_OP_ge: |
| return OpBinary([](StackEntry a, StackEntry b) { return static_cast<StackEntry>(a >= b); }); |
| case llvm::dwarf::DW_OP_gt: |
| return OpBinary([](StackEntry a, StackEntry b) { return static_cast<StackEntry>(a > b); }); |
| case llvm::dwarf::DW_OP_le: |
| return OpBinary([](StackEntry a, StackEntry b) { return static_cast<StackEntry>(a <= b); }); |
| case llvm::dwarf::DW_OP_lt: |
| return OpBinary([](StackEntry a, StackEntry b) { return static_cast<StackEntry>(a < b); }); |
| case llvm::dwarf::DW_OP_ne: |
| return OpBinary([](StackEntry a, StackEntry b) { return static_cast<StackEntry>(a != b); }); |
| case llvm::dwarf::DW_OP_regx: |
| return OpRegx(); |
| case llvm::dwarf::DW_OP_fbreg: |
| return OpFbreg(); |
| case llvm::dwarf::DW_OP_bregx: |
| return OpBregx(); |
| case llvm::dwarf::DW_OP_piece: |
| return OpPiece(); |
| case llvm::dwarf::DW_OP_deref: |
| return OpDeref(sizeof(TargetPointer)); |
| case llvm::dwarf::DW_OP_deref_size: |
| return OpDerefSize(); |
| case llvm::dwarf::DW_OP_xderef_size: |
| // We don't have multiple address spaces. |
| ReportUnimplementedOpcode(op); |
| return Completion::kSync; |
| case llvm::dwarf::DW_OP_nop: |
| return Completion::kSync; |
| case llvm::dwarf::DW_OP_push_object_address: |
| case llvm::dwarf::DW_OP_call2: // 2-byte offset of DIE. |
| case llvm::dwarf::DW_OP_call4: // 4-byte offset of DIE. |
| case llvm::dwarf::DW_OP_call_ref: // 4- or 8-byte offset of DIE. |
| // TODO(brettw) implement these. |
| ReportUnimplementedOpcode(op); |
| return Completion::kSync; |
| case llvm::dwarf::DW_OP_call_frame_cfa: |
| return OpCFA(); |
| case llvm::dwarf::DW_OP_bit_piece: // ULEB128 size + ULEB128 offset. |
| // Clang will generate bit_piece operations to make 80-bit long double constants, but |
| // the expressions are invalid: https://bugs.llvm.org/show_bug.cgi?id=43682 |
| // We were able to get GCC to generate a piece operation for: |
| // void foo(int x, int y) { |
| // struct { int x:3, :3, y:3; } s = {x, y}; |
| // } |
| // That also seems invalid. So we're waiting for a clearly valid example in the wild before |
| // spending time trying to implement this. |
| ReportError( |
| "The DWARF encoding for this symbol uses DW_OP_bit_piece which is unimplemented.\n" |
| "Please file a bit with a repro case so we can implement it properly."); |
| return Completion::kSync; |
| case llvm::dwarf::DW_OP_implicit_value: // ULEB128 size + block of size. |
| return OpImplicitValue(); |
| case llvm::dwarf::DW_OP_stack_value: |
| return OpStackValue(); |
| case llvm::dwarf::DW_OP_GNU_push_tls_address: |
| case llvm::dwarf::DW_OP_form_tls_address: |
| return OpTlsAddr(); |
| |
| case llvm::dwarf::DW_OP_implicit_pointer: |
| case 0xf2: // DW_OP_GNU_implicit_pointer (pre-DWARF5 GNU extension for this). |
| // GCC generates this when a pointer has been optimized out, but it still can provide the |
| // value of the thing that it pointed to. We don't implement this. |
| ReportError("Optimized out (DW_OP_implicit_pointer)"); |
| return Completion::kSync; |
| |
| case 0xf3: // DW_OP_GNU_entry_value |
| // This GNU extension is a ULEB128 length followed by a sub-expression |
| // of that length. This sub-expression is supposed to be evaluated in |
| // a separate stack using the register values that were present at the |
| // beginning of the function: |
| // https://gcc.gnu.org/ml/gcc-patches/2010-08/txt00152.txt |
| // |
| // Generally if the registers were saved registers it would just encode |
| // those locations. This is really used for non-saved registers and |
| // requires that the debugger have previously saved those registers |
| // separately. This isn't something that we currently do, and can't be |
| // done in general (it could be implemented if you previously single- |
| // stepped into that function though). |
| ReportError("Optimized out (DW_OP_GNU_entry_value)"); |
| return Completion::kSync; |
| |
| default: |
| // Invalid or unknown opcode. |
| ReportError(fxl::StringPrintf("Invalid opcode 0x%x in DWARF expression.", op)); |
| return Completion::kSync; |
| } |
| } |
| |
| DwarfExprEval::Completion DwarfExprEval::PushRegisterWithOffset(int dwarf_register_number, |
| SignedStackEntry offset) { |
| // Reading register data means the result is not constant. |
| result_is_constant_ = false; |
| |
| const debug_ipc::RegisterInfo* reg_info = |
| debug_ipc::DWARFToRegisterInfo(data_provider_->GetArch(), dwarf_register_number); |
| if (!reg_info) { |
| ReportError(fxl::StringPrintf("Register %d not known.", dwarf_register_number)); |
| return Completion::kSync; |
| } |
| |
| // This function doesn't set the result_type_ because it is called from different contexts. The |
| // callers should set the result_type_ as appropriate for their operation. |
| if (auto reg_data = data_provider_->GetRegister(reg_info->id)) { |
| // State known synchronously (could be available or known unavailable). |
| if (reg_data->empty()) { |
| ReportError(fxl::StringPrintf("Register %d not available.", dwarf_register_number)); |
| } else { |
| // This truncates to 128 bits and converts from little-endian. DWARF doesn't seem to use the |
| // stack machine for vector computations (it's not specified that the stack items are large |
| // enough). When it uses a stack register for a floating-point scalar computation, it just |
| // uses the low bits. |
| StackEntry reg_value = 0; |
| memcpy(®_value, reg_data->data(), std::min(sizeof(StackEntry), reg_data->size())); |
| Push(reg_value + offset); |
| |
| // When the current value represents a register, save that fact. |
| if (offset == 0) |
| current_register_id_ = reg_info->id; |
| } |
| return Completion::kSync; |
| } |
| |
| // Must request async. |
| data_provider_->GetRegisterAsync( |
| reg_info->id, [reg_id = reg_info->id, weak_eval = weak_factory_.GetWeakPtr(), offset]( |
| const Err& err, std::vector<uint8_t> reg_data) { |
| if (!weak_eval) |
| return; |
| if (err.has_error()) { |
| weak_eval->ReportError(err); |
| return; |
| } |
| |
| // Truncate/convert from little-endian as above. |
| StackEntry reg_value = 0; |
| memcpy(®_value, reg_data.data(), std::min(sizeof(StackEntry), reg_data.size())); |
| weak_eval->Push(static_cast<StackEntry>(reg_value + offset)); |
| |
| // When the current value represents a register, save that fact. |
| if (offset == 0) |
| weak_eval->current_register_id_ = reg_id; |
| |
| // Picks up processing at the next instruction. |
| weak_eval->ContinueEval(); |
| }); |
| |
| return Completion::kAsync; |
| } |
| |
| bool DwarfExprEval::ReadSigned(int byte_size, SignedStackEntry* output) { |
| uint64_t old_expr_index = expr_index_; |
| *output = data_extractor_->getSigned(&expr_index_, byte_size); |
| if (old_expr_index == expr_index_) { |
| ReportError("Bad number format in DWARF expression."); |
| return false; |
| } |
| return true; |
| } |
| |
| bool DwarfExprEval::ReadUnsigned(int byte_size, StackEntry* output) { |
| uint64_t old_expr_index = expr_index_; |
| *output = data_extractor_->getUnsigned(&expr_index_, byte_size); |
| if (old_expr_index == expr_index_) { |
| ReportError("Bad number format in DWARF expression."); |
| return false; |
| } |
| return true; |
| } |
| |
| bool DwarfExprEval::ReadLEBSigned(SignedStackEntry* output) { |
| uint64_t old_expr_index = expr_index_; |
| *output = data_extractor_->getSLEB128(&expr_index_); |
| if (old_expr_index == expr_index_) { |
| ReportError("Bad number format in DWARF expression."); |
| return false; |
| } |
| return true; |
| } |
| |
| bool DwarfExprEval::ReadLEBUnsigned(StackEntry* output) { |
| uint64_t old_expr_index = expr_index_; |
| *output = data_extractor_->getULEB128(&expr_index_); |
| if (old_expr_index == expr_index_) { |
| ReportError("Bad number format in DWARF expression."); |
| return false; |
| } |
| return true; |
| } |
| |
| void DwarfExprEval::ReadMemory( |
| TargetPointer address, uint32_t byte_size, |
| fit::callback<void(DwarfExprEval* eval, std::vector<uint8_t> value)> on_success) { |
| // Reading memory means the result is not constant. |
| result_is_constant_ = false; |
| |
| data_provider_->GetMemoryAsync( |
| address, byte_size, |
| [address, byte_size, weak_eval = weak_factory_.GetWeakPtr(), |
| on_success = std::move(on_success)](const Err& err, std::vector<uint8_t> value) mutable { |
| if (!weak_eval) { |
| return; |
| } else if (err.has_error()) { |
| weak_eval->ReportError(err); |
| } else if (value.size() != byte_size) { |
| weak_eval->ReportError( |
| fxl::StringPrintf("Invalid pointer 0x%" PRIx64 ".", static_cast<uint64_t>(address))); |
| } else { |
| on_success(weak_eval.get(), std::move(value)); |
| |
| // Picks up processing at the next instruction. |
| weak_eval->ContinueEval(); |
| } |
| }); |
| } |
| |
| void DwarfExprEval::ReportError(const std::string& msg) { ReportError(Err(msg)); } |
| |
| void DwarfExprEval::ReportError(const Err& err) { |
| data_provider_.reset(); |
| is_complete_ = true; |
| |
| // Wrap completion callback with the flag to catch deletions from within the |
| // callback. |
| in_completion_callback_ = true; |
| completion_callback_(this, err); |
| completion_callback_ = {}; |
| in_completion_callback_ = false; |
| } |
| |
| void DwarfExprEval::ReportStackUnderflow() { ReportError("Stack underflow for DWARF expression."); } |
| |
| void DwarfExprEval::ReportUnimplementedOpcode(uint8_t op) { |
| ReportError(fxl::StringPrintf("Unimplemented opcode 0x%x in DWARF expression.", op)); |
| } |
| |
| DwarfExprEval::Completion DwarfExprEval::OpUnary(StackEntry (*op)(StackEntry)) { |
| if (stack_.empty()) |
| ReportStackUnderflow(); |
| else |
| stack_.back() = op(stack_.back()); |
| return Completion::kSync; |
| } |
| |
| DwarfExprEval::Completion DwarfExprEval::OpBinary(StackEntry (*op)(StackEntry, StackEntry)) { |
| if (stack_.size() < 2) { |
| ReportStackUnderflow(); |
| } else { |
| StackEntry b = stack_.back(); |
| stack_.pop_back(); |
| StackEntry a = stack_.back(); |
| stack_.back() = op(a, b); |
| } |
| return Completion::kSync; |
| } |
| |
| // 1 parameter: unsigned the size of a pointer. This is relative to the load |
| // address of the current module. It is used to for globals and statics. |
| DwarfExprEval::Completion DwarfExprEval::OpAddr() { |
| StackEntry offset; |
| if (!ReadUnsigned(kTargetPointerSize, &offset)) |
| return Completion::kSync; |
| |
| Push(symbol_context_.RelativeToAbsolute(static_cast<TargetPointer>(offset))); |
| return Completion::kSync; |
| } |
| |
| // 1 parameter: 2 byte signed integer constant. |
| DwarfExprEval::Completion DwarfExprEval::OpBra() { |
| // "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." |
| SignedStackEntry skip_amount = 0; |
| if (!ReadSigned(2, &skip_amount)) |
| return Completion::kSync; |
| |
| if (stack_.empty()) { |
| ReportStackUnderflow(); |
| return Completion::kSync; |
| } |
| |
| // 0 @ top of stack means don't take the branch. |
| StackEntry condition = stack_.back(); |
| stack_.pop_back(); |
| if (condition == 0) |
| return Completion::kSync; |
| |
| // Otherwise take the branch. |
| Skip(skip_amount); |
| return Completion::kSync; |
| } |
| |
| // 1 parameter: SLEB128 offset added to base register. |
| DwarfExprEval::Completion DwarfExprEval::OpBreg(uint8_t op) { |
| int reg_index = op - llvm::dwarf::DW_OP_breg0; |
| SignedStackEntry offset = 0; |
| if (!ReadLEBSigned(&offset)) |
| return Completion::kSync; |
| |
| result_type_ = ResultType::kPointer; |
| return PushRegisterWithOffset(reg_index, offset); |
| } |
| |
| DwarfExprEval::Completion DwarfExprEval::OpCFA() { |
| // Reading the CFA means the result is not constant. |
| result_is_constant_ = false; |
| |
| if (StackEntry cfa = data_provider_->GetCanonicalFrameAddress()) |
| Push(cfa); |
| else |
| ReportError("Frame address is 0."); |
| return Completion::kSync; |
| } |
| |
| DwarfExprEval::Completion DwarfExprEval::OpDiv() { |
| if (stack_.size() < 2) { |
| ReportStackUnderflow(); |
| } else { |
| StackEntry b = stack_.back(); |
| stack_.pop_back(); |
| StackEntry a = stack_.back(); |
| |
| if (b == 0) { |
| ReportError("DWARF expression divided by zero."); |
| } else { |
| stack_.back() = static_cast<StackEntry>(static_cast<SignedStackEntry>(a) / |
| static_cast<SignedStackEntry>(b)); |
| } |
| } |
| return Completion::kSync; |
| } |
| |
| DwarfExprEval::Completion DwarfExprEval::OpDrop() { |
| if (stack_.empty()) |
| ReportStackUnderflow(); |
| else |
| stack_.pop_back(); |
| return Completion::kSync; |
| } |
| |
| DwarfExprEval::Completion DwarfExprEval::OpDup() { |
| if (stack_.empty()) |
| ReportStackUnderflow(); |
| else |
| stack_.push_back(stack_.back()); |
| return Completion::kSync; |
| } |
| |
| // 1 parameter: Signed LEB128 offset from frame base pointer. |
| DwarfExprEval::Completion DwarfExprEval::OpFbreg() { |
| // Reading the frame base means the result is not constant. |
| result_is_constant_ = false; |
| |
| SignedStackEntry offset = 0; |
| if (!ReadLEBSigned(&offset)) |
| return Completion::kSync; |
| |
| if (auto bp = data_provider_->GetFrameBase()) { |
| // Available synchronously. |
| |
| // Certain problems can cause the BP to be set to 0 which is obviously |
| // invalid, report that error specifically. |
| if (*bp == 0) |
| ReportError("Base Pointer is 0, can't evaluate."); |
| |
| result_type_ = ResultType::kPointer; |
| Push(*bp + offset); |
| return Completion::kSync; |
| } |
| |
| // Must request async. |
| data_provider_->GetFrameBaseAsync( |
| [weak_eval = weak_factory_.GetWeakPtr(), offset](const Err& err, StackEntry value) { |
| if (!weak_eval) |
| return; |
| if (err.has_error()) { |
| weak_eval->ReportError(err); |
| return; |
| } |
| |
| if (value == 0) { |
| weak_eval->ReportError("Base Pointer is 0, can't evaluate."); |
| return; |
| } |
| |
| weak_eval->result_type_ = ResultType::kPointer; |
| weak_eval->Push(static_cast<StackEntry>(value + offset)); |
| |
| // Picks up processing at the next instruction. |
| weak_eval->ContinueEval(); |
| }); |
| |
| return Completion::kAsync; |
| } |
| |
| // 2 parameters: ULEB128 length, followed by that much data (in machine-endianness). |
| DwarfExprEval::Completion DwarfExprEval::OpImplicitValue() { |
| StackEntry len = 0; |
| if (!ReadLEBUnsigned(&len)) |
| return Completion::kSync; |
| if (len > sizeof(StackEntry) || expr_index_ + static_cast<size_t>(len) > expr_.size()) { |
| ReportError(fxl::StringPrintf("DWARF implicit value length too long: 0x%x.", |
| static_cast<unsigned>(len))); |
| return Completion::kSync; |
| } |
| |
| StackEntry value = 0; |
| if (len > 0) { |
| memcpy(&value, &expr_[expr_index_], static_cast<size_t>(len)); |
| Skip(len); |
| } |
| |
| Push(value); |
| result_type_ = ResultType::kValue; |
| return Completion::kSync; |
| } |
| |
| // 1 parameter: ULEB128 constant indexing the register. |
| DwarfExprEval::Completion DwarfExprEval::OpRegx() { |
| StackEntry reg = 0; |
| if (!ReadLEBUnsigned(®)) |
| return Completion::kSync; |
| |
| result_type_ = ResultType::kValue; |
| return PushRegisterWithOffset(static_cast<int>(reg), 0); |
| } |
| |
| // 2 parameters: ULEB128 register number + SLEB128 offset. |
| DwarfExprEval::Completion DwarfExprEval::OpBregx() { |
| StackEntry reg = 0; |
| if (!ReadLEBUnsigned(®)) |
| return Completion::kSync; |
| |
| SignedStackEntry offset = 0; |
| if (!ReadLEBSigned(&offset)) |
| return Completion::kSync; |
| |
| result_type_ = ResultType::kPointer; |
| return PushRegisterWithOffset(static_cast<int>(reg), offset); |
| } |
| |
| // Pops the stack and pushes an given-sized value from memory at that location. |
| DwarfExprEval::Completion DwarfExprEval::OpDeref(uint32_t byte_size) { |
| if (stack_.empty()) { |
| ReportStackUnderflow(); |
| return Completion::kSync; |
| } |
| |
| if (byte_size == 0 || byte_size > sizeof(StackEntry)) { |
| ReportError(fxl::StringPrintf("Invalid DWARF expression read size: %u", byte_size)); |
| return Completion::kSync; |
| } |
| |
| StackEntry addr = stack_.back(); |
| stack_.pop_back(); |
| ReadMemory(addr, byte_size, [](DwarfExprEval* eval, std::vector<uint8_t> data) { |
| // Success. This assumes little-endian and copies starting from the low bytes. The data will |
| // have already been validated to be the correct size so we know it will fit in a StackEntry. |
| FX_DCHECK(data.size() <= sizeof(StackEntry)); |
| StackEntry to_push = 0; |
| memcpy(&to_push, &data[0], data.size()); |
| eval->Push(to_push); |
| }); |
| return Completion::kAsync; |
| } |
| |
| DwarfExprEval::Completion DwarfExprEval::OpDerefSize() { |
| // The operand is a 1-byte unsigned constant following the opcode. |
| StackEntry byte_size = 0; |
| if (!ReadUnsigned(1, &byte_size)) |
| return Completion::kSync; |
| |
| // The generic deref path can handle the rest. |
| return OpDeref(static_cast<uint32_t>(byte_size)); |
| } |
| |
| DwarfExprEval::Completion DwarfExprEval::OpMod() { |
| if (stack_.size() < 2) { |
| ReportStackUnderflow(); |
| } else { |
| StackEntry b = stack_.back(); |
| stack_.pop_back(); |
| StackEntry a = stack_.back(); |
| |
| if (b == 0) { |
| ReportError("DWARF expression divided by zero."); |
| } else { |
| stack_.back() = static_cast<StackEntry>(static_cast<SignedStackEntry>(a) % |
| static_cast<SignedStackEntry>(b)); |
| } |
| } |
| return Completion::kSync; |
| } |
| |
| DwarfExprEval::Completion DwarfExprEval::OpOver() { |
| // Duplicates the next-to-top over the top item. |
| if (stack_.size() < 2) |
| ReportStackUnderflow(); |
| else |
| Push(stack_[stack_.size() - 2]); |
| return Completion::kSync; |
| } |
| |
| // 1 parameter: 1-byte stack index from the top to push. |
| DwarfExprEval::Completion DwarfExprEval::OpPick() { |
| StackEntry index = 0; |
| if (!ReadUnsigned(1, &index)) |
| return Completion::kSync; |
| |
| if (stack_.size() <= index) { |
| ReportStackUnderflow(); |
| return Completion::kSync; |
| } |
| |
| // Index is from end (0 = last item). |
| Push(stack_[stack_.size() - 1 - index]); |
| return Completion::kSync; |
| } |
| |
| // 1 paramter: ULEB size of item in bytes. |
| DwarfExprEval::Completion DwarfExprEval::OpPiece() { |
| StackEntry byte_size = 0; |
| if (!ReadLEBUnsigned(&byte_size)) |
| return Completion::kSync; |
| |
| if (stack_.empty()) { |
| ReportStackUnderflow(); |
| return Completion::kSync; |
| } |
| |
| StackEntry source = stack_.back(); |
| stack_.pop_back(); |
| |
| if (result_type_ == ResultType::kValue) { |
| // Simple case where the source of the "piece" is the value at the top of the stack. |
| if (byte_size > sizeof(StackEntry)) { |
| ReportError(fxl::StringPrintf("DWARF expression listed a data size of %d which is too large.", |
| static_cast<int>(byte_size))); |
| return Completion::kSync; |
| } |
| |
| // We want the low bytes, this assumes little-endian. |
| uint8_t source_as_bytes[sizeof(StackEntry)]; |
| memcpy(&source_as_bytes, &source, sizeof(StackEntry)); |
| result_data_.insert(result_data_.end(), std::begin(source_as_bytes), |
| &source_as_bytes[byte_size]); |
| |
| // Reset the expression state to start a new one. |
| result_type_ = ResultType::kPointer; |
| return Completion::kSync; |
| } |
| |
| // This is the more complex case where the top of the stack is a pointer to the value in memory. |
| // We read that many bytes from memory and add it to the result data. |
| ReadMemory(source, byte_size, [](DwarfExprEval* eval, std::vector<uint8_t> data) { |
| // Success. Copy to the result. |
| eval->result_data_.insert(eval->result_data_.end(), data.begin(), data.end()); |
| |
| // Reset the expression state to start a new one. |
| eval->result_type_ = ResultType::kPointer; |
| }); |
| |
| // The ReadMemory call will complete asynchronously. |
| return Completion::kAsync; |
| } |
| |
| DwarfExprEval::Completion DwarfExprEval::OpPlusUconst() { |
| // "Pops the top stack entry, adds it to the unsigned LEB128 constant operand |
| // and pushes the result." |
| if (stack_.empty()) { |
| ReportStackUnderflow(); |
| } else { |
| StackEntry top = stack_.back(); |
| stack_.pop_back(); |
| |
| StackEntry param = 0; |
| if (ReadLEBUnsigned(¶m)) |
| Push(top + param); |
| } |
| return Completion::kSync; |
| } |
| |
| DwarfExprEval::Completion DwarfExprEval::OpPushSigned(int byte_count) { |
| SignedStackEntry value = 0; |
| if (ReadSigned(byte_count, &value)) |
| Push(static_cast<StackEntry>(value)); |
| return Completion::kSync; |
| } |
| |
| DwarfExprEval::Completion DwarfExprEval::OpPushUnsigned(int byte_count) { |
| StackEntry value = 0; |
| if (ReadUnsigned(byte_count, &value)) |
| Push(value); |
| return Completion::kSync; |
| } |
| |
| DwarfExprEval::Completion DwarfExprEval::OpPushLEBSigned() { |
| SignedStackEntry value = 0; |
| if (ReadLEBSigned(&value)) |
| Push(static_cast<StackEntry>(value)); |
| return Completion::kSync; |
| } |
| |
| DwarfExprEval::Completion DwarfExprEval::OpPushLEBUnsigned() { |
| StackEntry value = 0; |
| if (ReadLEBUnsigned(&value)) |
| Push(value); |
| return Completion::kSync; |
| } |
| |
| DwarfExprEval::Completion DwarfExprEval::OpRot() { |
| // Rotates the top 3 entries "down" with wraparound. "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." |
| if (stack_.size() < 3) { |
| ReportStackUnderflow(); |
| } else { |
| StackEntry top = stack_[stack_.size() - 1]; |
| StackEntry one_back = stack_[stack_.size() - 2]; |
| StackEntry two_back = stack_[stack_.size() - 3]; |
| |
| stack_[stack_.size() - 1] = one_back; |
| stack_[stack_.size() - 2] = two_back; |
| stack_[stack_.size() - 3] = top; |
| } |
| return Completion::kSync; |
| } |
| |
| // 1 parameter: 2-byte signed constant. |
| DwarfExprEval::Completion DwarfExprEval::OpSkip() { |
| SignedStackEntry skip_amount = 0; |
| if (!ReadSigned(2, &skip_amount)) |
| return Completion::kSync; |
| Skip(skip_amount); |
| return Completion::kSync; |
| } |
| |
| DwarfExprEval::Completion DwarfExprEval::OpStackValue() { |
| // "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." |
| result_type_ = ResultType::kValue; |
| return Completion::kSync; |
| } |
| |
| DwarfExprEval::Completion DwarfExprEval::OpSwap() { |
| if (stack_.size() < 2) |
| ReportStackUnderflow(); |
| else |
| std::swap(stack_[stack_.size() - 1], stack_[stack_.size() - 2]); |
| return Completion::kSync; |
| } |
| |
| DwarfExprEval::Completion DwarfExprEval::OpTlsAddr() { |
| if (stack_.size() < 1) { |
| ReportStackUnderflow(); |
| return Completion::kSync; |
| } |
| |
| auto debug_address = data_provider_->GetDebugAddressForContext(symbol_context_); |
| |
| if (!debug_address) { |
| ReportError("Debug address unavailable."); |
| return Completion::kSync; |
| } |
| |
| data_provider_->GetTLSSegment( |
| symbol_context_, [weak_eval = weak_factory_.GetWeakPtr()](ErrOr<uint64_t> value) { |
| if (!weak_eval) { |
| return; |
| } |
| |
| if (value.has_error()) { |
| weak_eval->ReportError(value.err()); |
| return; |
| } |
| |
| weak_eval->stack_.back() += static_cast<StackEntry>(value.value()); |
| weak_eval->ContinueEval(); |
| }); |
| |
| return Completion::kAsync; |
| } |
| |
| void DwarfExprEval::Skip(SignedStackEntry amount) { |
| SignedStackEntry new_index = static_cast<SignedStackEntry>(expr_index_) + amount; |
| if (new_index >= static_cast<SignedStackEntry>(expr_.size())) { |
| // Skip to or past the end just terminates the program. |
| expr_index_ = expr_.size(); |
| } else if (new_index < 0) { |
| // Skip before beginning is an error. |
| ReportError("DWARF expression skips out-of-bounds."); |
| } else { |
| expr_index_ = static_cast<uint32_t>(new_index); |
| } |
| } |
| |
| } // namespace zxdb |