diff options
author | Christopher Ferris <cferris@google.com> | 2017-04-04 20:53:43 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2017-04-04 20:53:43 +0000 |
commit | 3f220aaa63b99a0c22ffc18f33792a3704bd9e51 (patch) | |
tree | fe71cecc4364f628a7c3ee3a15f57a1c947a22fb | |
parent | b15882faab26af76f0bca30c2ce4ef4890eb4502 (diff) | |
parent | 55d22ef67c428a3f0994ee7da51b33c79ddcc552 (diff) | |
download | system_core-3f220aaa63b99a0c22ffc18f33792a3704bd9e51.tar.gz system_core-3f220aaa63b99a0c22ffc18f33792a3704bd9e51.tar.bz2 system_core-3f220aaa63b99a0c22ffc18f33792a3704bd9e51.zip |
Merge "Add DwarfOp support."
-rw-r--r-- | libunwindstack/Android.bp | 3 | ||||
-rw-r--r-- | libunwindstack/DwarfError.h | 32 | ||||
-rw-r--r-- | libunwindstack/DwarfOp.cpp | 462 | ||||
-rw-r--r-- | libunwindstack/DwarfOp.h | 1636 | ||||
-rw-r--r-- | libunwindstack/tests/DwarfOpLogTest.cpp | 68 | ||||
-rw-r--r-- | libunwindstack/tests/DwarfOpTest.cpp | 1593 |
6 files changed, 3794 insertions, 0 deletions
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp index dabeac1b9..17ede5163 100644 --- a/libunwindstack/Android.bp +++ b/libunwindstack/Android.bp @@ -48,6 +48,7 @@ cc_defaults { srcs: [ "ArmExidx.cpp", "DwarfMemory.cpp", + "DwarfOp.cpp", "Elf.cpp", "ElfInterface.cpp", "ElfInterfaceArm.cpp", @@ -89,6 +90,8 @@ cc_defaults { "tests/ArmExidxDecodeTest.cpp", "tests/ArmExidxExtractTest.cpp", "tests/DwarfMemoryTest.cpp", + "tests/DwarfOpLogTest.cpp", + "tests/DwarfOpTest.cpp", "tests/ElfInterfaceArmTest.cpp", "tests/ElfInterfaceTest.cpp", "tests/ElfTest.cpp", diff --git a/libunwindstack/DwarfError.h b/libunwindstack/DwarfError.h new file mode 100644 index 000000000..824c30725 --- /dev/null +++ b/libunwindstack/DwarfError.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LIBUNWINDSTACK_DWARF_ERROR_H +#define _LIBUNWINDSTACK_DWARF_ERROR_H + +#include <stdint.h> + +enum DwarfError : uint8_t { + DWARF_ERROR_NONE, + DWARF_ERROR_MEMORY_INVALID, + DWARF_ERROR_ILLEGAL_VALUE, + DWARF_ERROR_ILLEGAL_STATE, + DWARF_ERROR_STACK_INDEX_NOT_VALID, + DWARF_ERROR_NOT_IMPLEMENTED, + DWARF_ERROR_TOO_MANY_ITERATIONS, +}; + +#endif // _LIBUNWINDSTACK_DWARF_ERROR_H diff --git a/libunwindstack/DwarfOp.cpp b/libunwindstack/DwarfOp.cpp new file mode 100644 index 000000000..507ca083d --- /dev/null +++ b/libunwindstack/DwarfOp.cpp @@ -0,0 +1,462 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> + +#include <deque> +#include <string> +#include <vector> + +#include <android-base/stringprintf.h> + +#include "DwarfError.h" +#include "DwarfMemory.h" +#include "DwarfOp.h" +#include "Log.h" +#include "Memory.h" +#include "Regs.h" + +template <typename AddressType> +constexpr typename DwarfOp<AddressType>::OpCallback DwarfOp<AddressType>::kCallbackTable[256]; + +template <typename AddressType> +bool DwarfOp<AddressType>::Eval(uint64_t start, uint64_t end, uint8_t dwarf_version) { + uint32_t iterations = 0; + is_register_ = false; + stack_.clear(); + memory_->set_cur_offset(start); + while (memory_->cur_offset() < end) { + if (!Decode(dwarf_version)) { + return false; + } + // To protect against a branch that creates an infinite loop, + // terminate if the number of iterations gets too high. + if (iterations++ == 1000) { + last_error_ = DWARF_ERROR_TOO_MANY_ITERATIONS; + return false; + } + } + return true; +} + +template <typename AddressType> +bool DwarfOp<AddressType>::Decode(uint8_t dwarf_version) { + last_error_ = DWARF_ERROR_NONE; + if (!memory_->ReadBytes(&cur_op_, 1)) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + + const auto* op = &kCallbackTable[cur_op_]; + const auto handle_func = op->handle_func; + if (handle_func == nullptr) { + last_error_ = DWARF_ERROR_ILLEGAL_VALUE; + return false; + } + + // Check for an unsupported opcode. + if (dwarf_version < op->supported_version) { + last_error_ = DWARF_ERROR_ILLEGAL_VALUE; + return false; + } + + // Make sure that the required number of stack elements is available. + if (stack_.size() < op->num_required_stack_values) { + last_error_ = DWARF_ERROR_STACK_INDEX_NOT_VALID; + return false; + } + + operands_.clear(); + for (size_t i = 0; i < op->num_operands; i++) { + uint64_t value; + if (!memory_->ReadEncodedValue<AddressType>(op->operands[i], &value)) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + operands_.push_back(value); + } + return (this->*handle_func)(); +} + +template <typename AddressType> +void DwarfOp<AddressType>::GetLogInfo(uint64_t start, uint64_t end, + std::vector<std::string>* lines) { + memory_->set_cur_offset(start); + while (memory_->cur_offset() < end) { + uint8_t cur_op; + if (!memory_->ReadBytes(&cur_op, 1)) { + return; + } + + std::string raw_string(android::base::StringPrintf("Raw Data: 0x%02x", cur_op)); + std::string log_string; + const auto* op = &kCallbackTable[cur_op]; + if (op->handle_func == nullptr) { + log_string = "Illegal"; + } else { + log_string = op->name; + uint64_t start_offset = memory_->cur_offset(); + for (size_t i = 0; i < op->num_operands; i++) { + uint64_t value; + if (!memory_->ReadEncodedValue<AddressType>(op->operands[i], &value)) { + return; + } + log_string += ' ' + std::to_string(value); + } + uint64_t end_offset = memory_->cur_offset(); + + memory_->set_cur_offset(start_offset); + for (size_t i = start_offset; i < end_offset; i++) { + uint8_t byte; + if (!memory_->ReadBytes(&byte, 1)) { + return; + } + raw_string += android::base::StringPrintf(" 0x%02x", byte); + } + memory_->set_cur_offset(end_offset); + } + lines->push_back(std::move(log_string)); + lines->push_back(std::move(raw_string)); + } +} + +template <typename AddressType> +bool DwarfOp<AddressType>::op_deref() { + // Read the address and dereference it. + AddressType addr = StackPop(); + AddressType value; + if (!regular_memory()->Read(addr, &value, sizeof(value))) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + stack_.push_front(value); + return true; +} + +template <typename AddressType> +bool DwarfOp<AddressType>::op_deref_size() { + AddressType bytes_to_read = OperandAt(0); + if (bytes_to_read > sizeof(AddressType) || bytes_to_read == 0) { + last_error_ = DWARF_ERROR_ILLEGAL_VALUE; + return false; + } + // Read the address and dereference it. + AddressType addr = StackPop(); + AddressType value = 0; + if (!regular_memory()->Read(addr, &value, bytes_to_read)) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + stack_.push_front(value); + return true; +} + +template <typename AddressType> +bool DwarfOp<AddressType>::op_push() { + // Push all of the operands. + for (auto operand : operands_) { + stack_.push_front(operand); + } + return true; +} + +template <typename AddressType> +bool DwarfOp<AddressType>::op_dup() { + stack_.push_front(StackAt(0)); + return true; +} + +template <typename AddressType> +bool DwarfOp<AddressType>::op_drop() { + StackPop(); + return true; +} + +template <typename AddressType> +bool DwarfOp<AddressType>::op_over() { + stack_.push_front(StackAt(1)); + return true; +} + +template <typename AddressType> +bool DwarfOp<AddressType>::op_pick() { + AddressType index = OperandAt(0); + if (index > StackSize()) { + last_error_ = DWARF_ERROR_STACK_INDEX_NOT_VALID; + return false; + } + stack_.push_front(StackAt(index)); + return true; +} + +template <typename AddressType> +bool DwarfOp<AddressType>::op_swap() { + AddressType old_value = stack_[0]; + stack_[0] = stack_[1]; + stack_[1] = old_value; + return true; +} + +template <typename AddressType> +bool DwarfOp<AddressType>::op_rot() { + AddressType top = stack_[0]; + stack_[0] = stack_[1]; + stack_[1] = stack_[2]; + stack_[2] = top; + return true; +} + +template <typename AddressType> +bool DwarfOp<AddressType>::op_abs() { + SignedType signed_value = static_cast<SignedType>(stack_[0]); + if (signed_value < 0) { + signed_value = -signed_value; + } + stack_[0] = static_cast<AddressType>(signed_value); + return true; +} + +template <typename AddressType> +bool DwarfOp<AddressType>::op_and() { + AddressType top = StackPop(); + stack_[0] &= top; + return true; +} + +template <typename AddressType> +bool DwarfOp<AddressType>::op_div() { + AddressType top = StackPop(); + if (top == 0) { + last_error_ = DWARF_ERROR_ILLEGAL_VALUE; + return false; + } + SignedType signed_divisor = static_cast<SignedType>(top); + SignedType signed_dividend = static_cast<SignedType>(stack_[0]); + stack_[0] = static_cast<AddressType>(signed_dividend / signed_divisor); + return true; +} + +template <typename AddressType> +bool DwarfOp<AddressType>::op_minus() { + AddressType top = StackPop(); + stack_[0] -= top; + return true; +} + +template <typename AddressType> +bool DwarfOp<AddressType>::op_mod() { + AddressType top = StackPop(); + if (top == 0) { + last_error_ = DWARF_ERROR_ILLEGAL_VALUE; + return false; + } + stack_[0] %= top; + return true; +} + +template <typename AddressType> +bool DwarfOp<AddressType>::op_mul() { + AddressType top = StackPop(); + stack_[0] *= top; + return true; +} + +template <typename AddressType> +bool DwarfOp<AddressType>::op_neg() { + SignedType signed_value = static_cast<SignedType>(stack_[0]); + stack_[0] = static_cast<AddressType>(-signed_value); + return true; +} + +template <typename AddressType> +bool DwarfOp<AddressType>::op_not() { + stack_[0] = ~stack_[0]; + return true; +} + +template <typename AddressType> +bool DwarfOp<AddressType>::op_or() { + AddressType top = StackPop(); + stack_[0] |= top; + return true; +} + +template <typename AddressType> +bool DwarfOp<AddressType>::op_plus() { + AddressType top = StackPop(); + stack_[0] += top; + return true; +} + +template <typename AddressType> +bool DwarfOp<AddressType>::op_plus_uconst() { + stack_[0] += OperandAt(0); + return true; +} + +template <typename AddressType> +bool DwarfOp<AddressType>::op_shl() { + AddressType top = StackPop(); + stack_[0] <<= top; + return true; +} + +template <typename AddressType> +bool DwarfOp<AddressType>::op_shr() { + AddressType top = StackPop(); + stack_[0] >>= top; + return true; +} + +template <typename AddressType> +bool DwarfOp<AddressType>::op_shra() { + AddressType top = StackPop(); + SignedType signed_value = static_cast<SignedType>(stack_[0]) >> top; + stack_[0] = static_cast<AddressType>(signed_value); + return true; +} + +template <typename AddressType> +bool DwarfOp<AddressType>::op_xor() { + AddressType top = StackPop(); + stack_[0] ^= top; + return true; +} + +template <typename AddressType> +bool DwarfOp<AddressType>::op_bra() { + // Requires one stack element. + AddressType top = StackPop(); + int16_t offset = static_cast<int16_t>(OperandAt(0)); + uint64_t cur_offset; + if (top != 0) { + cur_offset = memory_->cur_offset() + offset; + } else { + cur_offset = memory_->cur_offset() - offset; + } + memory_->set_cur_offset(cur_offset); + return true; +} + +template <typename AddressType> +bool DwarfOp<AddressType>::op_eq() { + AddressType top = StackPop(); + stack_[0] = bool_to_dwarf_bool(stack_[0] == top); + return true; +} + +template <typename AddressType> +bool DwarfOp<AddressType>::op_ge() { + AddressType top = StackPop(); + stack_[0] = bool_to_dwarf_bool(stack_[0] >= top); + return true; +} + +template <typename AddressType> +bool DwarfOp<AddressType>::op_gt() { + AddressType top = StackPop(); + stack_[0] = bool_to_dwarf_bool(stack_[0] > top); + return true; +} + +template <typename AddressType> +bool DwarfOp<AddressType>::op_le() { + AddressType top = StackPop(); + stack_[0] = bool_to_dwarf_bool(stack_[0] <= top); + return true; +} + +template <typename AddressType> +bool DwarfOp<AddressType>::op_lt() { + AddressType top = StackPop(); + stack_[0] = bool_to_dwarf_bool(stack_[0] < top); + return true; +} + +template <typename AddressType> +bool DwarfOp<AddressType>::op_ne() { + AddressType top = StackPop(); + stack_[0] = bool_to_dwarf_bool(stack_[0] != top); + return true; +} + +template <typename AddressType> +bool DwarfOp<AddressType>::op_skip() { + int16_t offset = static_cast<int16_t>(OperandAt(0)); + uint64_t cur_offset = memory_->cur_offset() + offset; + memory_->set_cur_offset(cur_offset); + return true; +} + +template <typename AddressType> +bool DwarfOp<AddressType>::op_lit() { + stack_.push_front(cur_op() - 0x30); + return true; +} + +template <typename AddressType> +bool DwarfOp<AddressType>::op_reg() { + is_register_ = true; + stack_.push_front(cur_op() - 0x50); + return true; +} + +template <typename AddressType> +bool DwarfOp<AddressType>::op_regx() { + is_register_ = true; + stack_.push_front(OperandAt(0)); + return true; +} + +// It's not clear for breg/bregx, if this op should read the current +// value of the register, or where we think that register is located. +// For simplicity, the code will read the value before doing the unwind. +template <typename AddressType> +bool DwarfOp<AddressType>::op_breg() { + uint16_t reg = cur_op() - 0x70; + if (reg >= regs_->total_regs()) { + last_error_ = DWARF_ERROR_ILLEGAL_VALUE; + return false; + } + stack_.push_front((*regs_)[reg] + OperandAt(0)); + return true; +} + +template <typename AddressType> +bool DwarfOp<AddressType>::op_bregx() { + AddressType reg = OperandAt(0); + if (reg >= regs_->total_regs()) { + last_error_ = DWARF_ERROR_ILLEGAL_VALUE; + return false; + } + stack_.push_front((*regs_)[reg] + OperandAt(1)); + return true; +} + +template <typename AddressType> +bool DwarfOp<AddressType>::op_nop() { + return true; +} + +template <typename AddressType> +bool DwarfOp<AddressType>::op_not_implemented() { + last_error_ = DWARF_ERROR_NOT_IMPLEMENTED; + return false; +} + +// Explicitly instantiate DwarfOp. +template class DwarfOp<uint32_t>; +template class DwarfOp<uint64_t>; diff --git a/libunwindstack/DwarfOp.h b/libunwindstack/DwarfOp.h new file mode 100644 index 000000000..ed6537aeb --- /dev/null +++ b/libunwindstack/DwarfOp.h @@ -0,0 +1,1636 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LIBUNWINDSTACK_DWARF_OP_H +#define _LIBUNWINDSTACK_DWARF_OP_H + +#include <stdint.h> + +#include <deque> +#include <string> +#include <type_traits> +#include <vector> + +#include "DwarfEncoding.h" + +enum DwarfVersion : uint8_t { + DWARF_VERSION_2 = 2, + DWARF_VERSION_3 = 3, + DWARF_VERSION_4 = 4, + DWARF_VERSION_MAX = DWARF_VERSION_4, +}; + +// Forward declarations. +class DwarfMemory; +class Memory; +template <typename AddressType> +class RegsTmpl; + +template <typename AddressType> +class DwarfOp { + // Signed version of AddressType + typedef typename std::make_signed<AddressType>::type SignedType; + + struct OpCallback { + const char* name; + bool (DwarfOp::*handle_func)(); + uint8_t supported_version; + uint8_t num_required_stack_values; + uint8_t num_operands; + uint8_t operands[2]; + }; + + public: + DwarfOp(DwarfMemory* memory, Memory* regular_memory) + : memory_(memory), regular_memory_(regular_memory) {} + virtual ~DwarfOp() = default; + + bool Decode(uint8_t dwarf_version); + + bool Eval(uint64_t start, uint64_t end, uint8_t dwarf_version); + + void GetLogInfo(uint64_t start, uint64_t end, std::vector<std::string>* lines); + + AddressType StackAt(size_t index) { return stack_[index]; } + size_t StackSize() { return stack_.size(); } + + void set_regs(RegsTmpl<AddressType>* regs) { regs_ = regs; } + + DwarfError last_error() { return last_error_; } + + bool is_register() { return is_register_; } + + uint8_t cur_op() { return cur_op_; } + + Memory* regular_memory() { return regular_memory_; } + + protected: + AddressType OperandAt(size_t index) { return operands_[index]; } + size_t OperandsSize() { return operands_.size(); } + + AddressType StackPop() { + AddressType value = stack_.front(); + stack_.pop_front(); + return value; + } + + private: + DwarfMemory* memory_; + Memory* regular_memory_; + + RegsTmpl<AddressType>* regs_; + bool is_register_ = false; + DwarfError last_error_ = DWARF_ERROR_NONE; + uint8_t cur_op_; + std::vector<AddressType> operands_; + std::deque<AddressType> stack_; + + inline AddressType bool_to_dwarf_bool(bool value) { return value ? 1 : 0; } + + // Op processing functions. + bool op_deref(); + bool op_deref_size(); + bool op_push(); + bool op_dup(); + bool op_drop(); + bool op_over(); + bool op_pick(); + bool op_swap(); + bool op_rot(); + bool op_abs(); + bool op_and(); + bool op_div(); + bool op_minus(); + bool op_mod(); + bool op_mul(); + bool op_neg(); + bool op_not(); + bool op_or(); + bool op_plus(); + bool op_plus_uconst(); + bool op_shl(); + bool op_shr(); + bool op_shra(); + bool op_xor(); + bool op_bra(); + bool op_eq(); + bool op_ge(); + bool op_gt(); + bool op_le(); + bool op_lt(); + bool op_ne(); + bool op_skip(); + bool op_lit(); + bool op_reg(); + bool op_regx(); + bool op_breg(); + bool op_bregx(); + bool op_nop(); + bool op_not_implemented(); + + constexpr static OpCallback kCallbackTable[256] = { + {nullptr, nullptr, 0, 0, 0, {}}, // 0x00 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0x01 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0x02 illegal op + { + // 0x03 DW_OP_addr + "DW_OP_addr", + &DwarfOp::op_push, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_absptr}, + }, + {nullptr, nullptr, 0, 0, 0, {}}, // 0x04 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0x05 illegal op + { + // 0x06 DW_OP_deref + "DW_OP_deref", + &DwarfOp::op_deref, + DWARF_VERSION_2, + 1, + 0, + {}, + }, + {nullptr, nullptr, 0, 0, 0, {}}, // 0x07 illegal op + { + // 0x08 DW_OP_const1u + "DW_OP_const1u", + &DwarfOp::op_push, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_udata1}, + }, + { + // 0x09 DW_OP_const1s + "DW_OP_const1s", + &DwarfOp::op_push, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_sdata1}, + }, + { + // 0x0a DW_OP_const2u + "DW_OP_const2u", + &DwarfOp::op_push, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_udata2}, + }, + { + // 0x0b DW_OP_const2s + "DW_OP_const2s", + &DwarfOp::op_push, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_sdata2}, + }, + { + // 0x0c DW_OP_const4u + "DW_OP_const4u", + &DwarfOp::op_push, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_udata4}, + }, + { + // 0x0d DW_OP_const4s + "DW_OP_const4s", + &DwarfOp::op_push, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_sdata4}, + }, + { + // 0x0e DW_OP_const8u + "DW_OP_const8u", + &DwarfOp::op_push, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_udata8}, + }, + { + // 0x0f DW_OP_const8s + "DW_OP_const8s", + &DwarfOp::op_push, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_sdata8}, + }, + { + // 0x10 DW_OP_constu + "DW_OP_constu", + &DwarfOp::op_push, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_uleb128}, + }, + { + // 0x11 DW_OP_consts + "DW_OP_consts", + &DwarfOp::op_push, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_sleb128}, + }, + { + // 0x12 DW_OP_dup + "DW_OP_dup", + &DwarfOp::op_dup, + DWARF_VERSION_2, + 1, + 0, + {}, + }, + { + // 0x13 DW_OP_drop + "DW_OP_drop", + &DwarfOp::op_drop, + DWARF_VERSION_2, + 1, + 0, + {}, + }, + { + // 0x14 DW_OP_over + "DW_OP_over", + &DwarfOp::op_over, + DWARF_VERSION_2, + 2, + 0, + {}, + }, + { + // 0x15 DW_OP_pick + "DW_OP_pick", + &DwarfOp::op_pick, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_udata1}, + }, + { + // 0x16 DW_OP_swap + "DW_OP_swap", + &DwarfOp::op_swap, + DWARF_VERSION_2, + 2, + 0, + {}, + }, + { + // 0x17 DW_OP_rot + "DW_OP_rot", + &DwarfOp::op_rot, + DWARF_VERSION_2, + 3, + 0, + {}, + }, + { + // 0x18 DW_OP_xderef + "DW_OP_xderef", + &DwarfOp::op_not_implemented, + DWARF_VERSION_2, + 2, + 0, + {}, + }, + { + // 0x19 DW_OP_abs + "DW_OP_abs", + &DwarfOp::op_abs, + DWARF_VERSION_2, + 1, + 0, + {}, + }, + { + // 0x1a DW_OP_and + "DW_OP_and", + &DwarfOp::op_and, + DWARF_VERSION_2, + 2, + 0, + {}, + }, + { + // 0x1b DW_OP_div + "DW_OP_div", + &DwarfOp::op_div, + DWARF_VERSION_2, + 2, + 0, + {}, + }, + { + // 0x1c DW_OP_minus + "DW_OP_minus", + &DwarfOp::op_minus, + DWARF_VERSION_2, + 2, + 0, + {}, + }, + { + // 0x1d DW_OP_mod + "DW_OP_mod", + &DwarfOp::op_mod, + DWARF_VERSION_2, + 2, + 0, + {}, + }, + { + // 0x1e DW_OP_mul + "DW_OP_mul", + &DwarfOp::op_mul, + DWARF_VERSION_2, + 2, + 0, + {}, + }, + { + // 0x1f DW_OP_neg + "DW_OP_neg", + &DwarfOp::op_neg, + DWARF_VERSION_2, + 1, + 0, + {}, + }, + { + // 0x20 DW_OP_not + "DW_OP_not", + &DwarfOp::op_not, + DWARF_VERSION_2, + 1, + 0, + {}, + }, + { + // 0x21 DW_OP_or + "DW_OP_or", + &DwarfOp::op_or, + DWARF_VERSION_2, + 2, + 0, + {}, + }, + { + // 0x22 DW_OP_plus + "DW_OP_plus", + &DwarfOp::op_plus, + DWARF_VERSION_2, + 2, + 0, + {}, + }, + { + // 0x23 DW_OP_plus_uconst + "DW_OP_plus_uconst", + &DwarfOp::op_plus_uconst, + DWARF_VERSION_2, + 1, + 1, + {DW_EH_PE_uleb128}, + }, + { + // 0x24 DW_OP_shl + "DW_OP_shl", + &DwarfOp::op_shl, + DWARF_VERSION_2, + 2, + 0, + {}, + }, + { + // 0x25 DW_OP_shr + "DW_OP_shr", + &DwarfOp::op_shr, + DWARF_VERSION_2, + 2, + 0, + {}, + }, + { + // 0x26 DW_OP_shra + "DW_OP_shra", + &DwarfOp::op_shra, + DWARF_VERSION_2, + 2, + 0, + {}, + }, + { + // 0x27 DW_OP_xor + "DW_OP_xor", + &DwarfOp::op_xor, + DWARF_VERSION_2, + 2, + 0, + {}, + }, + { + // 0x28 DW_OP_bra + "DW_OP_bra", + &DwarfOp::op_bra, + DWARF_VERSION_2, + 1, + 1, + {DW_EH_PE_sdata2}, + }, + { + // 0x29 DW_OP_eq + "DW_OP_eq", + &DwarfOp::op_eq, + DWARF_VERSION_2, + 2, + 0, + {}, + }, + { + // 0x2a DW_OP_ge + "DW_OP_ge", + &DwarfOp::op_ge, + DWARF_VERSION_2, + 2, + 0, + {}, + }, + { + // 0x2b DW_OP_gt + "DW_OP_gt", + &DwarfOp::op_gt, + DWARF_VERSION_2, + 2, + 0, + {}, + }, + { + // 0x2c DW_OP_le + "DW_OP_le", + &DwarfOp::op_le, + DWARF_VERSION_2, + 2, + 0, + {}, + }, + { + // 0x2d DW_OP_lt + "DW_OP_lt", + &DwarfOp::op_lt, + DWARF_VERSION_2, + 2, + 0, + {}, + }, + { + // 0x2e DW_OP_ne + "DW_OP_ne", + &DwarfOp::op_ne, + DWARF_VERSION_2, + 2, + 0, + {}, + }, + { + // 0x2f DW_OP_skip + "DW_OP_skip", + &DwarfOp::op_skip, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_sdata2}, + }, + { + // 0x30 DW_OP_lit0 + "DW_OP_lit0", + &DwarfOp::op_lit, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x31 DW_OP_lit1 + "DW_OP_lit1", + &DwarfOp::op_lit, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x32 DW_OP_lit2 + "DW_OP_lit2", + &DwarfOp::op_lit, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x33 DW_OP_lit3 + "DW_OP_lit3", + &DwarfOp::op_lit, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x34 DW_OP_lit4 + "DW_OP_lit4", + &DwarfOp::op_lit, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x35 DW_OP_lit5 + "DW_OP_lit5", + &DwarfOp::op_lit, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x36 DW_OP_lit6 + "DW_OP_lit6", + &DwarfOp::op_lit, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x37 DW_OP_lit7 + "DW_OP_lit7", + &DwarfOp::op_lit, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x38 DW_OP_lit8 + "DW_OP_lit8", + &DwarfOp::op_lit, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x39 DW_OP_lit9 + "DW_OP_lit9", + &DwarfOp::op_lit, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x3a DW_OP_lit10 + "DW_OP_lit10", + &DwarfOp::op_lit, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x3b DW_OP_lit11 + "DW_OP_lit11", + &DwarfOp::op_lit, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x3c DW_OP_lit12 + "DW_OP_lit12", + &DwarfOp::op_lit, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x3d DW_OP_lit13 + "DW_OP_lit13", + &DwarfOp::op_lit, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x3e DW_OP_lit14 + "DW_OP_lit14", + &DwarfOp::op_lit, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x3f DW_OP_lit15 + "DW_OP_lit15", + &DwarfOp::op_lit, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x40 DW_OP_lit16 + "DW_OP_lit16", + &DwarfOp::op_lit, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x41 DW_OP_lit17 + "DW_OP_lit17", + &DwarfOp::op_lit, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x42 DW_OP_lit18 + "DW_OP_lit18", + &DwarfOp::op_lit, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x43 DW_OP_lit19 + "DW_OP_lit19", + &DwarfOp::op_lit, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x44 DW_OP_lit20 + "DW_OP_lit20", + &DwarfOp::op_lit, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x45 DW_OP_lit21 + "DW_OP_lit21", + &DwarfOp::op_lit, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x46 DW_OP_lit22 + "DW_OP_lit22", + &DwarfOp::op_lit, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x47 DW_OP_lit23 + "DW_OP_lit23", + &DwarfOp::op_lit, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x48 DW_OP_lit24 + "DW_OP_lit24", + &DwarfOp::op_lit, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x49 DW_OP_lit25 + "DW_OP_lit25", + &DwarfOp::op_lit, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x4a DW_OP_lit26 + "DW_OP_lit26", + &DwarfOp::op_lit, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x4b DW_OP_lit27 + "DW_OP_lit27", + &DwarfOp::op_lit, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x4c DW_OP_lit28 + "DW_OP_lit28", + &DwarfOp::op_lit, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x4d DW_OP_lit29 + "DW_OP_lit29", + &DwarfOp::op_lit, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x4e DW_OP_lit30 + "DW_OP_lit30", + &DwarfOp::op_lit, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x4f DW_OP_lit31 + "DW_OP_lit31", + &DwarfOp::op_lit, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x50 DW_OP_reg0 + "DW_OP_reg0", + &DwarfOp::op_reg, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x51 DW_OP_reg1 + "DW_OP_reg1", + &DwarfOp::op_reg, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x52 DW_OP_reg2 + "DW_OP_reg2", + &DwarfOp::op_reg, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x53 DW_OP_reg3 + "DW_OP_reg3", + &DwarfOp::op_reg, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x54 DW_OP_reg4 + "DW_OP_reg4", + &DwarfOp::op_reg, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x55 DW_OP_reg5 + "DW_OP_reg5", + &DwarfOp::op_reg, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x56 DW_OP_reg6 + "DW_OP_reg6", + &DwarfOp::op_reg, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x57 DW_OP_reg7 + "DW_OP_reg7", + &DwarfOp::op_reg, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x58 DW_OP_reg8 + "DW_OP_reg8", + &DwarfOp::op_reg, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x59 DW_OP_reg9 + "DW_OP_reg9", + &DwarfOp::op_reg, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x5a DW_OP_reg10 + "DW_OP_reg10", + &DwarfOp::op_reg, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x5b DW_OP_reg11 + "DW_OP_reg11", + &DwarfOp::op_reg, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x5c DW_OP_reg12 + "DW_OP_reg12", + &DwarfOp::op_reg, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x5d DW_OP_reg13 + "DW_OP_reg13", + &DwarfOp::op_reg, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x5e DW_OP_reg14 + "DW_OP_reg14", + &DwarfOp::op_reg, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x5f DW_OP_reg15 + "DW_OP_reg15", + &DwarfOp::op_reg, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x60 DW_OP_reg16 + "DW_OP_reg16", + &DwarfOp::op_reg, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x61 DW_OP_reg17 + "DW_OP_reg17", + &DwarfOp::op_reg, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x62 DW_OP_reg18 + "DW_OP_reg18", + &DwarfOp::op_reg, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x63 DW_OP_reg19 + "DW_OP_reg19", + &DwarfOp::op_reg, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x64 DW_OP_reg20 + "DW_OP_reg20", + &DwarfOp::op_reg, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x65 DW_OP_reg21 + "DW_OP_reg21", + &DwarfOp::op_reg, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x66 DW_OP_reg22 + "DW_OP_reg22", + &DwarfOp::op_reg, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x67 DW_OP_reg23 + "DW_OP_reg23", + &DwarfOp::op_reg, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x68 DW_OP_reg24 + "DW_OP_reg24", + &DwarfOp::op_reg, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x69 DW_OP_reg25 + "DW_OP_reg25", + &DwarfOp::op_reg, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x6a DW_OP_reg26 + "DW_OP_reg26", + &DwarfOp::op_reg, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x6b DW_OP_reg27 + "DW_OP_reg27", + &DwarfOp::op_reg, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x6c DW_OP_reg28 + "DW_OP_reg28", + &DwarfOp::op_reg, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x6d DW_OP_reg29 + "DW_OP_reg29", + &DwarfOp::op_reg, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x6e DW_OP_reg30 + "DW_OP_reg30", + &DwarfOp::op_reg, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x6f DW_OP_reg31 + "DW_OP_reg31", + &DwarfOp::op_reg, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x70 DW_OP_breg0 + "DW_OP_breg0", + &DwarfOp::op_breg, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_sleb128}, + }, + { + // 0x71 DW_OP_breg1 + "DW_OP_breg1", + &DwarfOp::op_breg, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_sleb128}, + }, + { + // 0x72 DW_OP_breg2 + "DW_OP_breg2", + &DwarfOp::op_breg, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_sleb128}, + }, + { + // 0x73 DW_OP_breg3 + "DW_OP_breg3", + &DwarfOp::op_breg, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_sleb128}, + }, + { + // 0x74 DW_OP_breg4 + "DW_OP_breg4", + &DwarfOp::op_breg, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_sleb128}, + }, + { + // 0x75 DW_OP_breg5 + "DW_OP_breg5", + &DwarfOp::op_breg, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_sleb128}, + }, + { + // 0x76 DW_OP_breg6 + "DW_OP_breg6", + &DwarfOp::op_breg, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_sleb128}, + }, + { + // 0x77 DW_OP_breg7 + "DW_OP_breg7", + &DwarfOp::op_breg, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_sleb128}, + }, + { + // 0x78 DW_OP_breg8 + "DW_OP_breg8", + &DwarfOp::op_breg, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_sleb128}, + }, + { + // 0x79 DW_OP_breg9 + "DW_OP_breg9", + &DwarfOp::op_breg, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_sleb128}, + }, + { + // 0x7a DW_OP_breg10 + "DW_OP_breg10", + &DwarfOp::op_breg, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_sleb128}, + }, + { + // 0x7b DW_OP_breg11 + "DW_OP_breg11", + &DwarfOp::op_breg, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_sleb128}, + }, + { + // 0x7c DW_OP_breg12 + "DW_OP_breg12", + &DwarfOp::op_breg, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_sleb128}, + }, + { + // 0x7d DW_OP_breg13 + "DW_OP_breg13", + &DwarfOp::op_breg, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_sleb128}, + }, + { + // 0x7e DW_OP_breg14 + "DW_OP_breg14", + &DwarfOp::op_breg, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_sleb128}, + }, + { + // 0x7f DW_OP_breg15 + "DW_OP_breg15", + &DwarfOp::op_breg, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_sleb128}, + }, + { + // 0x80 DW_OP_breg16 + "DW_OP_breg16", + &DwarfOp::op_breg, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_sleb128}, + }, + { + // 0x81 DW_OP_breg17 + "DW_OP_breg17", + &DwarfOp::op_breg, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_sleb128}, + }, + { + // 0x82 DW_OP_breg18 + "DW_OP_breg18", + &DwarfOp::op_breg, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_sleb128}, + }, + { + // 0x83 DW_OP_breg19 + "DW_OP_breg19", + &DwarfOp::op_breg, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_sleb128}, + }, + { + // 0x84 DW_OP_breg20 + "DW_OP_breg20", + &DwarfOp::op_breg, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_sleb128}, + }, + { + // 0x85 DW_OP_breg21 + "DW_OP_breg21", + &DwarfOp::op_breg, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_sleb128}, + }, + { + // 0x86 DW_OP_breg22 + "DW_OP_breg22", + &DwarfOp::op_breg, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_sleb128}, + }, + { + // 0x87 DW_OP_breg23 + "DW_OP_breg23", + &DwarfOp::op_breg, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_sleb128}, + }, + { + // 0x88 DW_OP_breg24 + "DW_OP_breg24", + &DwarfOp::op_breg, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_sleb128}, + }, + { + // 0x89 DW_OP_breg25 + "DW_OP_breg25", + &DwarfOp::op_breg, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_sleb128}, + }, + { + // 0x8a DW_OP_breg26 + "DW_OP_breg26", + &DwarfOp::op_breg, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_sleb128}, + }, + { + // 0x8b DW_OP_breg27 + "DW_OP_breg27", + &DwarfOp::op_breg, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_sleb128}, + }, + { + // 0x8c DW_OP_breg28 + "DW_OP_breg28", + &DwarfOp::op_breg, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_sleb128}, + }, + { + // 0x8d DW_OP_breg29 + "DW_OP_breg29", + &DwarfOp::op_breg, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_sleb128}, + }, + { + // 0x8e DW_OP_breg30 + "DW_OP_breg30", + &DwarfOp::op_breg, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_sleb128}, + }, + { + // 0x8f DW_OP_breg31 + "DW_OP_breg31", + &DwarfOp::op_breg, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_sleb128}, + }, + { + // 0x90 DW_OP_regx + "DW_OP_regx", + &DwarfOp::op_regx, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_uleb128}, + }, + { + // 0x91 DW_OP_fbreg + "DW_OP_fbreg", + &DwarfOp::op_not_implemented, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_sleb128}, + }, + { + // 0x92 DW_OP_bregx + "DW_OP_bregx", + &DwarfOp::op_bregx, + DWARF_VERSION_2, + 0, + 2, + {DW_EH_PE_uleb128, DW_EH_PE_sleb128}, + }, + { + // 0x93 DW_OP_piece + "DW_OP_piece", + &DwarfOp::op_not_implemented, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_uleb128}, + }, + { + // 0x94 DW_OP_deref_size + "DW_OP_deref_size", + &DwarfOp::op_deref_size, + DWARF_VERSION_2, + 1, + 1, + {DW_EH_PE_udata1}, + }, + { + // 0x95 DW_OP_xderef_size + "DW_OP_xderef_size", + &DwarfOp::op_not_implemented, + DWARF_VERSION_2, + 0, + 1, + {DW_EH_PE_udata1}, + }, + { + // 0x96 DW_OP_nop + "DW_OP_nop", + &DwarfOp::op_nop, + DWARF_VERSION_2, + 0, + 0, + {}, + }, + { + // 0x97 DW_OP_push_object_address + "DW_OP_push_object_address", + &DwarfOp::op_not_implemented, + DWARF_VERSION_3, + 0, + 0, + {}, + }, + { + // 0x98 DW_OP_call2 + "DW_OP_call2", + &DwarfOp::op_not_implemented, + DWARF_VERSION_3, + 0, + 1, + {DW_EH_PE_udata2}, + }, + { + // 0x99 DW_OP_call4 + "DW_OP_call4", + &DwarfOp::op_not_implemented, + DWARF_VERSION_3, + 0, + 1, + {DW_EH_PE_udata4}, + }, + { + // 0x9a DW_OP_call_ref + "DW_OP_call_ref", + &DwarfOp::op_not_implemented, + DWARF_VERSION_3, + 0, + 0, // Has a different sized operand (4 bytes or 8 bytes). + {}, + }, + { + // 0x9b DW_OP_form_tls_address + "DW_OP_form_tls_address", + &DwarfOp::op_not_implemented, + DWARF_VERSION_3, + 0, + 0, + {}, + }, + { + // 0x9c DW_OP_call_frame_cfa + "DW_OP_call_frame_cfa", + &DwarfOp::op_not_implemented, + DWARF_VERSION_3, + 0, + 0, + {}, + }, + { + // 0x9d DW_OP_bit_piece + "DW_OP_bit_piece", + &DwarfOp::op_not_implemented, + DWARF_VERSION_3, + 0, + 2, + {DW_EH_PE_uleb128, DW_EH_PE_uleb128}, + }, + { + // 0x9e DW_OP_implicit_value + "DW_OP_implicit_value", + &DwarfOp::op_not_implemented, + DWARF_VERSION_4, + 0, + 1, + {DW_EH_PE_uleb128}, + }, + { + // 0x9f DW_OP_stack_value + "DW_OP_stack_value", + &DwarfOp::op_not_implemented, + DWARF_VERSION_4, + 1, + 0, + {}, + }, + {nullptr, nullptr, 0, 0, 0, {}}, // 0xa0 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xa1 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xa2 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xa3 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xa4 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xa5 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xa6 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xa7 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xa8 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xa9 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xaa illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xab illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xac illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xad illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xae illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xaf illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xb0 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xb1 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xb2 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xb3 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xb4 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xb5 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xb6 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xb7 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xb8 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xb9 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xba illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xbb illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xbc illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xbd illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xbe illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xbf illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xc0 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xc1 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xc2 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xc3 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xc4 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xc5 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xc6 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xc7 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xc8 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xc9 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xca illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xcb illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xcc illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xcd illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xce illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xcf illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xd0 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xd1 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xd2 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xd3 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xd4 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xd5 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xd6 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xd7 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xd8 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xd9 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xda illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xdb illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xdc illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xdd illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xde illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xdf illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xe0 DW_OP_lo_user + {nullptr, nullptr, 0, 0, 0, {}}, // 0xe1 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xe2 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xe3 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xe4 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xe5 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xe6 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xe7 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xe8 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xe9 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xea illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xeb illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xec illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xed illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xee illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xef illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xf0 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xf1 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xf2 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xf3 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xf4 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xf5 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xf6 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xf7 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xf8 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xf9 illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xfa illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xfb illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xfc illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xfd illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xfe illegal op + {nullptr, nullptr, 0, 0, 0, {}}, // 0xff DW_OP_hi_user + }; +}; + +#endif // _LIBUNWINDSTACK_DWARF_OP_H diff --git a/libunwindstack/tests/DwarfOpLogTest.cpp b/libunwindstack/tests/DwarfOpLogTest.cpp new file mode 100644 index 000000000..d18aad056 --- /dev/null +++ b/libunwindstack/tests/DwarfOpLogTest.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> + +#include <ios> +#include <vector> + +#include <gtest/gtest.h> + +#include "DwarfError.h" +#include "DwarfMemory.h" +#include "DwarfOp.h" +#include "Log.h" +#include "Regs.h" + +#include "MemoryFake.h" + +template <typename TypeParam> +class DwarfOpLogTest : public ::testing::Test { + protected: + void SetUp() override { + op_memory_.Clear(); + regular_memory_.Clear(); + mem_.reset(new DwarfMemory(&op_memory_)); + op_.reset(new DwarfOp<TypeParam>(mem_.get(), ®ular_memory_)); + } + + MemoryFake op_memory_; + MemoryFake regular_memory_; + + std::unique_ptr<DwarfMemory> mem_; + std::unique_ptr<DwarfOp<TypeParam>> op_; +}; +TYPED_TEST_CASE_P(DwarfOpLogTest); + +TYPED_TEST_P(DwarfOpLogTest, multiple_ops) { + // Multi operation opcodes. + std::vector<uint8_t> opcode_buffer = { + 0x0a, 0x20, 0x10, 0x08, 0x03, 0x12, 0x27, + }; + this->op_memory_.SetMemory(0, opcode_buffer); + + std::vector<std::string> lines; + this->op_->GetLogInfo(0, opcode_buffer.size(), &lines); + std::vector<std::string> expected{ + "DW_OP_const2u 4128", "Raw Data: 0x0a 0x20 0x10", "DW_OP_const1u 3", "Raw Data: 0x08 0x03", + "DW_OP_dup", "Raw Data: 0x12", "DW_OP_xor", "Raw Data: 0x27"}; + ASSERT_EQ(expected, lines); +} + +REGISTER_TYPED_TEST_CASE_P(DwarfOpLogTest, multiple_ops); + +typedef ::testing::Types<uint32_t, uint64_t> DwarfOpLogTestTypes; +INSTANTIATE_TYPED_TEST_CASE_P(, DwarfOpLogTest, DwarfOpLogTestTypes); diff --git a/libunwindstack/tests/DwarfOpTest.cpp b/libunwindstack/tests/DwarfOpTest.cpp new file mode 100644 index 000000000..520c545d6 --- /dev/null +++ b/libunwindstack/tests/DwarfOpTest.cpp @@ -0,0 +1,1593 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> + +#include <ios> +#include <vector> + +#include <gtest/gtest.h> + +#include "DwarfError.h" +#include "DwarfMemory.h" +#include "DwarfOp.h" +#include "Log.h" +#include "Regs.h" + +#include "MemoryFake.h" + +template <typename TypeParam> +class RegsFake : public RegsTmpl<TypeParam> { + public: + RegsFake(uint16_t total_regs, uint16_t sp_reg) + : RegsTmpl<TypeParam>(total_regs, sp_reg, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {} + virtual ~RegsFake() = default; + + uint64_t GetRelPc(Elf*, const MapInfo*) override { return 0; } + uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; } + bool GetReturnAddressFromDefault(Memory*, uint64_t*) { return false; } +}; + +template <typename TypeParam> +class DwarfOpTest : public ::testing::Test { + protected: + void SetUp() override { + op_memory_.Clear(); + regular_memory_.Clear(); + mem_.reset(new DwarfMemory(&op_memory_)); + op_.reset(new DwarfOp<TypeParam>(mem_.get(), ®ular_memory_)); + } + + MemoryFake op_memory_; + MemoryFake regular_memory_; + + std::unique_ptr<DwarfMemory> mem_; + std::unique_ptr<DwarfOp<TypeParam>> op_; +}; +TYPED_TEST_CASE_P(DwarfOpTest); + +TYPED_TEST_P(DwarfOpTest, decode) { + // Memory error. + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->last_error()); + + // No error. + this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x96}); + this->mem_->set_cur_offset(0); + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_NONE, this->op_->last_error()); + ASSERT_EQ(0x96U, this->op_->cur_op()); + ASSERT_EQ(1U, this->mem_->cur_offset()); +} + +TYPED_TEST_P(DwarfOpTest, eval) { + // Memory error. + ASSERT_FALSE(this->op_->Eval(0, 2, DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->last_error()); + + // Register set. + // Do this first, to verify that subsequent calls reset the value. + this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x50}); + ASSERT_TRUE(this->op_->Eval(0, 1, DWARF_VERSION_MAX)); + ASSERT_TRUE(this->op_->is_register()); + ASSERT_EQ(1U, this->mem_->cur_offset()); + ASSERT_EQ(1U, this->op_->StackSize()); + + // Multi operation opcodes. + std::vector<uint8_t> opcode_buffer = { + 0x08, 0x04, 0x08, 0x03, 0x08, 0x02, 0x08, 0x01, + }; + this->op_memory_.SetMemory(0, opcode_buffer); + + ASSERT_TRUE(this->op_->Eval(0, 8, DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_NONE, this->op_->last_error()); + ASSERT_FALSE(this->op_->is_register()); + ASSERT_EQ(8U, this->mem_->cur_offset()); + ASSERT_EQ(4U, this->op_->StackSize()); + ASSERT_EQ(1U, this->op_->StackAt(0)); + ASSERT_EQ(2U, this->op_->StackAt(1)); + ASSERT_EQ(3U, this->op_->StackAt(2)); + ASSERT_EQ(4U, this->op_->StackAt(3)); + + // Infinite loop. + this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x2f, 0xfd, 0xff}); + ASSERT_FALSE(this->op_->Eval(0, 4, DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_TOO_MANY_ITERATIONS, this->op_->last_error()); + ASSERT_FALSE(this->op_->is_register()); + ASSERT_EQ(0U, this->op_->StackSize()); +} + +TYPED_TEST_P(DwarfOpTest, illegal_opcode) { + // Fill the buffer with all of the illegal opcodes. + std::vector<uint8_t> opcode_buffer = {0x00, 0x01, 0x02, 0x04, 0x05, 0x07}; + for (size_t opcode = 0xa0; opcode < 256; opcode++) { + opcode_buffer.push_back(opcode); + } + this->op_memory_.SetMemory(0, opcode_buffer); + + for (size_t i = 0; i < opcode_buffer.size(); i++) { + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error()); + ASSERT_EQ(opcode_buffer[i], this->op_->cur_op()); + } +} + +TYPED_TEST_P(DwarfOpTest, illegal_in_version3) { + std::vector<uint8_t> opcode_buffer = {0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d}; + this->op_memory_.SetMemory(0, opcode_buffer); + + for (size_t i = 0; i < opcode_buffer.size(); i++) { + ASSERT_FALSE(this->op_->Decode(2)); + ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error()); + ASSERT_EQ(opcode_buffer[i], this->op_->cur_op()); + } +} + +TYPED_TEST_P(DwarfOpTest, illegal_in_version4) { + std::vector<uint8_t> opcode_buffer = {0x9e, 0x9f}; + this->op_memory_.SetMemory(0, opcode_buffer); + + for (size_t i = 0; i < opcode_buffer.size(); i++) { + ASSERT_FALSE(this->op_->Decode(3)); + ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error()); + ASSERT_EQ(opcode_buffer[i], this->op_->cur_op()); + } +} + +TYPED_TEST_P(DwarfOpTest, not_implemented) { + std::vector<uint8_t> opcode_buffer = { + // Push values so that any not implemented ops will return the right error. + 0x08, 0x03, 0x08, 0x02, 0x08, 0x01, + // xderef + 0x18, + // fbreg + 0x91, 0x01, + // piece + 0x93, 0x01, + // xderef_size + 0x95, 0x01, + // push_object_address + 0x97, + // call2 + 0x98, 0x01, 0x02, + // call4 + 0x99, 0x01, 0x02, 0x03, 0x04, + // call_ref + 0x9a, + // form_tls_address + 0x9b, + // call_frame_cfa + 0x9c, + // bit_piece + 0x9d, 0x01, 0x01, + // implicit_value + 0x9e, 0x01, + // stack_value + 0x9f, + }; + this->op_memory_.SetMemory(0, opcode_buffer); + + // Push the stack values. + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + + while (this->mem_->cur_offset() < opcode_buffer.size()) { + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_NOT_IMPLEMENTED, this->op_->last_error()); + } +} + +TYPED_TEST_P(DwarfOpTest, op_addr) { + std::vector<uint8_t> opcode_buffer = {0x03, 0x12, 0x23, 0x34, 0x45}; + if (sizeof(TypeParam) == 8) { + opcode_buffer.push_back(0x56); + opcode_buffer.push_back(0x67); + opcode_buffer.push_back(0x78); + opcode_buffer.push_back(0x89); + } + this->op_memory_.SetMemory(0, opcode_buffer); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x03, this->op_->cur_op()); + ASSERT_EQ(1U, this->op_->StackSize()); + if (sizeof(TypeParam) == 4) { + ASSERT_EQ(0x45342312U, this->op_->StackAt(0)); + } else { + ASSERT_EQ(0x8978675645342312UL, this->op_->StackAt(0)); + } +} + +TYPED_TEST_P(DwarfOpTest, op_deref) { + std::vector<uint8_t> opcode_buffer = { + // Try a dereference with nothing on the stack. + 0x06, + // Add an address, then dereference. + 0x0a, 0x10, 0x20, 0x06, + // Now do another dereference that should fail in memory. + 0x06, + }; + this->op_memory_.SetMemory(0, opcode_buffer); + TypeParam value = 0x12345678; + this->regular_memory_.SetMemory(0x2010, &value, sizeof(value)); + + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(1U, this->op_->StackSize()); + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x06, this->op_->cur_op()); + ASSERT_EQ(1U, this->op_->StackSize()); + ASSERT_EQ(value, this->op_->StackAt(0)); + + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->last_error()); +} + +TYPED_TEST_P(DwarfOpTest, op_deref_size) { + this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x94}); + TypeParam value = 0x12345678; + this->regular_memory_.SetMemory(0x2010, &value, sizeof(value)); + + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error()); + + // Read all byte sizes up to the sizeof the type. + for (size_t i = 1; i < sizeof(TypeParam); i++) { + this->op_memory_.SetMemory( + 0, std::vector<uint8_t>{0x0a, 0x10, 0x20, 0x94, static_cast<uint8_t>(i)}); + ASSERT_TRUE(this->op_->Eval(0, 5, DWARF_VERSION_MAX)) << "Failed at size " << i; + ASSERT_EQ(1U, this->op_->StackSize()) << "Failed at size " << i; + ASSERT_EQ(0x94, this->op_->cur_op()) << "Failed at size " << i; + TypeParam expected_value = 0; + memcpy(&expected_value, &value, i); + ASSERT_EQ(expected_value, this->op_->StackAt(0)) << "Failed at size " << i; + } + + // Zero byte read. + this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0a, 0x10, 0x20, 0x94, 0x00}); + ASSERT_FALSE(this->op_->Eval(0, 5, DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error()); + + // Read too many bytes. + this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0a, 0x10, 0x20, 0x94, sizeof(TypeParam) + 1}); + ASSERT_FALSE(this->op_->Eval(0, 5, DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error()); + + // Force bad memory read. + this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0a, 0x10, 0x40, 0x94, 0x01}); + ASSERT_FALSE(this->op_->Eval(0, 5, DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->last_error()); +} + +TYPED_TEST_P(DwarfOpTest, const_unsigned) { + std::vector<uint8_t> opcode_buffer = { + // const1u + 0x08, 0x12, 0x08, 0xff, + // const2u + 0x0a, 0x45, 0x12, 0x0a, 0x00, 0xff, + // const4u + 0x0c, 0x12, 0x23, 0x34, 0x45, 0x0c, 0x03, 0x02, 0x01, 0xff, + // const8u + 0x0e, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x0e, 0x87, 0x98, 0xa9, 0xba, 0xcb, + 0xdc, 0xed, 0xfe, + }; + this->op_memory_.SetMemory(0, opcode_buffer); + + // const1u + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x08, this->op_->cur_op()); + ASSERT_EQ(1U, this->op_->StackSize()); + ASSERT_EQ(0x12U, this->op_->StackAt(0)); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x08, this->op_->cur_op()); + ASSERT_EQ(2U, this->op_->StackSize()); + ASSERT_EQ(0xffU, this->op_->StackAt(0)); + + // const2u + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x0a, this->op_->cur_op()); + ASSERT_EQ(3U, this->op_->StackSize()); + ASSERT_EQ(0x1245U, this->op_->StackAt(0)); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x0a, this->op_->cur_op()); + ASSERT_EQ(4U, this->op_->StackSize()); + ASSERT_EQ(0xff00U, this->op_->StackAt(0)); + + // const4u + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x0c, this->op_->cur_op()); + ASSERT_EQ(5U, this->op_->StackSize()); + ASSERT_EQ(0x45342312U, this->op_->StackAt(0)); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x0c, this->op_->cur_op()); + ASSERT_EQ(6U, this->op_->StackSize()); + ASSERT_EQ(0xff010203U, this->op_->StackAt(0)); + + // const8u + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x0e, this->op_->cur_op()); + ASSERT_EQ(7U, this->op_->StackSize()); + if (sizeof(TypeParam) == 4) { + ASSERT_EQ(0x05060708U, this->op_->StackAt(0)); + } else { + ASSERT_EQ(0x0102030405060708ULL, this->op_->StackAt(0)); + } + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x0e, this->op_->cur_op()); + ASSERT_EQ(8U, this->op_->StackSize()); + if (sizeof(TypeParam) == 4) { + ASSERT_EQ(0xbaa99887UL, this->op_->StackAt(0)); + } else { + ASSERT_EQ(0xfeeddccbbaa99887ULL, this->op_->StackAt(0)); + } +} + +TYPED_TEST_P(DwarfOpTest, const_signed) { + std::vector<uint8_t> opcode_buffer = { + // const1s + 0x09, 0x12, 0x09, 0xff, + // const2s + 0x0b, 0x21, 0x32, 0x0b, 0x08, 0xff, + // const4s + 0x0d, 0x45, 0x34, 0x23, 0x12, 0x0d, 0x01, 0x02, 0x03, 0xff, + // const8s + 0x0f, 0x89, 0x78, 0x67, 0x56, 0x45, 0x34, 0x23, 0x12, 0x0f, 0x04, 0x03, 0x02, 0x01, 0xef, + 0xef, 0xef, 0xff, + }; + this->op_memory_.SetMemory(0, opcode_buffer); + + // const1s + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x09, this->op_->cur_op()); + ASSERT_EQ(1U, this->op_->StackSize()); + ASSERT_EQ(0x12U, this->op_->StackAt(0)); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x09, this->op_->cur_op()); + ASSERT_EQ(2U, this->op_->StackSize()); + ASSERT_EQ(static_cast<TypeParam>(-1), this->op_->StackAt(0)); + + // const2s + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x0b, this->op_->cur_op()); + ASSERT_EQ(3U, this->op_->StackSize()); + ASSERT_EQ(0x3221U, this->op_->StackAt(0)); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x0b, this->op_->cur_op()); + ASSERT_EQ(4U, this->op_->StackSize()); + ASSERT_EQ(static_cast<TypeParam>(-248), this->op_->StackAt(0)); + + // const4s + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x0d, this->op_->cur_op()); + ASSERT_EQ(5U, this->op_->StackSize()); + ASSERT_EQ(0x12233445U, this->op_->StackAt(0)); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x0d, this->op_->cur_op()); + ASSERT_EQ(6U, this->op_->StackSize()); + ASSERT_EQ(static_cast<TypeParam>(-16580095), this->op_->StackAt(0)); + + // const8s + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x0f, this->op_->cur_op()); + ASSERT_EQ(7U, this->op_->StackSize()); + if (sizeof(TypeParam) == 4) { + ASSERT_EQ(0x56677889ULL, this->op_->StackAt(0)); + } else { + ASSERT_EQ(0x1223344556677889ULL, this->op_->StackAt(0)); + } + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x0f, this->op_->cur_op()); + ASSERT_EQ(8U, this->op_->StackSize()); + if (sizeof(TypeParam) == 4) { + ASSERT_EQ(0x01020304U, this->op_->StackAt(0)); + } else { + ASSERT_EQ(static_cast<TypeParam>(-4521264810949884LL), this->op_->StackAt(0)); + } +} + +TYPED_TEST_P(DwarfOpTest, const_uleb) { + std::vector<uint8_t> opcode_buffer = { + // Single byte ULEB128 + 0x10, 0x22, 0x10, 0x7f, + // Multi byte ULEB128 + 0x10, 0xa2, 0x22, 0x10, 0xa2, 0x74, 0x10, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, + 0x09, 0x10, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x79, + }; + this->op_memory_.SetMemory(0, opcode_buffer); + + // Single byte ULEB128 + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x10, this->op_->cur_op()); + ASSERT_EQ(1U, this->op_->StackSize()); + ASSERT_EQ(0x22U, this->op_->StackAt(0)); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x10, this->op_->cur_op()); + ASSERT_EQ(2U, this->op_->StackSize()); + ASSERT_EQ(0x7fU, this->op_->StackAt(0)); + + // Multi byte ULEB128 + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x10, this->op_->cur_op()); + ASSERT_EQ(3U, this->op_->StackSize()); + ASSERT_EQ(0x1122U, this->op_->StackAt(0)); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x10, this->op_->cur_op()); + ASSERT_EQ(4U, this->op_->StackSize()); + ASSERT_EQ(0x3a22U, this->op_->StackAt(0)); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x10, this->op_->cur_op()); + ASSERT_EQ(5U, this->op_->StackSize()); + if (sizeof(TypeParam) == 4) { + ASSERT_EQ(0x5080c101U, this->op_->StackAt(0)); + } else { + ASSERT_EQ(0x9101c305080c101ULL, this->op_->StackAt(0)); + } + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x10, this->op_->cur_op()); + ASSERT_EQ(6U, this->op_->StackSize()); + if (sizeof(TypeParam) == 4) { + ASSERT_EQ(0x5080c101U, this->op_->StackAt(0)); + } else { + ASSERT_EQ(0x79101c305080c101ULL, this->op_->StackAt(0)); + } +} + +TYPED_TEST_P(DwarfOpTest, const_sleb) { + std::vector<uint8_t> opcode_buffer = { + // Single byte SLEB128 + 0x11, 0x22, 0x11, 0x7f, + // Multi byte SLEB128 + 0x11, 0xa2, 0x22, 0x11, 0xa2, 0x74, 0x11, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, + 0x09, 0x11, + }; + if (sizeof(TypeParam) == 4) { + opcode_buffer.push_back(0xb8); + opcode_buffer.push_back(0xd3); + opcode_buffer.push_back(0x63); + } else { + opcode_buffer.push_back(0x81); + opcode_buffer.push_back(0x82); + opcode_buffer.push_back(0x83); + opcode_buffer.push_back(0x84); + opcode_buffer.push_back(0x85); + opcode_buffer.push_back(0x86); + opcode_buffer.push_back(0x87); + opcode_buffer.push_back(0x88); + opcode_buffer.push_back(0x79); + } + this->op_memory_.SetMemory(0, opcode_buffer); + + // Single byte SLEB128 + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x11, this->op_->cur_op()); + ASSERT_EQ(1U, this->op_->StackSize()); + ASSERT_EQ(0x22U, this->op_->StackAt(0)); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x11, this->op_->cur_op()); + ASSERT_EQ(2U, this->op_->StackSize()); + ASSERT_EQ(static_cast<TypeParam>(-1), this->op_->StackAt(0)); + + // Multi byte SLEB128 + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x11, this->op_->cur_op()); + ASSERT_EQ(3U, this->op_->StackSize()); + ASSERT_EQ(0x1122U, this->op_->StackAt(0)); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x11, this->op_->cur_op()); + ASSERT_EQ(4U, this->op_->StackSize()); + ASSERT_EQ(static_cast<TypeParam>(-1502), this->op_->StackAt(0)); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x11, this->op_->cur_op()); + ASSERT_EQ(5U, this->op_->StackSize()); + if (sizeof(TypeParam) == 4) { + ASSERT_EQ(0x5080c101U, this->op_->StackAt(0)); + } else { + ASSERT_EQ(0x9101c305080c101ULL, this->op_->StackAt(0)); + } + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x11, this->op_->cur_op()); + ASSERT_EQ(6U, this->op_->StackSize()); + if (sizeof(TypeParam) == 4) { + ASSERT_EQ(static_cast<TypeParam>(-464456), this->op_->StackAt(0)); + } else { + ASSERT_EQ(static_cast<TypeParam>(-499868564803501823LL), this->op_->StackAt(0)); + } +} + +TYPED_TEST_P(DwarfOpTest, op_dup) { + std::vector<uint8_t> opcode_buffer = { + // Should fail since nothing is on the stack. + 0x12, + // Push on a value and dup. + 0x08, 0x15, 0x12, + // Do it again. + 0x08, 0x23, 0x12, + }; + this->op_memory_.SetMemory(0, opcode_buffer); + + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x12, this->op_->cur_op()); + ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(1U, this->op_->StackSize()); + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x12, this->op_->cur_op()); + ASSERT_EQ(2U, this->op_->StackSize()); + ASSERT_EQ(0x15U, this->op_->StackAt(0)); + ASSERT_EQ(0x15U, this->op_->StackAt(1)); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(3U, this->op_->StackSize()); + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x12, this->op_->cur_op()); + ASSERT_EQ(4U, this->op_->StackSize()); + ASSERT_EQ(0x23U, this->op_->StackAt(0)); + ASSERT_EQ(0x23U, this->op_->StackAt(1)); + ASSERT_EQ(0x15U, this->op_->StackAt(2)); + ASSERT_EQ(0x15U, this->op_->StackAt(3)); +} + +TYPED_TEST_P(DwarfOpTest, op_drop) { + std::vector<uint8_t> opcode_buffer = { + // Push a couple of values. + 0x08, 0x10, 0x08, 0x20, + // Drop the values. + 0x13, 0x13, + // Attempt to drop empty stack. + 0x13, + }; + this->op_memory_.SetMemory(0, opcode_buffer); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(1U, this->op_->StackSize()); + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(2U, this->op_->StackSize()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x13, this->op_->cur_op()); + ASSERT_EQ(1U, this->op_->StackSize()); + ASSERT_EQ(0x10U, this->op_->StackAt(0)); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x13, this->op_->cur_op()); + ASSERT_EQ(0U, this->op_->StackSize()); + + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x13, this->op_->cur_op()); + ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error()); +} + +TYPED_TEST_P(DwarfOpTest, op_over) { + std::vector<uint8_t> opcode_buffer = { + // Push a couple of values. + 0x08, 0x1a, 0x08, 0xed, + // Copy a value. + 0x14, + // Remove all but one element. + 0x13, 0x13, + // Provoke a failure with this opcode. + 0x14, + }; + this->op_memory_.SetMemory(0, opcode_buffer); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(1U, this->op_->StackSize()); + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(2U, this->op_->StackSize()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x14, this->op_->cur_op()); + ASSERT_EQ(3U, this->op_->StackSize()); + ASSERT_EQ(0x1aU, this->op_->StackAt(0)); + ASSERT_EQ(0xedU, this->op_->StackAt(1)); + ASSERT_EQ(0x1aU, this->op_->StackAt(2)); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(2U, this->op_->StackSize()); + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(1U, this->op_->StackSize()); + + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x14, this->op_->cur_op()); + ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error()); +} + +TYPED_TEST_P(DwarfOpTest, op_pick) { + std::vector<uint8_t> opcode_buffer = { + // Push a few values. + 0x08, 0x1a, 0x08, 0xed, 0x08, 0x34, + // Copy the value at offset 2. + 0x15, 0x01, + // Copy the last value in the stack. + 0x15, 0x03, + // Choose an invalid index. + 0x15, 0x10, + }; + this->op_memory_.SetMemory(0, opcode_buffer); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(1U, this->op_->StackSize()); + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(2U, this->op_->StackSize()); + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(3U, this->op_->StackSize()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x15, this->op_->cur_op()); + ASSERT_EQ(4U, this->op_->StackSize()); + ASSERT_EQ(0xedU, this->op_->StackAt(0)); + ASSERT_EQ(0x34U, this->op_->StackAt(1)); + ASSERT_EQ(0xedU, this->op_->StackAt(2)); + ASSERT_EQ(0x1aU, this->op_->StackAt(3)); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x15, this->op_->cur_op()); + ASSERT_EQ(5U, this->op_->StackSize()); + ASSERT_EQ(0x1aU, this->op_->StackAt(0)); + ASSERT_EQ(0xedU, this->op_->StackAt(1)); + ASSERT_EQ(0x34U, this->op_->StackAt(2)); + ASSERT_EQ(0xedU, this->op_->StackAt(3)); + ASSERT_EQ(0x1aU, this->op_->StackAt(4)); + + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x15, this->op_->cur_op()); + ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error()); +} + +TYPED_TEST_P(DwarfOpTest, op_swap) { + std::vector<uint8_t> opcode_buffer = { + // Push a couple of values. + 0x08, 0x26, 0x08, 0xab, + // Swap values. + 0x16, + // Pop a value to cause a failure. + 0x13, 0x16, + }; + this->op_memory_.SetMemory(0, opcode_buffer); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(1U, this->op_->StackSize()); + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(2U, this->op_->StackSize()); + ASSERT_EQ(0xabU, this->op_->StackAt(0)); + ASSERT_EQ(0x26U, this->op_->StackAt(1)); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x16, this->op_->cur_op()); + ASSERT_EQ(2U, this->op_->StackSize()); + ASSERT_EQ(0x26U, this->op_->StackAt(0)); + ASSERT_EQ(0xabU, this->op_->StackAt(1)); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(1U, this->op_->StackSize()); + + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x16, this->op_->cur_op()); + ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error()); +} + +TYPED_TEST_P(DwarfOpTest, op_rot) { + std::vector<uint8_t> opcode_buffer = { + // Rotate that should cause a failure. + 0x17, 0x08, 0x10, + // Only 1 value on stack, should fail. + 0x17, 0x08, 0x20, + // Only 2 values on stack, should fail. + 0x17, 0x08, 0x30, + // Should rotate properly. + 0x17, + }; + this->op_memory_.SetMemory(0, opcode_buffer); + + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(1U, this->op_->StackSize()); + + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(2U, this->op_->StackSize()); + + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(3U, this->op_->StackSize()); + ASSERT_EQ(0x30U, this->op_->StackAt(0)); + ASSERT_EQ(0x20U, this->op_->StackAt(1)); + ASSERT_EQ(0x10U, this->op_->StackAt(2)); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x17, this->op_->cur_op()); + ASSERT_EQ(3U, this->op_->StackSize()); + ASSERT_EQ(0x20U, this->op_->StackAt(0)); + ASSERT_EQ(0x10U, this->op_->StackAt(1)); + ASSERT_EQ(0x30U, this->op_->StackAt(2)); +} + +TYPED_TEST_P(DwarfOpTest, op_abs) { + std::vector<uint8_t> opcode_buffer = { + // Abs that should fail. + 0x19, + // A value that is already positive. + 0x08, 0x10, 0x19, + // A value that is negative. + 0x11, 0x7f, 0x19, + // A value that is large and negative. + 0x11, 0x81, 0x80, 0x80, 0x80, + }; + if (sizeof(TypeParam) == 4) { + opcode_buffer.push_back(0x08); + } else { + opcode_buffer.push_back(0x80); + opcode_buffer.push_back(0x80); + opcode_buffer.push_back(0x01); + } + opcode_buffer.push_back(0x19); + this->op_memory_.SetMemory(0, opcode_buffer); + + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(1U, this->op_->StackSize()); + ASSERT_EQ(0x10U, this->op_->StackAt(0)); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x19, this->op_->cur_op()); + ASSERT_EQ(1U, this->op_->StackSize()); + ASSERT_EQ(0x10U, this->op_->StackAt(0)); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(2U, this->op_->StackSize()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x19, this->op_->cur_op()); + ASSERT_EQ(2U, this->op_->StackSize()); + ASSERT_EQ(0x1U, this->op_->StackAt(0)); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(3U, this->op_->StackSize()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x19, this->op_->cur_op()); + ASSERT_EQ(3U, this->op_->StackSize()); + if (sizeof(TypeParam) == 4) { + ASSERT_EQ(2147483647U, this->op_->StackAt(0)); + } else { + ASSERT_EQ(4398046511105UL, this->op_->StackAt(0)); + } +} + +TYPED_TEST_P(DwarfOpTest, op_and) { + std::vector<uint8_t> opcode_buffer = { + // No stack, and op will fail. + 0x1b, + // Push a single value. + 0x08, 0x20, + // One element stack, and op will fail. + 0x1b, + // Push another value. + 0x08, 0x02, 0x1b, + // Push on two negative values. + 0x11, 0x7c, 0x11, 0x7f, 0x1b, + // Push one negative, one positive. + 0x11, 0x10, 0x11, 0x7c, 0x1b, + // Divide by zero. + 0x11, 0x10, 0x11, 0x00, 0x1b, + }; + this->op_memory_.SetMemory(0, opcode_buffer); + + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(1U, this->op_->StackSize()); + + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error()); + + // Two positive values. + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(2U, this->op_->StackSize()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x1b, this->op_->cur_op()); + ASSERT_EQ(1U, this->op_->StackSize()); + ASSERT_EQ(0x10U, this->op_->StackAt(0)); + + // Two negative values. + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(2U, this->op_->StackSize()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(3U, this->op_->StackSize()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x1b, this->op_->cur_op()); + ASSERT_EQ(2U, this->op_->StackSize()); + ASSERT_EQ(0x04U, this->op_->StackAt(0)); + + // One negative value, one positive value. + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(3U, this->op_->StackSize()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(4U, this->op_->StackSize()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x1b, this->op_->cur_op()); + ASSERT_EQ(3U, this->op_->StackSize()); + ASSERT_EQ(static_cast<TypeParam>(-4), this->op_->StackAt(0)); + + // Divide by zero. + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(4U, this->op_->StackSize()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(5U, this->op_->StackSize()); + + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error()); +} + +TYPED_TEST_P(DwarfOpTest, op_div) { + std::vector<uint8_t> opcode_buffer = { + // No stack, and op will fail. + 0x1a, + // Push a single value. + 0x08, 0x48, + // One element stack, and op will fail. + 0x1a, + // Push another value. + 0x08, 0xf0, 0x1a, + }; + this->op_memory_.SetMemory(0, opcode_buffer); + + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(1U, this->op_->StackSize()); + + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(2U, this->op_->StackSize()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x1a, this->op_->cur_op()); + ASSERT_EQ(1U, this->op_->StackSize()); + ASSERT_EQ(0x40U, this->op_->StackAt(0)); +} + +TYPED_TEST_P(DwarfOpTest, op_minus) { + std::vector<uint8_t> opcode_buffer = { + // No stack, and op will fail. + 0x1c, + // Push a single value. + 0x08, 0x48, + // One element stack, and op will fail. + 0x1c, + // Push another value. + 0x08, 0x04, 0x1c, + }; + this->op_memory_.SetMemory(0, opcode_buffer); + + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(1U, this->op_->StackSize()); + + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(2U, this->op_->StackSize()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x1c, this->op_->cur_op()); + ASSERT_EQ(1U, this->op_->StackSize()); + ASSERT_EQ(0x44U, this->op_->StackAt(0)); +} + +TYPED_TEST_P(DwarfOpTest, op_mod) { + std::vector<uint8_t> opcode_buffer = { + // No stack, and op will fail. + 0x1d, + // Push a single value. + 0x08, 0x47, + // One element stack, and op will fail. + 0x1d, + // Push another value. + 0x08, 0x04, 0x1d, + // Try a mod of zero. + 0x08, 0x01, 0x08, 0x00, 0x1d, + }; + this->op_memory_.SetMemory(0, opcode_buffer); + + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(1U, this->op_->StackSize()); + + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(2U, this->op_->StackSize()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x1d, this->op_->cur_op()); + ASSERT_EQ(1U, this->op_->StackSize()); + ASSERT_EQ(0x03U, this->op_->StackAt(0)); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(2U, this->op_->StackSize()); + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(3U, this->op_->StackSize()); + + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error()); +} + +TYPED_TEST_P(DwarfOpTest, op_mul) { + std::vector<uint8_t> opcode_buffer = { + // No stack, and op will fail. + 0x1e, + // Push a single value. + 0x08, 0x48, + // One element stack, and op will fail. + 0x1e, + // Push another value. + 0x08, 0x04, 0x1e, + }; + this->op_memory_.SetMemory(0, opcode_buffer); + + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(1U, this->op_->StackSize()); + + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(2U, this->op_->StackSize()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x1e, this->op_->cur_op()); + ASSERT_EQ(1U, this->op_->StackSize()); + ASSERT_EQ(0x120U, this->op_->StackAt(0)); +} + +TYPED_TEST_P(DwarfOpTest, op_neg) { + std::vector<uint8_t> opcode_buffer = { + // No stack, and op will fail. + 0x1f, + // Push a single value. + 0x08, 0x48, 0x1f, + // Push a negative value. + 0x11, 0x7f, 0x1f, + }; + this->op_memory_.SetMemory(0, opcode_buffer); + + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(1U, this->op_->StackSize()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x1f, this->op_->cur_op()); + ASSERT_EQ(1U, this->op_->StackSize()); + ASSERT_EQ(static_cast<TypeParam>(-72), this->op_->StackAt(0)); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(2U, this->op_->StackSize()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x1f, this->op_->cur_op()); + ASSERT_EQ(2U, this->op_->StackSize()); + ASSERT_EQ(0x01U, this->op_->StackAt(0)); +} + +TYPED_TEST_P(DwarfOpTest, op_not) { + std::vector<uint8_t> opcode_buffer = { + // No stack, and op will fail. + 0x20, + // Push a single value. + 0x08, 0x4, 0x20, + // Push a negative value. + 0x11, 0x7c, 0x20, + }; + this->op_memory_.SetMemory(0, opcode_buffer); + + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(1U, this->op_->StackSize()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x20, this->op_->cur_op()); + ASSERT_EQ(1U, this->op_->StackSize()); + ASSERT_EQ(static_cast<TypeParam>(-5), this->op_->StackAt(0)); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(2U, this->op_->StackSize()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x20, this->op_->cur_op()); + ASSERT_EQ(2U, this->op_->StackSize()); + ASSERT_EQ(0x03U, this->op_->StackAt(0)); +} + +TYPED_TEST_P(DwarfOpTest, op_or) { + std::vector<uint8_t> opcode_buffer = { + // No stack, and op will fail. + 0x21, + // Push a single value. + 0x08, 0x48, + // One element stack, and op will fail. + 0x21, + // Push another value. + 0x08, 0xf4, 0x21, + }; + this->op_memory_.SetMemory(0, opcode_buffer); + + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(1U, this->op_->StackSize()); + + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(2U, this->op_->StackSize()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x21, this->op_->cur_op()); + ASSERT_EQ(1U, this->op_->StackSize()); + ASSERT_EQ(0xfcU, this->op_->StackAt(0)); +} + +TYPED_TEST_P(DwarfOpTest, op_plus) { + std::vector<uint8_t> opcode_buffer = { + // No stack, and op will fail. + 0x22, + // Push a single value. + 0x08, 0xff, + // One element stack, and op will fail. + 0x22, + // Push another value. + 0x08, 0xf2, 0x22, + }; + this->op_memory_.SetMemory(0, opcode_buffer); + + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(1U, this->op_->StackSize()); + + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(2U, this->op_->StackSize()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x22, this->op_->cur_op()); + ASSERT_EQ(1U, this->op_->StackSize()); + ASSERT_EQ(0x1f1U, this->op_->StackAt(0)); +} + +TYPED_TEST_P(DwarfOpTest, op_plus_uconst) { + std::vector<uint8_t> opcode_buffer = { + // No stack, and op will fail. + 0x23, + // Push a single value. + 0x08, 0x50, 0x23, 0x80, 0x51, + }; + this->op_memory_.SetMemory(0, opcode_buffer); + + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(1U, this->op_->StackSize()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x23, this->op_->cur_op()); + ASSERT_EQ(1U, this->op_->StackSize()); + ASSERT_EQ(0x28d0U, this->op_->StackAt(0)); +} + +TYPED_TEST_P(DwarfOpTest, op_shl) { + std::vector<uint8_t> opcode_buffer = { + // No stack, and op will fail. + 0x24, + // Push a single value. + 0x08, 0x67, + // One element stack, and op will fail. + 0x24, + // Push another value. + 0x08, 0x03, 0x24, + }; + this->op_memory_.SetMemory(0, opcode_buffer); + + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(1U, this->op_->StackSize()); + + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(2U, this->op_->StackSize()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x24, this->op_->cur_op()); + ASSERT_EQ(1U, this->op_->StackSize()); + ASSERT_EQ(0x338U, this->op_->StackAt(0)); +} + +TYPED_TEST_P(DwarfOpTest, op_shr) { + std::vector<uint8_t> opcode_buffer = { + // No stack, and op will fail. + 0x25, + // Push a single value. + 0x11, 0x70, + // One element stack, and op will fail. + 0x25, + // Push another value. + 0x08, 0x03, 0x25, + }; + this->op_memory_.SetMemory(0, opcode_buffer); + + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(1U, this->op_->StackSize()); + + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(2U, this->op_->StackSize()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x25, this->op_->cur_op()); + ASSERT_EQ(1U, this->op_->StackSize()); + if (sizeof(TypeParam) == 4) { + ASSERT_EQ(0x1ffffffeU, this->op_->StackAt(0)); + } else { + ASSERT_EQ(0x1ffffffffffffffeULL, this->op_->StackAt(0)); + } +} + +TYPED_TEST_P(DwarfOpTest, op_shra) { + std::vector<uint8_t> opcode_buffer = { + // No stack, and op will fail. + 0x26, + // Push a single value. + 0x11, 0x70, + // One element stack, and op will fail. + 0x26, + // Push another value. + 0x08, 0x03, 0x26, + }; + this->op_memory_.SetMemory(0, opcode_buffer); + + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(1U, this->op_->StackSize()); + + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(2U, this->op_->StackSize()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x26, this->op_->cur_op()); + ASSERT_EQ(1U, this->op_->StackSize()); + ASSERT_EQ(static_cast<TypeParam>(-2), this->op_->StackAt(0)); +} + +TYPED_TEST_P(DwarfOpTest, op_xor) { + std::vector<uint8_t> opcode_buffer = { + // No stack, and op will fail. + 0x27, + // Push a single value. + 0x08, 0x11, + // One element stack, and op will fail. + 0x27, + // Push another value. + 0x08, 0x41, 0x27, + }; + this->op_memory_.SetMemory(0, opcode_buffer); + + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(1U, this->op_->StackSize()); + + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(2U, this->op_->StackSize()); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x27, this->op_->cur_op()); + ASSERT_EQ(1U, this->op_->StackSize()); + ASSERT_EQ(0x50U, this->op_->StackAt(0)); +} + +TYPED_TEST_P(DwarfOpTest, op_bra) { + std::vector<uint8_t> opcode_buffer = { + // No stack, and op will fail. + 0x28, + // Push on a non-zero value with a positive branch. + 0x08, 0x11, 0x28, 0x02, 0x01, + // Push on a zero value with a positive branch. + 0x08, 0x00, 0x28, 0x05, 0x00, + // Push on a non-zero value with a negative branch. + 0x08, 0x11, 0x28, 0xfc, 0xff, + // Push on a zero value with a negative branch. + 0x08, 0x00, 0x28, 0xf0, 0xff, + }; + this->op_memory_.SetMemory(0, opcode_buffer); + + ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error()); + + // Push on a non-zero value with a positive branch. + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(1U, this->op_->StackSize()); + + uint64_t offset = this->mem_->cur_offset() + 3; + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x28, this->op_->cur_op()); + ASSERT_EQ(0U, this->op_->StackSize()); + ASSERT_EQ(offset + 0x102, this->mem_->cur_offset()); + + // Push on a zero value with a positive branch. + this->mem_->set_cur_offset(offset); + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(1U, this->op_->StackSize()); + + offset = this->mem_->cur_offset() + 3; + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x28, this->op_->cur_op()); + ASSERT_EQ(0U, this->op_->StackSize()); + ASSERT_EQ(offset - 5, this->mem_->cur_offset()); + + // Push on a non-zero value with a negative branch. + this->mem_->set_cur_offset(offset); + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(1U, this->op_->StackSize()); + + offset = this->mem_->cur_offset() + 3; + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x28, this->op_->cur_op()); + ASSERT_EQ(0U, this->op_->StackSize()); + ASSERT_EQ(offset - 4, this->mem_->cur_offset()); + + // Push on a zero value with a negative branch. + this->mem_->set_cur_offset(offset); + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(1U, this->op_->StackSize()); + + offset = this->mem_->cur_offset() + 3; + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x28, this->op_->cur_op()); + ASSERT_EQ(0U, this->op_->StackSize()); + ASSERT_EQ(offset + 16, this->mem_->cur_offset()); +} + +TYPED_TEST_P(DwarfOpTest, compare_opcode_stack_error) { + // All of the ops require two stack elements. Loop through all of these + // ops with potential errors. + std::vector<uint8_t> opcode_buffer = { + 0xff, // Place holder for compare op. + 0x08, 0x11, + 0xff, // Place holder for compare op. + }; + + for (uint8_t opcode = 0x29; opcode <= 0x2e; opcode++) { + opcode_buffer[0] = opcode; + opcode_buffer[3] = opcode; + this->op_memory_.SetMemory(0, opcode_buffer); + + ASSERT_FALSE(this->op_->Eval(0, 1, DWARF_VERSION_MAX)); + ASSERT_EQ(opcode, this->op_->cur_op()); + ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error()); + + ASSERT_FALSE(this->op_->Eval(1, 4, DWARF_VERSION_MAX)); + ASSERT_EQ(opcode, this->op_->cur_op()); + ASSERT_EQ(1U, this->op_->StackSize()); + ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error()); + } +} + +TYPED_TEST_P(DwarfOpTest, compare_opcodes) { + // Have three different checks for each compare op: + // - Both values the same. + // - The first value larger than the second. + // - The second value larger than the first. + std::vector<uint8_t> opcode_buffer = { + // Values the same. + 0x08, 0x11, 0x08, 0x11, + 0xff, // Placeholder. + // First value larger. + 0x08, 0x12, 0x08, 0x10, + 0xff, // Placeholder. + // Second value larger. + 0x08, 0x10, 0x08, 0x12, + 0xff, // Placeholder. + }; + + // Opcode followed by the expected values on the stack. + std::vector<uint8_t> expected = { + 0x29, 1, 0, 0, // eq + 0x2a, 1, 1, 0, // ge + 0x2b, 0, 1, 0, // gt + 0x2c, 1, 0, 1, // le + 0x2d, 0, 0, 1, // lt + 0x2e, 0, 1, 1, // ne + }; + for (size_t i = 0; i < expected.size(); i += 4) { + opcode_buffer[4] = expected[i]; + opcode_buffer[9] = expected[i]; + opcode_buffer[14] = expected[i]; + this->op_memory_.SetMemory(0, opcode_buffer); + + ASSERT_TRUE(this->op_->Eval(0, 15, DWARF_VERSION_MAX)) + << "Op: 0x" << std::hex << static_cast<uint32_t>(expected[i]) << " failed"; + + ASSERT_EQ(3U, this->op_->StackSize()); + ASSERT_EQ(expected[i + 1], this->op_->StackAt(2)); + ASSERT_EQ(expected[i + 2], this->op_->StackAt(1)); + ASSERT_EQ(expected[i + 3], this->op_->StackAt(0)); + } +} + +TYPED_TEST_P(DwarfOpTest, op_skip) { + std::vector<uint8_t> opcode_buffer = { + // Positive value. + 0x2f, 0x10, 0x20, + // Negative value. + 0x2f, 0xfd, 0xff, + }; + this->op_memory_.SetMemory(0, opcode_buffer); + + uint64_t offset = this->mem_->cur_offset() + 3; + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x2f, this->op_->cur_op()); + ASSERT_EQ(0U, this->op_->StackSize()); + ASSERT_EQ(offset + 0x2010, this->mem_->cur_offset()); + + this->mem_->set_cur_offset(offset); + offset = this->mem_->cur_offset() + 3; + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x2f, this->op_->cur_op()); + ASSERT_EQ(0U, this->op_->StackSize()); + ASSERT_EQ(offset - 3, this->mem_->cur_offset()); +} + +TYPED_TEST_P(DwarfOpTest, op_lit) { + std::vector<uint8_t> opcode_buffer; + + // Verify every lit opcode. + for (uint8_t op = 0x30; op <= 0x4f; op++) { + opcode_buffer.push_back(op); + } + this->op_memory_.SetMemory(0, opcode_buffer); + + for (size_t i = 0; i < opcode_buffer.size(); i++) { + uint32_t op = opcode_buffer[i]; + ASSERT_TRUE(this->op_->Eval(i, i + 1, DWARF_VERSION_MAX)) << "Failed op: 0x" << std::hex << op; + ASSERT_EQ(op, this->op_->cur_op()); + ASSERT_EQ(1U, this->op_->StackSize()) << "Failed op: 0x" << std::hex << op; + ASSERT_EQ(op - 0x30U, this->op_->StackAt(0)) << "Failed op: 0x" << std::hex << op; + } +} + +TYPED_TEST_P(DwarfOpTest, op_reg) { + std::vector<uint8_t> opcode_buffer; + + // Verify every reg opcode. + for (uint8_t op = 0x50; op <= 0x6f; op++) { + opcode_buffer.push_back(op); + } + this->op_memory_.SetMemory(0, opcode_buffer); + + for (size_t i = 0; i < opcode_buffer.size(); i++) { + uint32_t op = opcode_buffer[i]; + ASSERT_TRUE(this->op_->Eval(i, i + 1, DWARF_VERSION_MAX)) << "Failed op: 0x" << std::hex << op; + ASSERT_EQ(op, this->op_->cur_op()); + ASSERT_TRUE(this->op_->is_register()) << "Failed op: 0x" << std::hex << op; + ASSERT_EQ(1U, this->op_->StackSize()) << "Failed op: 0x" << std::hex << op; + ASSERT_EQ(op - 0x50U, this->op_->StackAt(0)) << "Failed op: 0x" << std::hex << op; + } +} + +TYPED_TEST_P(DwarfOpTest, op_regx) { + std::vector<uint8_t> opcode_buffer = { + 0x90, 0x02, 0x90, 0x80, 0x15, + }; + this->op_memory_.SetMemory(0, opcode_buffer); + + ASSERT_TRUE(this->op_->Eval(0, 2, DWARF_VERSION_MAX)); + ASSERT_EQ(0x90, this->op_->cur_op()); + ASSERT_TRUE(this->op_->is_register()); + ASSERT_EQ(1U, this->op_->StackSize()); + ASSERT_EQ(0x02U, this->op_->StackAt(0)); + + ASSERT_TRUE(this->op_->Eval(2, 5, DWARF_VERSION_MAX)); + ASSERT_EQ(0x90, this->op_->cur_op()); + ASSERT_TRUE(this->op_->is_register()); + ASSERT_EQ(1U, this->op_->StackSize()); + ASSERT_EQ(0xa80U, this->op_->StackAt(0)); +} + +TYPED_TEST_P(DwarfOpTest, op_breg) { + std::vector<uint8_t> opcode_buffer; + + // Verify every reg opcode. + for (uint8_t op = 0x70; op <= 0x8f; op++) { + // Positive value added to register. + opcode_buffer.push_back(op); + opcode_buffer.push_back(0x12); + // Negative value added to register. + opcode_buffer.push_back(op); + opcode_buffer.push_back(0x7e); + } + this->op_memory_.SetMemory(0, opcode_buffer); + + RegsFake<TypeParam> regs(32, 10); + for (size_t i = 0; i < 32; i++) { + regs[i] = i + 10; + } + this->op_->set_regs(®s); + + uint64_t offset = 0; + for (uint32_t op = 0x70; op <= 0x8f; op++) { + // Positive value added to register. + ASSERT_TRUE(this->op_->Eval(offset, offset + 2, DWARF_VERSION_MAX)) << "Failed op: 0x" + << std::hex << op; + ASSERT_EQ(op, this->op_->cur_op()); + ASSERT_EQ(1U, this->op_->StackSize()) << "Failed op: 0x" << std::hex << op; + ASSERT_EQ(op - 0x70 + 10 + 0x12, this->op_->StackAt(0)) << "Failed op: 0x" << std::hex << op; + offset += 2; + + // Negative value added to register. + ASSERT_TRUE(this->op_->Eval(offset, offset + 2, DWARF_VERSION_MAX)) << "Failed op: 0x" + << std::hex << op; + ASSERT_EQ(op, this->op_->cur_op()); + ASSERT_EQ(1U, this->op_->StackSize()) << "Failed op: 0x" << std::hex << op; + ASSERT_EQ(op - 0x70 + 10 - 2, this->op_->StackAt(0)) << "Failed op: 0x" << std::hex << op; + offset += 2; + } +} + +TYPED_TEST_P(DwarfOpTest, op_breg_invalid_register) { + std::vector<uint8_t> opcode_buffer = { + 0x7f, 0x12, 0x80, 0x12, + }; + this->op_memory_.SetMemory(0, opcode_buffer); + + RegsFake<TypeParam> regs(16, 10); + for (size_t i = 0; i < 16; i++) { + regs[i] = i + 10; + } + this->op_->set_regs(®s); + + // Should pass since this references the last regsister. + ASSERT_TRUE(this->op_->Eval(0, 2, DWARF_VERSION_MAX)); + ASSERT_EQ(0x7fU, this->op_->cur_op()); + ASSERT_EQ(1U, this->op_->StackSize()); + ASSERT_EQ(0x2bU, this->op_->StackAt(0)); + + // Should fail since this references a non-existent register. + ASSERT_FALSE(this->op_->Eval(2, 4, DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error()); +} + +TYPED_TEST_P(DwarfOpTest, op_bregx) { + std::vector<uint8_t> opcode_buffer = {// Positive value added to register. + 0x92, 0x05, 0x20, + // Negative value added to register. + 0x92, 0x06, 0x80, 0x7e, + // Illegal register. + 0x92, 0x80, 0x15, 0x80, 0x02}; + this->op_memory_.SetMemory(0, opcode_buffer); + + RegsFake<TypeParam> regs(10, 10); + regs[5] = 0x45; + regs[6] = 0x190; + this->op_->set_regs(®s); + + ASSERT_TRUE(this->op_->Eval(0, 3, DWARF_VERSION_MAX)); + ASSERT_EQ(0x92, this->op_->cur_op()); + ASSERT_EQ(1U, this->op_->StackSize()); + ASSERT_EQ(0x65U, this->op_->StackAt(0)); + + ASSERT_TRUE(this->op_->Eval(3, 7, DWARF_VERSION_MAX)); + ASSERT_EQ(0x92, this->op_->cur_op()); + ASSERT_EQ(1U, this->op_->StackSize()); + ASSERT_EQ(0x90U, this->op_->StackAt(0)); + + ASSERT_FALSE(this->op_->Eval(7, 12, DWARF_VERSION_MAX)); + ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error()); +} + +TYPED_TEST_P(DwarfOpTest, op_nop) { + this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x96}); + + ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX)); + ASSERT_EQ(0x96, this->op_->cur_op()); + ASSERT_EQ(0U, this->op_->StackSize()); +} + +REGISTER_TYPED_TEST_CASE_P(DwarfOpTest, decode, eval, illegal_opcode, illegal_in_version3, + illegal_in_version4, not_implemented, op_addr, op_deref, op_deref_size, + const_unsigned, const_signed, const_uleb, const_sleb, op_dup, op_drop, + op_over, op_pick, op_swap, op_rot, op_abs, op_and, op_div, op_minus, + op_mod, op_mul, op_neg, op_not, op_or, op_plus, op_plus_uconst, op_shl, + op_shr, op_shra, op_xor, op_bra, compare_opcode_stack_error, + compare_opcodes, op_skip, op_lit, op_reg, op_regx, op_breg, + op_breg_invalid_register, op_bregx, op_nop); + +typedef ::testing::Types<uint32_t, uint64_t> DwarfOpTestTypes; +INSTANTIATE_TYPED_TEST_CASE_P(, DwarfOpTest, DwarfOpTestTypes); |