| // 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 <stdlib.h> |
| |
| #include <utility> |
| |
| #include "src/lib/fxl/logging.h" |
| #include "src/lib/fxl/strings/string_printf.h" |
| #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" |
| |
| namespace zxdb { |
| |
| DwarfExprEval::DwarfExprEval() |
| : symbol_context_(SymbolContext::ForRelativeAddresses()), |
| weak_factory_(this) {} |
| DwarfExprEval::~DwarfExprEval() = default; |
| |
| DwarfExprEval::ResultType DwarfExprEval::GetResultType() const { |
| FXL_DCHECK(is_complete_); |
| FXL_DCHECK(is_success_); |
| return result_type_; |
| } |
| |
| uint64_t DwarfExprEval::GetResult() const { |
| FXL_DCHECK(is_complete_); |
| FXL_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); |
| stack_.clear(); |
| |
| 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()) { |
| // Failure to compute any values. |
| err = Err("DWARF expression produced no results."); |
| is_success_ = false; |
| } else { |
| is_success_ = true; |
| } |
| |
| // The callback may delete |this| but we also want to clear the value to |
| // prevent accidental future use. So we need to copy members to local |
| // variables in case |this| goes away. |
| auto is_complete = is_complete_; |
| auto cb = std::move(completion_callback_); |
| cb(this, err); |
| 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() { |
| FXL_DCHECK(!is_complete_); |
| FXL_DCHECK(expr_index_ < expr_.size()); |
| |
| // 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: |
| // TODO(brettw) implement this. |
| ReportUnimplementedOpcode(op); |
| return Completion::kSync; |
| case llvm::dwarf::DW_OP_abs: |
| return OpUnary([](uint64_t a) { |
| return static_cast<uint64_t>(llabs(static_cast<long long>(a))); |
| }); |
| case llvm::dwarf::DW_OP_and: |
| return OpBinary([](uint64_t a, uint64_t b) { return a & b; }); |
| case llvm::dwarf::DW_OP_div: |
| return OpDiv(); |
| case llvm::dwarf::DW_OP_minus: |
| return OpBinary([](uint64_t a, uint64_t b) { return a - b; }); |
| case llvm::dwarf::DW_OP_mod: |
| return OpMod(); |
| case llvm::dwarf::DW_OP_mul: |
| return OpBinary([](uint64_t a, uint64_t b) { return a * b; }); |
| case llvm::dwarf::DW_OP_neg: |
| return OpUnary([](uint64_t a) { |
| return static_cast<uint64_t>(-static_cast<int64_t>(a)); |
| }); |
| case llvm::dwarf::DW_OP_not: |
| return OpUnary([](uint64_t a) { return ~a; }); |
| case llvm::dwarf::DW_OP_or: |
| return OpBinary([](uint64_t a, uint64_t b) { return a | b; }); |
| case llvm::dwarf::DW_OP_plus: |
| return OpBinary([](uint64_t a, uint64_t b) { return a + b; }); |
| case llvm::dwarf::DW_OP_plus_uconst: |
| return OpPlusUconst(); |
| case llvm::dwarf::DW_OP_shl: |
| return OpBinary([](uint64_t a, uint64_t b) { return a << b; }); |
| case llvm::dwarf::DW_OP_shr: |
| return OpBinary([](uint64_t a, uint64_t b) { return a >> b; }); |
| case llvm::dwarf::DW_OP_shra: |
| return OpBinary([](uint64_t a, uint64_t b) { |
| return static_cast<uint64_t>(static_cast<int64_t>(a) >> |
| static_cast<int64_t>(b)); |
| }); |
| case llvm::dwarf::DW_OP_xor: |
| return OpBinary([](uint64_t a, uint64_t 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( |
| [](uint64_t a, uint64_t b) { return static_cast<uint64_t>(a == b); }); |
| case llvm::dwarf::DW_OP_ge: |
| return OpBinary( |
| [](uint64_t a, uint64_t b) { return static_cast<uint64_t>(a >= b); }); |
| case llvm::dwarf::DW_OP_gt: |
| return OpBinary( |
| [](uint64_t a, uint64_t b) { return static_cast<uint64_t>(a > b); }); |
| case llvm::dwarf::DW_OP_le: |
| return OpBinary( |
| [](uint64_t a, uint64_t b) { return static_cast<uint64_t>(a <= b); }); |
| case llvm::dwarf::DW_OP_lt: |
| return OpBinary( |
| [](uint64_t a, uint64_t b) { return static_cast<uint64_t>(a < b); }); |
| case llvm::dwarf::DW_OP_ne: |
| return OpBinary( |
| [](uint64_t a, uint64_t b) { return static_cast<uint64_t>(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: |
| // TODO(brettw) implement this. |
| ReportUnimplementedOpcode(op); |
| return Completion::kSync; |
| case llvm::dwarf::DW_OP_deref: |
| return OpDeref(); |
| case llvm::dwarf::DW_OP_deref_size: |
| // TODO(brettw) implement this. |
| ReportUnimplementedOpcode(op); |
| return Completion::kSync; |
| case llvm::dwarf::DW_OP_xderef_size: |
| // TODO(brettw) implement this. |
| 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. |
| case llvm::dwarf::DW_OP_form_tls_address: |
| case llvm::dwarf::DW_OP_call_frame_cfa: // TODO(DX-1510). |
| case llvm::dwarf::DW_OP_bit_piece: // ULEB128 size + ULEB128 offset. |
| case llvm::dwarf::DW_OP_implicit_value: // ULEB128 size + block of size. |
| // TODO(brettw) implement these. |
| ReportUnimplementedOpcode(op); |
| return Completion::kSync; |
| case llvm::dwarf::DW_OP_stack_value: |
| return OpStackValue(); |
| case llvm::dwarf::DW_OP_GNU_push_tls_address: |
| // TODO(DX-694) support TLS. |
| ReportError("TLS not currently supported. See DX-694."); |
| 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, int64_t offset) { |
| auto reg = debug_ipc::DWARFToRegisterID(data_provider_->GetArch(), |
| dwarf_register_number); |
| // 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)) { |
| // Register data available synchronously. |
| Push(*reg_data + offset); |
| return Completion::kSync; |
| } |
| |
| // Must request async. |
| data_provider_->GetRegisterAsync( |
| reg, [weak_eval = weak_factory_.GetWeakPtr(), offset](const Err& err, |
| uint64_t value) { |
| if (!weak_eval) |
| return; |
| if (err.has_error()) { |
| weak_eval->ReportError(err); |
| return; |
| } |
| weak_eval->Push(static_cast<uint64_t>(value + offset)); |
| |
| // Picks up processing at the next instruction. |
| weak_eval->ContinueEval(); |
| }); |
| |
| return Completion::kAsync; |
| } |
| |
| void DwarfExprEval::Push(uint64_t value) { stack_.push_back(value); } |
| |
| bool DwarfExprEval::ReadSigned(int byte_size, int64_t* output) { |
| uint32_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, uint64_t* output) { |
| uint32_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(int64_t* output) { |
| uint32_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(uint64_t* output) { |
| uint32_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::ReportError(const std::string& msg) { |
| ReportError(Err(msg)); |
| } |
| |
| void DwarfExprEval::ReportError(const Err& err) { |
| data_provider_.reset(); |
| is_complete_ = true; |
| |
| // The callback may delete |this| but we also want to clear the value to |
| // prevent accidental future use. |
| auto cb = std::move(completion_callback_); |
| cb(this, err); |
| } |
| |
| 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(uint64_t (*op)(uint64_t)) { |
| if (stack_.empty()) |
| ReportStackUnderflow(); |
| else |
| stack_.back() = op(stack_.back()); |
| return Completion::kSync; |
| } |
| |
| DwarfExprEval::Completion DwarfExprEval::OpBinary(uint64_t (*op)(uint64_t, |
| uint64_t)) { |
| if (stack_.size() < 2) { |
| ReportStackUnderflow(); |
| } else { |
| uint64_t b = stack_.back(); |
| stack_.pop_back(); |
| uint64_t 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() { |
| TargetPointer offset; |
| if (!ReadUnsigned(kTargetPointerSize, &offset)) |
| return Completion::kSync; |
| |
| Push(symbol_context_.RelativeToAbsolute(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." |
| int64_t 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. |
| uint64_t 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; |
| int64_t offset = 0; |
| if (!ReadLEBSigned(&offset)) |
| return Completion::kSync; |
| |
| result_type_ = ResultType::kPointer; |
| return PushRegisterWithOffset(reg_index, offset); |
| } |
| |
| DwarfExprEval::Completion DwarfExprEval::OpDiv() { |
| if (stack_.size() < 2) { |
| ReportStackUnderflow(); |
| } else { |
| uint64_t b = stack_.back(); |
| stack_.pop_back(); |
| uint64_t a = stack_.back(); |
| |
| if (b == 0) { |
| ReportError("DWARF expression divided by zero."); |
| } else { |
| stack_.back() = static_cast<uint64_t>(static_cast<int64_t>(a) / |
| static_cast<int64_t>(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() { |
| int64_t 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, uint64_t 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<uint64_t>(value + offset)); |
| |
| // Picks up processing at the next instruction. |
| weak_eval->ContinueEval(); |
| }); |
| |
| return Completion::kAsync; |
| } |
| |
| // 1 parameter: ULEB128 constant indexing the register. |
| DwarfExprEval::Completion DwarfExprEval::OpRegx() { |
| uint64_t 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() { |
| uint64_t reg = 0; |
| if (!ReadLEBUnsigned(®)) |
| return Completion::kSync; |
| |
| int64_t 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 address-sized value from memory at that |
| // location. |
| DwarfExprEval::Completion DwarfExprEval::OpDeref() { |
| if (stack_.empty()) { |
| ReportStackUnderflow(); |
| return Completion::kSync; |
| } |
| |
| uint64_t addr = stack_.back(); |
| stack_.pop_back(); |
| data_provider_->GetMemoryAsync( |
| addr, 8, |
| [addr, weak_eval = weak_factory_.GetWeakPtr()]( |
| const Err& err, std::vector<uint8_t> value) { |
| if (!weak_eval) { |
| return; |
| } else if (err.has_error()) { |
| weak_eval->ReportError(err); |
| } else if (value.size() != 8) { |
| weak_eval->ReportError( |
| fxl::StringPrintf("Invalid pointer 0x%" PRIx64 ".", addr)); |
| } else { |
| // Success reading 8 bytes. |
| uint64_t to_push; |
| memcpy(&to_push, &value[0], 8); |
| weak_eval->Push(to_push); |
| |
| // Picks up processing at the next instruction. |
| weak_eval->ContinueEval(); |
| } |
| }); |
| return Completion::kAsync; |
| } |
| |
| DwarfExprEval::Completion DwarfExprEval::OpMod() { |
| if (stack_.size() < 2) { |
| ReportStackUnderflow(); |
| } else { |
| uint64_t b = stack_.back(); |
| stack_.pop_back(); |
| uint64_t a = stack_.back(); |
| |
| if (b == 0) { |
| ReportError("DWARF expression divided by zero."); |
| } else { |
| stack_.back() = static_cast<uint64_t>(static_cast<int64_t>(a) % |
| static_cast<int64_t>(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() { |
| uint64_t 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; |
| } |
| |
| 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 { |
| uint64_t top = stack_.back(); |
| stack_.pop_back(); |
| |
| uint64_t param = 0; |
| if (ReadLEBUnsigned(¶m)) |
| Push(top + param); |
| } |
| return Completion::kSync; |
| } |
| |
| DwarfExprEval::Completion DwarfExprEval::OpPushSigned(int byte_count) { |
| int64_t value = 0; |
| if (ReadSigned(byte_count, &value)) |
| Push(static_cast<uint64_t>(value)); |
| return Completion::kSync; |
| } |
| |
| DwarfExprEval::Completion DwarfExprEval::OpPushUnsigned(int byte_count) { |
| uint64_t value = 0; |
| if (ReadUnsigned(byte_count, &value)) |
| Push(value); |
| return Completion::kSync; |
| } |
| |
| DwarfExprEval::Completion DwarfExprEval::OpPushLEBSigned() { |
| int64_t value = 0; |
| if (ReadLEBSigned(&value)) |
| Push(static_cast<uint64_t>(value)); |
| return Completion::kSync; |
| } |
| |
| DwarfExprEval::Completion DwarfExprEval::OpPushLEBUnsigned() { |
| uint64_t 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 { |
| uint64_t top = stack_[stack_.size() - 1]; |
| uint64_t one_back = stack_[stack_.size() - 2]; |
| uint64_t 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() { |
| int64_t 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; |
| |
| // This operation also implicitly terminates the computation. Jump to the |
| // end to indicate this. |
| expr_index_ = expr_.size(); |
| |
| 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; |
| } |
| |
| void DwarfExprEval::Skip(int64_t amount) { |
| int64_t new_index = static_cast<int64_t>(expr_index_) + amount; |
| if (new_index >= static_cast<int64_t>(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 |