From 8642fcbad2fbe20e84fe4f4e3e88a9258545c3ad Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Mon, 24 Apr 2017 11:14:39 -0700 Subject: [PATCH] Add the Dwarf CFA handling. Bug: 23762183 Test: Ran new unit tests. Change-Id: I31abac1238cc671b78a505da655fb6298be072b4 --- libunwindstack/Android.bp | 3 + libunwindstack/DwarfCfa.cpp | 713 +++++++++++++++++ libunwindstack/DwarfCfa.h | 255 ++++++ libunwindstack/DwarfLocation.h | 41 + libunwindstack/DwarfStructs.h | 52 ++ libunwindstack/tests/DwarfCfaLogTest.cpp | 814 +++++++++++++++++++ libunwindstack/tests/DwarfCfaTest.cpp | 959 +++++++++++++++++++++++ 7 files changed, 2837 insertions(+) create mode 100644 libunwindstack/DwarfCfa.cpp create mode 100644 libunwindstack/DwarfCfa.h create mode 100644 libunwindstack/DwarfLocation.h create mode 100644 libunwindstack/DwarfStructs.h create mode 100644 libunwindstack/tests/DwarfCfaLogTest.cpp create mode 100644 libunwindstack/tests/DwarfCfaTest.cpp diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp index 05d035396..32ed6c303 100644 --- a/libunwindstack/Android.bp +++ b/libunwindstack/Android.bp @@ -47,6 +47,7 @@ cc_defaults { srcs: [ "ArmExidx.cpp", + "DwarfCfa.cpp", "DwarfMemory.cpp", "DwarfOp.cpp", "Elf.cpp", @@ -92,6 +93,8 @@ cc_defaults { srcs: [ "tests/ArmExidxDecodeTest.cpp", "tests/ArmExidxExtractTest.cpp", + "tests/DwarfCfaLogTest.cpp", + "tests/DwarfCfaTest.cpp", "tests/DwarfMemoryTest.cpp", "tests/DwarfOpLogTest.cpp", "tests/DwarfOpTest.cpp", diff --git a/libunwindstack/DwarfCfa.cpp b/libunwindstack/DwarfCfa.cpp new file mode 100644 index 000000000..006f0397f --- /dev/null +++ b/libunwindstack/DwarfCfa.cpp @@ -0,0 +1,713 @@ +/* + * 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 +#include + +#include +#include +#include + +#include + +#include "DwarfCfa.h" +#include "DwarfEncoding.h" +#include "DwarfMemory.h" +#include "DwarfOp.h" +#include "DwarfStructs.h" +#include "Log.h" + +template +constexpr typename DwarfCfa::process_func DwarfCfa::kCallbackTable[64]; + +template +bool DwarfCfa::GetLocationInfo(uint64_t pc, uint64_t start_offset, uint64_t end_offset, + dwarf_loc_regs_t* loc_regs) { + if (cie_loc_regs_ != nullptr) { + for (const auto& entry : *cie_loc_regs_) { + (*loc_regs)[entry.first] = entry.second; + } + } + last_error_ = DWARF_ERROR_NONE; + + memory_->set_cur_offset(start_offset); + uint64_t cfa_offset; + cur_pc_ = fde_->pc_start; + while ((cfa_offset = memory_->cur_offset()) < end_offset && cur_pc_ <= pc) { + operands_.clear(); + // Read the cfa information. + uint8_t cfa_value; + if (!memory_->ReadBytes(&cfa_value, 1)) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + uint8_t cfa_low = cfa_value & 0x3f; + // Check the 2 high bits. + switch (cfa_value >> 6) { + case 1: + cur_pc_ += cfa_low * fde_->cie->code_alignment_factor; + break; + case 2: { + uint64_t offset; + if (!memory_->ReadULEB128(&offset)) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + SignedType signed_offset = + static_cast(offset) * fde_->cie->data_alignment_factor; + (*loc_regs)[cfa_low] = {.type = DWARF_LOCATION_OFFSET, + .values = {static_cast(signed_offset)}}; + break; + } + case 3: { + if (cie_loc_regs_ == nullptr) { + log(0, "restore while processing cie"); + last_error_ = DWARF_ERROR_ILLEGAL_STATE; + return false; + } + + auto reg_entry = cie_loc_regs_->find(cfa_low); + if (reg_entry == cie_loc_regs_->end()) { + loc_regs->erase(cfa_low); + } else { + (*loc_regs)[cfa_low] = reg_entry->second; + } + break; + } + case 0: { + const auto handle_func = DwarfCfa::kCallbackTable[cfa_low]; + if (handle_func == nullptr) { + last_error_ = DWARF_ERROR_ILLEGAL_VALUE; + return false; + } + + const auto cfa = &DwarfCfaInfo::kTable[cfa_low]; + for (size_t i = 0; i < cfa->num_operands; i++) { + if (cfa->operands[i] == DW_EH_PE_block) { + uint64_t block_length; + if (!memory_->ReadULEB128(&block_length)) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + operands_.push_back(block_length); + memory_->set_cur_offset(memory_->cur_offset() + block_length); + continue; + } + uint64_t value; + if (!memory_->ReadEncodedValue(cfa->operands[i], &value)) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + operands_.push_back(value); + } + + if (!(this->*handle_func)(loc_regs)) { + return false; + } + break; + } + } + } + return true; +} + +template +std::string DwarfCfa::GetOperandString(uint8_t operand, uint64_t value, + uint64_t* cur_pc) { + std::string string; + switch (operand) { + case DwarfCfaInfo::DWARF_DISPLAY_REGISTER: + string = " register(" + std::to_string(value) + ")"; + break; + case DwarfCfaInfo::DWARF_DISPLAY_SIGNED_NUMBER: + string += " " + std::to_string(static_cast(value)); + break; + case DwarfCfaInfo::DWARF_DISPLAY_ADVANCE_LOC: + *cur_pc += value; + // Fall through to log the value. + case DwarfCfaInfo::DWARF_DISPLAY_NUMBER: + string += " " + std::to_string(value); + break; + case DwarfCfaInfo::DWARF_DISPLAY_SET_LOC: + *cur_pc = value; + // Fall through to log the value. + case DwarfCfaInfo::DWARF_DISPLAY_ADDRESS: + if (std::is_same::value) { + string += android::base::StringPrintf(" 0x%" PRIx32, static_cast(value)); + } else { + string += android::base::StringPrintf(" 0x%" PRIx64, static_cast(value)); + } + break; + default: + string = " unknown"; + } + return string; +} + +template +bool DwarfCfa::LogOffsetRegisterString(uint32_t indent, uint64_t cfa_offset, + uint8_t reg) { + uint64_t offset; + if (!memory_->ReadULEB128(&offset)) { + return false; + } + uint64_t end_offset = memory_->cur_offset(); + memory_->set_cur_offset(cfa_offset); + + std::string raw_data = "Raw Data:"; + for (uint64_t i = cfa_offset; i < end_offset; i++) { + uint8_t value; + if (!memory_->ReadBytes(&value, 1)) { + return false; + } + raw_data += android::base::StringPrintf(" 0x%02x", value); + } + log(indent, "DW_CFA_offset register(%d) %" PRId64, reg, offset); + log(indent, "%s", raw_data.c_str()); + return true; +} + +template +bool DwarfCfa::LogInstruction(uint32_t indent, uint64_t cfa_offset, uint8_t op, + uint64_t* cur_pc) { + const auto* cfa = &DwarfCfaInfo::kTable[op]; + if (cfa->name == nullptr) { + log(indent, "Illegal"); + log(indent, "Raw Data: 0x%02x", op); + return true; + } + + std::string log_string(cfa->name); + std::vector expression_lines; + for (size_t i = 0; i < cfa->num_operands; i++) { + if (cfa->operands[i] == DW_EH_PE_block) { + // This is a Dwarf Expression. + uint64_t end_offset; + if (!memory_->ReadULEB128(&end_offset)) { + return false; + } + log_string += " " + std::to_string(end_offset); + end_offset += memory_->cur_offset(); + + DwarfOp op(memory_, nullptr); + op.GetLogInfo(memory_->cur_offset(), end_offset, &expression_lines); + memory_->set_cur_offset(end_offset); + } else { + uint64_t value; + if (!memory_->ReadEncodedValue(cfa->operands[i], &value)) { + return false; + } + log_string += GetOperandString(cfa->display_operands[i], value, cur_pc); + } + } + log(indent, "%s", log_string.c_str()); + + // Get the raw bytes of the data. + uint64_t end_offset = memory_->cur_offset(); + memory_->set_cur_offset(cfa_offset); + std::string raw_data("Raw Data:"); + for (uint64_t i = 0; i < end_offset - cfa_offset; i++) { + uint8_t value; + if (!memory_->ReadBytes(&value, 1)) { + return false; + } + + // Only show 10 raw bytes per line. + if ((i % 10) == 0 && i != 0) { + log(indent, "%s", raw_data.c_str()); + raw_data.clear(); + } + if (raw_data.empty()) { + raw_data = "Raw Data:"; + } + raw_data += android::base::StringPrintf(" 0x%02x", value); + } + if (!raw_data.empty()) { + log(indent, "%s", raw_data.c_str()); + } + + // Log any of the expression data. + for (const auto line : expression_lines) { + log(indent + 1, "%s", line.c_str()); + } + return true; +} + +template +bool DwarfCfa::Log(uint32_t indent, uint64_t pc, uint64_t load_bias, + uint64_t start_offset, uint64_t end_offset) { + memory_->set_cur_offset(start_offset); + uint64_t cfa_offset; + uint64_t cur_pc = fde_->pc_start; + uint64_t old_pc = cur_pc; + while ((cfa_offset = memory_->cur_offset()) < end_offset && cur_pc <= pc) { + // Read the cfa information. + uint8_t cfa_value; + if (!memory_->ReadBytes(&cfa_value, 1)) { + return false; + } + + // Check the 2 high bits. + uint8_t cfa_low = cfa_value & 0x3f; + switch (cfa_value >> 6) { + case 0: + if (!LogInstruction(indent, cfa_offset, cfa_low, &cur_pc)) { + return false; + } + break; + case 1: + log(indent, "DW_CFA_advance_loc %d", cfa_low); + log(indent, "Raw Data: 0x%02x", cfa_value); + cur_pc += cfa_low * fde_->cie->code_alignment_factor; + break; + case 2: + if (!LogOffsetRegisterString(indent, cfa_offset, cfa_low)) { + return false; + } + break; + case 3: + log(indent, "DW_CFA_restore register(%d)", cfa_low); + log(indent, "Raw Data: 0x%02x", cfa_value); + break; + } + if (cur_pc != old_pc) { + log(indent, ""); + log(indent, "PC 0x%" PRIx64, cur_pc + load_bias); + } + old_pc = cur_pc; + } + return true; +} + +// Static data. +template +bool DwarfCfa::cfa_nop(dwarf_loc_regs_t*) { + return true; +} + +template +bool DwarfCfa::cfa_set_loc(dwarf_loc_regs_t*) { + AddressType cur_pc = cur_pc_; + AddressType new_pc = operands_[0]; + if (new_pc < cur_pc) { + if (std::is_same::value) { + log(0, "Warning: PC is moving backwards: old 0x%" PRIx32 " new 0x%" PRIx32, cur_pc, new_pc); + } else { + log(0, "Warning: PC is moving backwards: old 0x%" PRIx64 " new 0x%" PRIx64, cur_pc, new_pc); + } + } + cur_pc_ = new_pc; + return true; +} + +template +bool DwarfCfa::cfa_advance_loc(dwarf_loc_regs_t*) { + cur_pc_ += operands_[0] * fde_->cie->code_alignment_factor; + return true; +} + +template +bool DwarfCfa::cfa_offset(dwarf_loc_regs_t* loc_regs) { + AddressType reg = operands_[0]; + (*loc_regs)[reg] = {.type = DWARF_LOCATION_OFFSET, .values = {operands_[1]}}; + return true; +} + +template +bool DwarfCfa::cfa_restore(dwarf_loc_regs_t* loc_regs) { + AddressType reg = operands_[0]; + if (cie_loc_regs_ == nullptr) { + log(0, "restore while processing cie"); + last_error_ = DWARF_ERROR_ILLEGAL_STATE; + return false; + } + auto reg_entry = cie_loc_regs_->find(reg); + if (reg_entry == cie_loc_regs_->end()) { + loc_regs->erase(reg); + } else { + (*loc_regs)[reg] = reg_entry->second; + } + return true; +} + +template +bool DwarfCfa::cfa_undefined(dwarf_loc_regs_t* loc_regs) { + AddressType reg = operands_[0]; + (*loc_regs)[reg] = {.type = DWARF_LOCATION_UNDEFINED}; + return true; +} + +template +bool DwarfCfa::cfa_same_value(dwarf_loc_regs_t* loc_regs) { + AddressType reg = operands_[0]; + loc_regs->erase(reg); + return true; +} + +template +bool DwarfCfa::cfa_register(dwarf_loc_regs_t* loc_regs) { + AddressType reg = operands_[0]; + AddressType reg_dst = operands_[1]; + (*loc_regs)[reg] = {.type = DWARF_LOCATION_REGISTER, .values = {reg_dst}}; + return true; +} + +template +bool DwarfCfa::cfa_remember_state(dwarf_loc_regs_t* loc_regs) { + loc_reg_state_.push(*loc_regs); + return true; +} + +template +bool DwarfCfa::cfa_restore_state(dwarf_loc_regs_t* loc_regs) { + if (loc_reg_state_.size() == 0) { + log(0, "Warning: Attempt to restore without remember."); + return true; + } + *loc_regs = loc_reg_state_.top(); + loc_reg_state_.pop(); + return true; +} + +template +bool DwarfCfa::cfa_def_cfa(dwarf_loc_regs_t* loc_regs) { + (*loc_regs)[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {operands_[0], operands_[1]}}; + return true; +} + +template +bool DwarfCfa::cfa_def_cfa_register(dwarf_loc_regs_t* loc_regs) { + auto cfa_location = loc_regs->find(CFA_REG); + if (cfa_location == loc_regs->end() || cfa_location->second.type != DWARF_LOCATION_REGISTER) { + log(0, "Attempt to set new register, but cfa is not already set to a register."); + last_error_ = DWARF_ERROR_ILLEGAL_STATE; + return false; + } + + cfa_location->second.values[0] = operands_[0]; + return true; +} + +template +bool DwarfCfa::cfa_def_cfa_offset(dwarf_loc_regs_t* loc_regs) { + // Changing the offset if this is not a register is illegal. + auto cfa_location = loc_regs->find(CFA_REG); + if (cfa_location == loc_regs->end() || cfa_location->second.type != DWARF_LOCATION_REGISTER) { + log(0, "Attempt to set offset, but cfa is not set to a register."); + last_error_ = DWARF_ERROR_ILLEGAL_STATE; + return false; + } + cfa_location->second.values[1] = operands_[0]; + return true; +} + +template +bool DwarfCfa::cfa_def_cfa_expression(dwarf_loc_regs_t* loc_regs) { + (*loc_regs)[CFA_REG] = {.type = DWARF_LOCATION_EXPRESSION, + .values = {operands_[0], memory_->cur_offset()}}; + return true; +} + +template +bool DwarfCfa::cfa_expression(dwarf_loc_regs_t* loc_regs) { + AddressType reg = operands_[0]; + (*loc_regs)[reg] = {.type = DWARF_LOCATION_EXPRESSION, + .values = {operands_[1], memory_->cur_offset()}}; + return true; +} + +template +bool DwarfCfa::cfa_offset_extended_sf(dwarf_loc_regs_t* loc_regs) { + AddressType reg = operands_[0]; + SignedType value = static_cast(operands_[1]) * fde_->cie->data_alignment_factor; + (*loc_regs)[reg] = {.type = DWARF_LOCATION_OFFSET, .values = {static_cast(value)}}; + return true; +} + +template +bool DwarfCfa::cfa_def_cfa_sf(dwarf_loc_regs_t* loc_regs) { + SignedType offset = static_cast(operands_[1]) * fde_->cie->data_alignment_factor; + (*loc_regs)[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, + .values = {operands_[0], static_cast(offset)}}; + return true; +} + +template +bool DwarfCfa::cfa_def_cfa_offset_sf(dwarf_loc_regs_t* loc_regs) { + // Changing the offset if this is not a register is illegal. + auto cfa_location = loc_regs->find(CFA_REG); + if (cfa_location == loc_regs->end() || cfa_location->second.type != DWARF_LOCATION_REGISTER) { + log(0, "Attempt to set offset, but cfa is not set to a register."); + last_error_ = DWARF_ERROR_ILLEGAL_STATE; + return false; + } + SignedType offset = static_cast(operands_[0]) * fde_->cie->data_alignment_factor; + cfa_location->second.values[1] = static_cast(offset); + return true; +} + +template +bool DwarfCfa::cfa_val_offset(dwarf_loc_regs_t* loc_regs) { + AddressType reg = operands_[0]; + SignedType offset = static_cast(operands_[1]) * fde_->cie->data_alignment_factor; + (*loc_regs)[reg] = {.type = DWARF_LOCATION_VAL_OFFSET, .values = {static_cast(offset)}}; + return true; +} + +template +bool DwarfCfa::cfa_val_offset_sf(dwarf_loc_regs_t* loc_regs) { + AddressType reg = operands_[0]; + SignedType offset = static_cast(operands_[1]) * fde_->cie->data_alignment_factor; + (*loc_regs)[reg] = {.type = DWARF_LOCATION_VAL_OFFSET, .values = {static_cast(offset)}}; + return true; +} + +template +bool DwarfCfa::cfa_val_expression(dwarf_loc_regs_t* loc_regs) { + AddressType reg = operands_[0]; + (*loc_regs)[reg] = {.type = DWARF_LOCATION_VAL_EXPRESSION, + .values = {operands_[1], memory_->cur_offset()}}; + return true; +} + +template +bool DwarfCfa::cfa_gnu_negative_offset_extended(dwarf_loc_regs_t* loc_regs) { + AddressType reg = operands_[0]; + SignedType offset = -static_cast(operands_[1]); + (*loc_regs)[reg] = {.type = DWARF_LOCATION_OFFSET, .values = {static_cast(offset)}}; + return true; +} + +const DwarfCfaInfo::Info DwarfCfaInfo::kTable[64] = { + { + // 0x00 DW_CFA_nop + "DW_CFA_nop", + 2, + 0, + {}, + {}, + }, + { + "DW_CFA_set_loc", // 0x01 DW_CFA_set_loc + 2, + 1, + {DW_EH_PE_absptr}, + {DWARF_DISPLAY_SET_LOC}, + }, + { + "DW_CFA_advance_loc1", // 0x02 DW_CFA_advance_loc1 + 2, + 1, + {DW_EH_PE_udata1}, + {DWARF_DISPLAY_ADVANCE_LOC}, + }, + { + "DW_CFA_advance_loc2", // 0x03 DW_CFA_advance_loc2 + 2, + 1, + {DW_EH_PE_udata2}, + {DWARF_DISPLAY_ADVANCE_LOC}, + }, + { + "DW_CFA_advance_loc4", // 0x04 DW_CFA_advance_loc4 + 2, + 1, + {DW_EH_PE_udata4}, + {DWARF_DISPLAY_ADVANCE_LOC}, + }, + { + "DW_CFA_offset_extended", // 0x05 DW_CFA_offset_extended + 2, + 2, + {DW_EH_PE_uleb128, DW_EH_PE_uleb128}, + {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_NUMBER}, + }, + { + "DW_CFA_restore_extended", // 0x06 DW_CFA_restore_extended + 2, + 1, + {DW_EH_PE_uleb128}, + {DWARF_DISPLAY_REGISTER}, + }, + { + "DW_CFA_undefined", // 0x07 DW_CFA_undefined + 2, + 1, + {DW_EH_PE_uleb128}, + {DWARF_DISPLAY_REGISTER}, + }, + { + "DW_CFA_same_value", // 0x08 DW_CFA_same_value + 2, + 1, + {DW_EH_PE_uleb128}, + {DWARF_DISPLAY_REGISTER}, + }, + { + "DW_CFA_register", // 0x09 DW_CFA_register + 2, + 2, + {DW_EH_PE_uleb128, DW_EH_PE_uleb128}, + {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_REGISTER}, + }, + { + "DW_CFA_remember_state", // 0x0a DW_CFA_remember_state + 2, + 0, + {}, + {}, + }, + { + "DW_CFA_restore_state", // 0x0b DW_CFA_restore_state + 2, + 0, + {}, + {}, + }, + { + "DW_CFA_def_cfa", // 0x0c DW_CFA_def_cfa + 2, + 2, + {DW_EH_PE_uleb128, DW_EH_PE_uleb128}, + {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_NUMBER}, + }, + { + "DW_CFA_def_cfa_register", // 0x0d DW_CFA_def_cfa_register + 2, + 1, + {DW_EH_PE_uleb128}, + {DWARF_DISPLAY_REGISTER}, + }, + { + "DW_CFA_def_cfa_offset", // 0x0e DW_CFA_def_cfa_offset + 2, + 1, + {DW_EH_PE_uleb128}, + {DWARF_DISPLAY_NUMBER}, + }, + { + "DW_CFA_def_cfa_expression", // 0x0f DW_CFA_def_cfa_expression + 2, + 1, + {DW_EH_PE_block}, + {DWARF_DISPLAY_EVAL_BLOCK}, + }, + { + "DW_CFA_expression", // 0x10 DW_CFA_expression + 2, + 2, + {DW_EH_PE_uleb128, DW_EH_PE_block}, + {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_EVAL_BLOCK}, + }, + { + "DW_CFA_offset_extended_sf", // 0x11 DW_CFA_offset_extend_sf + 2, + 2, + {DW_EH_PE_uleb128, DW_EH_PE_sleb128}, + {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_SIGNED_NUMBER}, + }, + { + "DW_CFA_def_cfa_sf", // 0x12 DW_CFA_def_cfa_sf + 2, + 2, + {DW_EH_PE_uleb128, DW_EH_PE_sleb128}, + {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_SIGNED_NUMBER}, + }, + { + "DW_CFA_def_cfa_offset_sf", // 0x13 DW_CFA_def_cfa_offset_sf + 2, + 1, + {DW_EH_PE_sleb128}, + {DWARF_DISPLAY_SIGNED_NUMBER}, + }, + { + "DW_CFA_val_offset", // 0x14 DW_CFA_val_offset + 2, + 2, + {DW_EH_PE_uleb128, DW_EH_PE_uleb128}, + {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_NUMBER}, + }, + { + "DW_CFA_val_offset_sf", // 0x15 DW_CFA_val_offset_sf + 2, + 2, + {DW_EH_PE_uleb128, DW_EH_PE_sleb128}, + {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_SIGNED_NUMBER}, + }, + { + "DW_CFA_val_expression", // 0x16 DW_CFA_val_expression + 2, + 2, + {DW_EH_PE_uleb128, DW_EH_PE_block}, + {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_EVAL_BLOCK}, + }, + {nullptr, 0, 0, {}, {}}, // 0x17 illegal cfa + {nullptr, 0, 0, {}, {}}, // 0x18 illegal cfa + {nullptr, 0, 0, {}, {}}, // 0x19 illegal cfa + {nullptr, 0, 0, {}, {}}, // 0x1a illegal cfa + {nullptr, 0, 0, {}, {}}, // 0x1b illegal cfa + {nullptr, 0, 0, {}, {}}, // 0x1c DW_CFA_lo_user (Treat as illegal) + {nullptr, 0, 0, {}, {}}, // 0x1d illegal cfa + {nullptr, 0, 0, {}, {}}, // 0x1e illegal cfa + {nullptr, 0, 0, {}, {}}, // 0x1f illegal cfa + {nullptr, 0, 0, {}, {}}, // 0x20 illegal cfa + {nullptr, 0, 0, {}, {}}, // 0x21 illegal cfa + {nullptr, 0, 0, {}, {}}, // 0x22 illegal cfa + {nullptr, 0, 0, {}, {}}, // 0x23 illegal cfa + {nullptr, 0, 0, {}, {}}, // 0x24 illegal cfa + {nullptr, 0, 0, {}, {}}, // 0x25 illegal cfa + {nullptr, 0, 0, {}, {}}, // 0x26 illegal cfa + {nullptr, 0, 0, {}, {}}, // 0x27 illegal cfa + {nullptr, 0, 0, {}, {}}, // 0x28 illegal cfa + {nullptr, 0, 0, {}, {}}, // 0x29 illegal cfa + {nullptr, 0, 0, {}, {}}, // 0x2a illegal cfa + {nullptr, 0, 0, {}, {}}, // 0x2b illegal cfa + {nullptr, 0, 0, {}, {}}, // 0x2c illegal cfa + {nullptr, 0, 0, {}, {}}, // 0x2d DW_CFA_GNU_window_save (Treat as illegal) + { + "DW_CFA_GNU_args_size", // 0x2e DW_CFA_GNU_args_size + 2, + 1, + {DW_EH_PE_uleb128}, + {DWARF_DISPLAY_NUMBER}, + }, + { + "DW_CFA_GNU_negative_offset_extended", // 0x2f DW_CFA_GNU_negative_offset_extended + 2, + 2, + {DW_EH_PE_uleb128, DW_EH_PE_uleb128}, + {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_NUMBER}, + }, + {nullptr, 0, 0, {}, {}}, // 0x31 illegal cfa + {nullptr, 0, 0, {}, {}}, // 0x32 illegal cfa + {nullptr, 0, 0, {}, {}}, // 0x33 illegal cfa + {nullptr, 0, 0, {}, {}}, // 0x34 illegal cfa + {nullptr, 0, 0, {}, {}}, // 0x35 illegal cfa + {nullptr, 0, 0, {}, {}}, // 0x36 illegal cfa + {nullptr, 0, 0, {}, {}}, // 0x37 illegal cfa + {nullptr, 0, 0, {}, {}}, // 0x38 illegal cfa + {nullptr, 0, 0, {}, {}}, // 0x39 illegal cfa + {nullptr, 0, 0, {}, {}}, // 0x3a illegal cfa + {nullptr, 0, 0, {}, {}}, // 0x3b illegal cfa + {nullptr, 0, 0, {}, {}}, // 0x3c illegal cfa + {nullptr, 0, 0, {}, {}}, // 0x3d illegal cfa + {nullptr, 0, 0, {}, {}}, // 0x3e illegal cfa + {nullptr, 0, 0, {}, {}}, // 0x3f DW_CFA_hi_user (Treat as illegal) +}; + +// Explicitly instantiate DwarfCfa. +template class DwarfCfa; +template class DwarfCfa; diff --git a/libunwindstack/DwarfCfa.h b/libunwindstack/DwarfCfa.h new file mode 100644 index 000000000..ce7da4a12 --- /dev/null +++ b/libunwindstack/DwarfCfa.h @@ -0,0 +1,255 @@ +/* + * 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_CFA_H +#define _LIBUNWINDSTACK_DWARF_CFA_H + +#include + +#include +#include +#include +#include + +#include "DwarfError.h" +#include "DwarfLocation.h" +#include "DwarfMemory.h" +#include "DwarfStructs.h" + +// DWARF Standard home: http://dwarfstd.org/ +// This code is based on DWARF 4: http://http://dwarfstd.org/doc/DWARF4.pdf +// See section 6.4.2.1 for a description of the DW_CFA_xxx values. + +class DwarfCfaInfo { + public: + enum DisplayType : uint8_t { + DWARF_DISPLAY_NONE = 0, + DWARF_DISPLAY_REGISTER, + DWARF_DISPLAY_NUMBER, + DWARF_DISPLAY_SIGNED_NUMBER, + DWARF_DISPLAY_EVAL_BLOCK, + DWARF_DISPLAY_ADDRESS, + DWARF_DISPLAY_SET_LOC, + DWARF_DISPLAY_ADVANCE_LOC, + }; + + struct Info { + const char* name; + uint8_t supported_version; + uint8_t num_operands; + uint8_t operands[2]; + uint8_t display_operands[2]; + }; + + const static Info kTable[64]; +}; + +template +class DwarfCfa { + // Signed version of AddressType + typedef typename std::make_signed::type SignedType; + + public: + DwarfCfa(DwarfMemory* memory, const DwarfFDE* fde) : memory_(memory), fde_(fde) {} + virtual ~DwarfCfa() = default; + + bool GetLocationInfo(uint64_t pc, uint64_t start_offset, uint64_t end_offset, + dwarf_loc_regs_t* loc_regs); + + bool Log(uint32_t indent, uint64_t pc, uint64_t load_bias, uint64_t start_offset, + uint64_t end_offset); + + DwarfError last_error() { return last_error_; } + + AddressType cur_pc() { return cur_pc_; } + + void set_cie_loc_regs(const dwarf_loc_regs_t* cie_loc_regs) { cie_loc_regs_ = cie_loc_regs; } + + protected: + std::string GetOperandString(uint8_t operand, uint64_t value, uint64_t* cur_pc); + + bool LogOffsetRegisterString(uint32_t indent, uint64_t cfa_offset, uint8_t reg); + + bool LogInstruction(uint32_t indent, uint64_t cfa_offset, uint8_t op, uint64_t* cur_pc); + + private: + DwarfError last_error_; + DwarfMemory* memory_; + const DwarfFDE* fde_; + + AddressType cur_pc_; + const dwarf_loc_regs_t* cie_loc_regs_ = nullptr; + std::vector operands_; + std::stack loc_reg_state_; + + // CFA processing functions. + bool cfa_nop(dwarf_loc_regs_t*); + bool cfa_set_loc(dwarf_loc_regs_t*); + bool cfa_advance_loc(dwarf_loc_regs_t*); + bool cfa_offset(dwarf_loc_regs_t*); + bool cfa_restore(dwarf_loc_regs_t*); + bool cfa_undefined(dwarf_loc_regs_t*); + bool cfa_same_value(dwarf_loc_regs_t*); + bool cfa_register(dwarf_loc_regs_t*); + bool cfa_remember_state(dwarf_loc_regs_t*); + bool cfa_restore_state(dwarf_loc_regs_t*); + bool cfa_def_cfa(dwarf_loc_regs_t*); + bool cfa_def_cfa_register(dwarf_loc_regs_t*); + bool cfa_def_cfa_offset(dwarf_loc_regs_t*); + bool cfa_def_cfa_expression(dwarf_loc_regs_t*); + bool cfa_expression(dwarf_loc_regs_t*); + bool cfa_offset_extended_sf(dwarf_loc_regs_t*); + bool cfa_def_cfa_sf(dwarf_loc_regs_t*); + bool cfa_def_cfa_offset_sf(dwarf_loc_regs_t*); + bool cfa_val_offset(dwarf_loc_regs_t*); + bool cfa_val_offset_sf(dwarf_loc_regs_t*); + bool cfa_val_expression(dwarf_loc_regs_t*); + bool cfa_gnu_negative_offset_extended(dwarf_loc_regs_t*); + + using process_func = bool (DwarfCfa::*)(dwarf_loc_regs_t*); + constexpr static process_func kCallbackTable[64] = { + // 0x00 DW_CFA_nop + &DwarfCfa::cfa_nop, + // 0x01 DW_CFA_set_loc + &DwarfCfa::cfa_set_loc, + // 0x02 DW_CFA_advance_loc1 + &DwarfCfa::cfa_advance_loc, + // 0x03 DW_CFA_advance_loc2 + &DwarfCfa::cfa_advance_loc, + // 0x04 DW_CFA_advance_loc4 + &DwarfCfa::cfa_advance_loc, + // 0x05 DW_CFA_offset_extended + &DwarfCfa::cfa_offset, + // 0x06 DW_CFA_restore_extended + &DwarfCfa::cfa_restore, + // 0x07 DW_CFA_undefined + &DwarfCfa::cfa_undefined, + // 0x08 DW_CFA_same_value + &DwarfCfa::cfa_same_value, + // 0x09 DW_CFA_register + &DwarfCfa::cfa_register, + // 0x0a DW_CFA_remember_state + &DwarfCfa::cfa_remember_state, + // 0x0b DW_CFA_restore_state + &DwarfCfa::cfa_restore_state, + // 0x0c DW_CFA_def_cfa + &DwarfCfa::cfa_def_cfa, + // 0x0d DW_CFA_def_cfa_register + &DwarfCfa::cfa_def_cfa_register, + // 0x0e DW_CFA_def_cfa_offset + &DwarfCfa::cfa_def_cfa_offset, + // 0x0f DW_CFA_def_cfa_expression + &DwarfCfa::cfa_def_cfa_expression, + // 0x10 DW_CFA_expression + &DwarfCfa::cfa_expression, + // 0x11 DW_CFA_offset_extended_sf + &DwarfCfa::cfa_offset_extended_sf, + // 0x12 DW_CFA_def_cfa_sf + &DwarfCfa::cfa_def_cfa_sf, + // 0x13 DW_CFA_def_cfa_offset_sf + &DwarfCfa::cfa_def_cfa_offset_sf, + // 0x14 DW_CFA_val_offset + &DwarfCfa::cfa_val_offset, + // 0x15 DW_CFA_val_offset_sf + &DwarfCfa::cfa_val_offset_sf, + // 0x16 DW_CFA_val_expression + &DwarfCfa::cfa_val_expression, + // 0x17 illegal cfa + nullptr, + // 0x18 illegal cfa + nullptr, + // 0x19 illegal cfa + nullptr, + // 0x1a illegal cfa + nullptr, + // 0x1b illegal cfa + nullptr, + // 0x1c DW_CFA_lo_user (Treat this as illegal) + nullptr, + // 0x1d illegal cfa + nullptr, + // 0x1e illegal cfa + nullptr, + // 0x1f illegal cfa + nullptr, + // 0x20 illegal cfa + nullptr, + // 0x21 illegal cfa + nullptr, + // 0x22 illegal cfa + nullptr, + // 0x23 illegal cfa + nullptr, + // 0x24 illegal cfa + nullptr, + // 0x25 illegal cfa + nullptr, + // 0x26 illegal cfa + nullptr, + // 0x27 illegal cfa + nullptr, + // 0x28 illegal cfa + nullptr, + // 0x29 illegal cfa + nullptr, + // 0x2a illegal cfa + nullptr, + // 0x2b illegal cfa + nullptr, + // 0x2c illegal cfa + nullptr, + // 0x2d DW_CFA_GNU_window_save (Treat this as illegal) + nullptr, + // 0x2e DW_CFA_GNU_args_size + &DwarfCfa::cfa_nop, + // 0x2f DW_CFA_GNU_negative_offset_extended + &DwarfCfa::cfa_gnu_negative_offset_extended, + // 0x30 illegal cfa + nullptr, + // 0x31 illegal cfa + nullptr, + // 0x32 illegal cfa + nullptr, + // 0x33 illegal cfa + nullptr, + // 0x34 illegal cfa + nullptr, + // 0x35 illegal cfa + nullptr, + // 0x36 illegal cfa + nullptr, + // 0x37 illegal cfa + nullptr, + // 0x38 illegal cfa + nullptr, + // 0x39 illegal cfa + nullptr, + // 0x3a illegal cfa + nullptr, + // 0x3b illegal cfa + nullptr, + // 0x3c illegal cfa + nullptr, + // 0x3d illegal cfa + nullptr, + // 0x3e illegal cfa + nullptr, + // 0x3f DW_CFA_hi_user (Treat this as illegal) + nullptr, + }; +}; + +#endif // _LIBUNWINDSTACK_DWARF_CFA_H diff --git a/libunwindstack/DwarfLocation.h b/libunwindstack/DwarfLocation.h new file mode 100644 index 000000000..062d1256e --- /dev/null +++ b/libunwindstack/DwarfLocation.h @@ -0,0 +1,41 @@ +/* + * 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_LOCATION_H +#define _LIBUNWINDSTACK_DWARF_LOCATION_H + +#include + +#include + +enum DwarfLocationEnum : uint8_t { + DWARF_LOCATION_INVALID = 0, + DWARF_LOCATION_UNDEFINED, + DWARF_LOCATION_OFFSET, + DWARF_LOCATION_VAL_OFFSET, + DWARF_LOCATION_REGISTER, + DWARF_LOCATION_EXPRESSION, + DWARF_LOCATION_VAL_EXPRESSION, +}; + +struct DwarfLocation { + DwarfLocationEnum type; + uint64_t values[2]; +}; + +typedef std::unordered_map dwarf_loc_regs_t; + +#endif // _LIBUNWINDSTACK_DWARF_LOCATION_H diff --git a/libunwindstack/DwarfStructs.h b/libunwindstack/DwarfStructs.h new file mode 100644 index 000000000..57aac88b2 --- /dev/null +++ b/libunwindstack/DwarfStructs.h @@ -0,0 +1,52 @@ +/* + * 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_STRUCTS_H +#define _LIBUNWINDSTACK_DWARF_STRUCTS_H + +#include + +#include + +#include "DwarfEncoding.h" + +struct DwarfCIE { + uint8_t version = 0; + uint8_t fde_address_encoding = DW_EH_PE_absptr; + uint8_t lsda_encoding = DW_EH_PE_omit; + uint8_t segment_size = 0; + std::vector augmentation_string; + uint64_t personality_handler = 0; + uint64_t cfa_instructions_offset = 0; + uint64_t cfa_instructions_end = 0; + uint64_t code_alignment_factor = 0; + int64_t data_alignment_factor = 0; + uint64_t return_address_register = 0; +}; + +struct DwarfFDE { + uint64_t cie_offset = 0; + uint64_t cfa_instructions_offset = 0; + uint64_t cfa_instructions_end = 0; + uint64_t pc_start = 0; + uint64_t pc_end = 0; + uint64_t lsda_address = 0; + const DwarfCIE* cie = nullptr; +}; + +constexpr uint16_t CFA_REG = static_cast(-1); + +#endif // _LIBUNWINDSTACK_DWARF_STRUCTS_H diff --git a/libunwindstack/tests/DwarfCfaLogTest.cpp b/libunwindstack/tests/DwarfCfaLogTest.cpp new file mode 100644 index 000000000..3185bc3bd --- /dev/null +++ b/libunwindstack/tests/DwarfCfaLogTest.cpp @@ -0,0 +1,814 @@ +/* + * 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 + +#include +#include +#include + +#include +#include + +#include "DwarfCfa.h" +#include "DwarfLocation.h" +#include "DwarfMemory.h" +#include "DwarfStructs.h" +#include "Log.h" + +#include "LogFake.h" +#include "MemoryFake.h" + +template +class DwarfCfaLogTest : public ::testing::Test { + protected: + void SetUp() override { + ResetLogs(); + memory_.Clear(); + + dmem_.reset(new DwarfMemory(&memory_)); + + cie_.cfa_instructions_offset = 0x1000; + cie_.cfa_instructions_end = 0x1030; + // These two values should be different to distinguish between + // operations that deal with code versus data. + cie_.code_alignment_factor = 4; + cie_.data_alignment_factor = 8; + + fde_.cfa_instructions_offset = 0x2000; + fde_.cfa_instructions_end = 0x2030; + fde_.pc_start = 0x2000; + fde_.pc_end = 0x2000; + fde_.pc_end = 0x10000; + fde_.cie = &cie_; + cfa_.reset(new DwarfCfa(dmem_.get(), &fde_)); + } + + MemoryFake memory_; + std::unique_ptr dmem_; + std::unique_ptr> cfa_; + DwarfCIE cie_; + DwarfFDE fde_; +}; +TYPED_TEST_CASE_P(DwarfCfaLogTest); + +// NOTE: All class variable references have to be prefaced with this->. + +TYPED_TEST_P(DwarfCfaLogTest, cfa_illegal) { + for (uint8_t i = 0x17; i < 0x3f; i++) { + if (i == 0x2e || i == 0x2f) { + // Skip gnu extension ops. + continue; + } + this->memory_.SetMemory(0x2000, std::vector{i}); + + ResetLogs(); + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2001)); + std::string expected = "4 unwind Illegal\n"; + expected += android::base::StringPrintf("4 unwind Raw Data: 0x%02x\n", i); + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + } +} + +TYPED_TEST_P(DwarfCfaLogTest, cfa_nop) { + this->memory_.SetMemory(0x2000, std::vector{0x00}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2001)); + std::string expected = + "4 unwind DW_CFA_nop\n" + "4 unwind Raw Data: 0x00\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaLogTest, cfa_offset) { + this->memory_.SetMemory(0x2000, std::vector{0x83, 0x04}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2002)); + std::string expected = + "4 unwind DW_CFA_offset register(3) 4\n" + "4 unwind Raw Data: 0x83 0x04\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + this->memory_.SetMemory(0x2100, std::vector{0x83, 0x84, 0x01}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2100, 0x2103)); + expected = + "4 unwind DW_CFA_offset register(3) 132\n" + "4 unwind Raw Data: 0x83 0x84 0x01\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaLogTest, cfa_offset_extended) { + this->memory_.SetMemory(0x500, std::vector{0x05, 0x03, 0x02}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x500, 0x503)); + std::string expected = + "4 unwind DW_CFA_offset_extended register(3) 2\n" + "4 unwind Raw Data: 0x05 0x03 0x02\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + this->memory_.SetMemory(0x1500, std::vector{0x05, 0x81, 0x01, 0x82, 0x12}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x1500, 0x1505)); + expected = + "4 unwind DW_CFA_offset_extended register(129) 2306\n" + "4 unwind Raw Data: 0x05 0x81 0x01 0x82 0x12\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaLogTest, cfa_offset_extended_sf) { + this->memory_.SetMemory(0x500, std::vector{0x11, 0x05, 0x10}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x500, 0x503)); + std::string expected = + "4 unwind DW_CFA_offset_extended_sf register(5) 16\n" + "4 unwind Raw Data: 0x11 0x05 0x10\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + // Check a negative value for the offset. + ResetLogs(); + this->memory_.SetMemory(0x1500, std::vector{0x11, 0x86, 0x01, 0xff, 0x7f}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x1500, 0x1505)); + expected = + "4 unwind DW_CFA_offset_extended_sf register(134) -1\n" + "4 unwind Raw Data: 0x11 0x86 0x01 0xff 0x7f\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaLogTest, cfa_restore) { + this->memory_.SetMemory(0x2000, std::vector{0xc2}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2001)); + std::string expected = + "4 unwind DW_CFA_restore register(2)\n" + "4 unwind Raw Data: 0xc2\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + this->memory_.SetMemory(0x3000, std::vector{0x82, 0x04, 0xc2}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x3000, 0x3003)); + expected = + "4 unwind DW_CFA_offset register(2) 4\n" + "4 unwind Raw Data: 0x82 0x04\n" + "4 unwind DW_CFA_restore register(2)\n" + "4 unwind Raw Data: 0xc2\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaLogTest, cfa_restore_extended) { + this->memory_.SetMemory(0x4000, std::vector{0x06, 0x08}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x4000, 0x4002)); + std::string expected = + "4 unwind DW_CFA_restore_extended register(8)\n" + "4 unwind Raw Data: 0x06 0x08\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + this->memory_.SetMemory(0x5000, std::vector{0x05, 0x82, 0x02, 0x04, 0x06, 0x82, 0x02}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x5000, 0x5007)); + expected = + "4 unwind DW_CFA_offset_extended register(258) 4\n" + "4 unwind Raw Data: 0x05 0x82 0x02 0x04\n" + "4 unwind DW_CFA_restore_extended register(258)\n" + "4 unwind Raw Data: 0x06 0x82 0x02\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaLogTest, cfa_set_loc) { + uint8_t buffer[1 + sizeof(TypeParam)]; + buffer[0] = 0x1; + TypeParam address; + std::string raw_data("Raw Data: 0x01 "); + std::string address_str; + if (std::is_same::value) { + address = 0x81234578U; + address_str = "0x81234578"; + raw_data += "0x78 0x45 0x23 0x81"; + } else { + address = 0x8123456712345678ULL; + address_str = "0x8123456712345678"; + raw_data += "0x78 0x56 0x34 0x12 0x67 0x45 0x23 0x81"; + } + memcpy(&buffer[1], &address, sizeof(address)); + + this->memory_.SetMemory(0x50, buffer, sizeof(buffer)); + ResetLogs(); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x50, 0x51 + sizeof(TypeParam))); + std::string expected = "4 unwind DW_CFA_set_loc " + address_str + "\n"; + expected += "4 unwind " + raw_data + "\n"; + expected += "4 unwind \n"; + expected += "4 unwind PC " + address_str + "\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + // Check for a set going back. + ResetLogs(); + this->fde_.pc_start = address + 0x10; + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x50, 0x51 + sizeof(TypeParam))); + expected = "4 unwind DW_CFA_set_loc " + address_str + "\n"; + expected += "4 unwind " + raw_data + "\n"; + expected += "4 unwind \n"; + expected += "4 unwind PC " + address_str + "\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc) { + this->memory_.SetMemory(0x200, std::vector{0x44}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x201)); + std::string expected = + "4 unwind DW_CFA_advance_loc 4\n" + "4 unwind Raw Data: 0x44\n" + "4 unwind \n" + "4 unwind PC 0x2010\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x200, 0x201)); + expected = + "4 unwind DW_CFA_advance_loc 4\n" + "4 unwind Raw Data: 0x44\n" + "4 unwind \n" + "4 unwind PC 0x2110\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc1) { + this->memory_.SetMemory(0x200, std::vector{0x02, 0x04}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x202)); + std::string expected = + "4 unwind DW_CFA_advance_loc1 4\n" + "4 unwind Raw Data: 0x02 0x04\n" + "4 unwind \n" + "4 unwind PC 0x2004\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x10, 0x200, 0x202)); + expected = + "4 unwind DW_CFA_advance_loc1 4\n" + "4 unwind Raw Data: 0x02 0x04\n" + "4 unwind \n" + "4 unwind PC 0x2014\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc2) { + this->memory_.SetMemory(0x600, std::vector{0x03, 0x04, 0x03}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x600, 0x603)); + std::string expected = + "4 unwind DW_CFA_advance_loc2 772\n" + "4 unwind Raw Data: 0x03 0x04 0x03\n" + "4 unwind \n" + "4 unwind PC 0x2304\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x1000, 0x600, 0x603)); + expected = + "4 unwind DW_CFA_advance_loc2 772\n" + "4 unwind Raw Data: 0x03 0x04 0x03\n" + "4 unwind \n" + "4 unwind PC 0x3304\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc4) { + this->memory_.SetMemory(0x500, std::vector{0x04, 0x04, 0x03, 0x02, 0x01}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x500, 0x505)); + std::string expected = + "4 unwind DW_CFA_advance_loc4 16909060\n" + "4 unwind Raw Data: 0x04 0x04 0x03 0x02 0x01\n" + "4 unwind \n" + "4 unwind PC 0x1022304\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x500, 0x505)); + expected = + "4 unwind DW_CFA_advance_loc4 16909060\n" + "4 unwind Raw Data: 0x04 0x04 0x03 0x02 0x01\n" + "4 unwind \n" + "4 unwind PC 0x1024304\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaLogTest, cfa_undefined) { + this->memory_.SetMemory(0xa00, std::vector{0x07, 0x09}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0xa00, 0xa02)); + std::string expected = + "4 unwind DW_CFA_undefined register(9)\n" + "4 unwind Raw Data: 0x07 0x09\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + dwarf_loc_regs_t cie_loc_regs; + this->memory_.SetMemory(0x1a00, std::vector{0x07, 0x81, 0x01}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x1a00, 0x1a03)); + expected = + "4 unwind DW_CFA_undefined register(129)\n" + "4 unwind Raw Data: 0x07 0x81 0x01\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaLogTest, cfa_same) { + this->memory_.SetMemory(0x100, std::vector{0x08, 0x7f}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102)); + std::string expected = + "4 unwind DW_CFA_same_value register(127)\n" + "4 unwind Raw Data: 0x08 0x7f\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + this->memory_.SetMemory(0x2100, std::vector{0x08, 0xff, 0x01}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2100, 0x2103)); + expected = + "4 unwind DW_CFA_same_value register(255)\n" + "4 unwind Raw Data: 0x08 0xff 0x01\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaLogTest, cfa_register) { + this->memory_.SetMemory(0x300, std::vector{0x09, 0x02, 0x01}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x300, 0x303)); + std::string expected = + "4 unwind DW_CFA_register register(2) register(1)\n" + "4 unwind Raw Data: 0x09 0x02 0x01\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + this->memory_.SetMemory(0x4300, std::vector{0x09, 0xff, 0x01, 0xff, 0x03}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x4300, 0x4305)); + expected = + "4 unwind DW_CFA_register register(255) register(511)\n" + "4 unwind Raw Data: 0x09 0xff 0x01 0xff 0x03\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaLogTest, cfa_state) { + this->memory_.SetMemory(0x300, std::vector{0x0a}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x300, 0x301)); + + std::string expected = + "4 unwind DW_CFA_remember_state\n" + "4 unwind Raw Data: 0x0a\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + this->memory_.SetMemory(0x4300, std::vector{0x0b}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x4300, 0x4301)); + + expected = + "4 unwind DW_CFA_restore_state\n" + "4 unwind Raw Data: 0x0b\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaLogTest, cfa_state_cfa_offset_restore) { + this->memory_.SetMemory(0x3000, std::vector{0x0a, 0x0e, 0x40, 0x0b}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x3000, 0x3004)); + + std::string expected = + "4 unwind DW_CFA_remember_state\n" + "4 unwind Raw Data: 0x0a\n" + "4 unwind DW_CFA_def_cfa_offset 64\n" + "4 unwind Raw Data: 0x0e 0x40\n" + "4 unwind DW_CFA_restore_state\n" + "4 unwind Raw Data: 0x0b\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa) { + this->memory_.SetMemory(0x100, std::vector{0x0c, 0x7f, 0x74}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x103)); + + std::string expected = + "4 unwind DW_CFA_def_cfa register(127) 116\n" + "4 unwind Raw Data: 0x0c 0x7f 0x74\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + this->memory_.SetMemory(0x200, std::vector{0x0c, 0xff, 0x02, 0xf4, 0x04}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x205)); + + expected = + "4 unwind DW_CFA_def_cfa register(383) 628\n" + "4 unwind Raw Data: 0x0c 0xff 0x02 0xf4 0x04\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_sf) { + this->memory_.SetMemory(0x100, std::vector{0x12, 0x30, 0x25}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x103)); + + std::string expected = + "4 unwind DW_CFA_def_cfa_sf register(48) 37\n" + "4 unwind Raw Data: 0x12 0x30 0x25\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + // Test a negative value. + ResetLogs(); + this->memory_.SetMemory(0x200, std::vector{0x12, 0xa3, 0x01, 0xfa, 0x7f}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x205)); + + expected = + "4 unwind DW_CFA_def_cfa_sf register(163) -6\n" + "4 unwind Raw Data: 0x12 0xa3 0x01 0xfa 0x7f\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_register) { + this->memory_.SetMemory(0x100, std::vector{0x0d, 0x72}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102)); + + std::string expected = + "4 unwind DW_CFA_def_cfa_register register(114)\n" + "4 unwind Raw Data: 0x0d 0x72\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + this->memory_.SetMemory(0x200, std::vector{0x0d, 0xf9, 0x20}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x203)); + + expected = + "4 unwind DW_CFA_def_cfa_register register(4217)\n" + "4 unwind Raw Data: 0x0d 0xf9 0x20\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_offset) { + this->memory_.SetMemory(0x100, std::vector{0x0e, 0x59}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102)); + + std::string expected = + "4 unwind DW_CFA_def_cfa_offset 89\n" + "4 unwind Raw Data: 0x0e 0x59\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102)); + + expected = + "4 unwind DW_CFA_def_cfa_offset 89\n" + "4 unwind Raw Data: 0x0e 0x59\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + this->memory_.SetMemory(0x200, std::vector{0x0e, 0xd4, 0x0a}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x203)); + + expected = + "4 unwind DW_CFA_def_cfa_offset 1364\n" + "4 unwind Raw Data: 0x0e 0xd4 0x0a\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_offset_sf) { + this->memory_.SetMemory(0x100, std::vector{0x13, 0x23}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102)); + + std::string expected = + "4 unwind DW_CFA_def_cfa_offset_sf 35\n" + "4 unwind Raw Data: 0x13 0x23\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102)); + + expected = + "4 unwind DW_CFA_def_cfa_offset_sf 35\n" + "4 unwind Raw Data: 0x13 0x23\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + // Negative offset. + ResetLogs(); + this->memory_.SetMemory(0x200, std::vector{0x13, 0xf6, 0x7f}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x203)); + + expected = + "4 unwind DW_CFA_def_cfa_offset_sf -10\n" + "4 unwind Raw Data: 0x13 0xf6 0x7f\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_expression) { + this->memory_.SetMemory(0x100, std::vector{0x0f, 0x04, 0x01, 0x02, 0x04, 0x05}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x106)); + + std::string expected = + "4 unwind DW_CFA_def_cfa_expression 4\n" + "4 unwind Raw Data: 0x0f 0x04 0x01 0x02 0x04 0x05\n" + "4 unwind Illegal\n" + "4 unwind Raw Data: 0x01\n" + "4 unwind Illegal\n" + "4 unwind Raw Data: 0x02\n" + "4 unwind Illegal\n" + "4 unwind Raw Data: 0x04\n" + "4 unwind Illegal\n" + "4 unwind Raw Data: 0x05\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + std::vector ops{0x0f, 0x81, 0x01}; + expected = "4 unwind Raw Data: 0x0f 0x81 0x01"; + std::string op_string; + for (uint8_t i = 3; i < 132; i++) { + ops.push_back(0x05); + op_string += + "4 unwind Illegal\n" + "4 unwind Raw Data: 0x05\n"; + expected += " 0x05"; + if (((i + 1) % 10) == 0) { + expected += "\n4 unwind Raw Data:"; + } + } + expected += '\n'; + this->memory_.SetMemory(0x200, ops); + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x284)); + + expected = "4 unwind DW_CFA_def_cfa_expression 129\n" + expected; + ASSERT_EQ(expected + op_string, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaLogTest, cfa_expression) { + this->memory_.SetMemory(0x100, std::vector{0x10, 0x04, 0x02, 0xc0, 0xc1}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x105)); + + std::string expected = + "4 unwind DW_CFA_expression register(4) 2\n" + "4 unwind Raw Data: 0x10 0x04 0x02 0xc0 0xc1\n" + "4 unwind Illegal\n" + "4 unwind Raw Data: 0xc0\n" + "4 unwind Illegal\n" + "4 unwind Raw Data: 0xc1\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + std::vector ops{0x10, 0xff, 0x01, 0x82, 0x01}; + expected = "4 unwind Raw Data: 0x10 0xff 0x01 0x82 0x01"; + std::string op_string; + for (uint8_t i = 5; i < 135; i++) { + ops.push_back(0xa0 + (i - 5) % 96); + op_string += "4 unwind Illegal\n"; + op_string += android::base::StringPrintf("4 unwind Raw Data: 0x%02x\n", ops.back()); + expected += android::base::StringPrintf(" 0x%02x", ops.back()); + if (((i + 1) % 10) == 0) { + expected += "\n4 unwind Raw Data:"; + } + } + expected = "4 unwind DW_CFA_expression register(255) 130\n" + expected + "\n"; + + this->memory_.SetMemory(0x200, ops); + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x287)); + + ASSERT_EQ(expected + op_string, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaLogTest, cfa_val_offset) { + this->memory_.SetMemory(0x100, std::vector{0x14, 0x45, 0x54}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x103)); + + std::string expected = + "4 unwind DW_CFA_val_offset register(69) 84\n" + "4 unwind Raw Data: 0x14 0x45 0x54\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + this->memory_.SetMemory(0x400, std::vector{0x14, 0xa2, 0x02, 0xb4, 0x05}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x400, 0x405)); + + expected = + "4 unwind DW_CFA_val_offset register(290) 692\n" + "4 unwind Raw Data: 0x14 0xa2 0x02 0xb4 0x05\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaLogTest, cfa_val_offset_sf) { + this->memory_.SetMemory(0x100, std::vector{0x15, 0x56, 0x12}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x103)); + + std::string expected = + "4 unwind DW_CFA_val_offset_sf register(86) 18\n" + "4 unwind Raw Data: 0x15 0x56 0x12\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + // Negative value. + ResetLogs(); + this->memory_.SetMemory(0xa00, std::vector{0x15, 0xff, 0x01, 0xc0, 0x7f}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0xa00, 0xa05)); + + expected = + "4 unwind DW_CFA_val_offset_sf register(255) -64\n" + "4 unwind Raw Data: 0x15 0xff 0x01 0xc0 0x7f\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaLogTest, cfa_val_expression) { + this->memory_.SetMemory(0x100, std::vector{0x16, 0x05, 0x02, 0xb0, 0xb1}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x105)); + + std::string expected = + "4 unwind DW_CFA_val_expression register(5) 2\n" + "4 unwind Raw Data: 0x16 0x05 0x02 0xb0 0xb1\n" + "4 unwind Illegal\n" + "4 unwind Raw Data: 0xb0\n" + "4 unwind Illegal\n" + "4 unwind Raw Data: 0xb1\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + std::vector ops{0x16, 0x83, 0x10, 0xa8, 0x01}; + expected = "4 unwind Raw Data: 0x16 0x83 0x10 0xa8 0x01"; + std::string op_string; + for (uint8_t i = 0; i < 168; i++) { + ops.push_back(0xa0 + (i % 96)); + op_string += "4 unwind Illegal\n"; + op_string += android::base::StringPrintf("4 unwind Raw Data: 0x%02x\n", ops.back()); + expected += android::base::StringPrintf(" 0x%02x", ops.back()); + if (((i + 6) % 10) == 0) { + expected += "\n4 unwind Raw Data:"; + } + } + expected = "4 unwind DW_CFA_val_expression register(2051) 168\n" + expected + "\n"; + + this->memory_.SetMemory(0xa00, ops); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0xa00, 0xaad)); + + ASSERT_EQ(expected + op_string, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaLogTest, cfa_gnu_args_size) { + this->memory_.SetMemory(0x2000, std::vector{0x2e, 0x04}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2002)); + + std::string expected = + "4 unwind DW_CFA_GNU_args_size 4\n" + "4 unwind Raw Data: 0x2e 0x04\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + this->memory_.SetMemory(0x5000, std::vector{0x2e, 0xa4, 0x80, 0x04}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x5000, 0x5004)); + + expected = + "4 unwind DW_CFA_GNU_args_size 65572\n" + "4 unwind Raw Data: 0x2e 0xa4 0x80 0x04\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaLogTest, cfa_gnu_negative_offset_extended) { + this->memory_.SetMemory(0x500, std::vector{0x2f, 0x08, 0x10}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x500, 0x503)); + + std::string expected = + "4 unwind DW_CFA_GNU_negative_offset_extended register(8) 16\n" + "4 unwind Raw Data: 0x2f 0x08 0x10\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + this->memory_.SetMemory(0x1500, std::vector{0x2f, 0x81, 0x02, 0xff, 0x01}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x1500, 0x1505)); + + expected = + "4 unwind DW_CFA_GNU_negative_offset_extended register(257) 255\n" + "4 unwind Raw Data: 0x2f 0x81 0x02 0xff 0x01\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaLogTest, cfa_register_override) { + this->memory_.SetMemory(0x300, std::vector{0x09, 0x02, 0x01, 0x09, 0x02, 0x04}); + + ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x300, 0x306)); + + std::string expected = + "4 unwind DW_CFA_register register(2) register(1)\n" + "4 unwind Raw Data: 0x09 0x02 0x01\n" + "4 unwind DW_CFA_register register(2) register(4)\n" + "4 unwind Raw Data: 0x09 0x02 0x04\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +REGISTER_TYPED_TEST_CASE_P(DwarfCfaLogTest, cfa_illegal, cfa_nop, cfa_offset, cfa_offset_extended, + cfa_offset_extended_sf, cfa_restore, cfa_restore_extended, cfa_set_loc, + cfa_advance_loc, cfa_advance_loc1, cfa_advance_loc2, cfa_advance_loc4, + cfa_undefined, cfa_same, cfa_register, cfa_state, + cfa_state_cfa_offset_restore, cfa_def_cfa, cfa_def_cfa_sf, + cfa_def_cfa_register, cfa_def_cfa_offset, cfa_def_cfa_offset_sf, + cfa_def_cfa_expression, cfa_expression, cfa_val_offset, + cfa_val_offset_sf, cfa_val_expression, cfa_gnu_args_size, + cfa_gnu_negative_offset_extended, cfa_register_override); + +typedef ::testing::Types DwarfCfaLogTestTypes; +INSTANTIATE_TYPED_TEST_CASE_P(, DwarfCfaLogTest, DwarfCfaLogTestTypes); diff --git a/libunwindstack/tests/DwarfCfaTest.cpp b/libunwindstack/tests/DwarfCfaTest.cpp new file mode 100644 index 000000000..6cf028a22 --- /dev/null +++ b/libunwindstack/tests/DwarfCfaTest.cpp @@ -0,0 +1,959 @@ +/* + * 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 + +#include +#include + +#include + +#include "DwarfCfa.h" +#include "DwarfLocation.h" +#include "DwarfMemory.h" +#include "DwarfStructs.h" +#include "Log.h" + +#include "LogFake.h" +#include "MemoryFake.h" + +template +class DwarfCfaTest : public ::testing::Test { + protected: + void SetUp() override { + ResetLogs(); + memory_.Clear(); + + dmem_.reset(new DwarfMemory(&memory_)); + + cie_.cfa_instructions_offset = 0x1000; + cie_.cfa_instructions_end = 0x1030; + // These two values should be different to distinguish between + // operations that deal with code versus data. + cie_.code_alignment_factor = 4; + cie_.data_alignment_factor = 8; + + fde_.cfa_instructions_offset = 0x2000; + fde_.cfa_instructions_end = 0x2030; + fde_.pc_start = 0x2000; + fde_.cie = &cie_; + + cfa_.reset(new DwarfCfa(dmem_.get(), &fde_)); + } + + MemoryFake memory_; + std::unique_ptr dmem_; + std::unique_ptr> cfa_; + DwarfCIE cie_; + DwarfFDE fde_; +}; +TYPED_TEST_CASE_P(DwarfCfaTest); + +// NOTE: All test class variables need to be referenced as this->. + +TYPED_TEST_P(DwarfCfaTest, cfa_illegal) { + for (uint8_t i = 0x17; i < 0x3f; i++) { + if (i == 0x2e || i == 0x2f) { + // Skip gnu extension ops. + continue; + } + this->memory_.SetMemory(0x2000, std::vector{i}); + dwarf_loc_regs_t loc_regs; + + ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2001, &loc_regs)); + ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->cfa_->last_error()); + ASSERT_EQ(0x2001U, this->dmem_->cur_offset()); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + } +} + +TYPED_TEST_P(DwarfCfaTest, cfa_nop) { + this->memory_.SetMemory(0x2000, std::vector{0x00}); + dwarf_loc_regs_t loc_regs; + + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2001, &loc_regs)); + ASSERT_EQ(0x2001U, this->dmem_->cur_offset()); + ASSERT_EQ(0U, loc_regs.size()); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +// This test needs to be examined. +TYPED_TEST_P(DwarfCfaTest, cfa_offset) { + this->memory_.SetMemory(0x2000, std::vector{0x83, 0x04}); + dwarf_loc_regs_t loc_regs; + + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2002, &loc_regs)); + ASSERT_EQ(0x2002U, this->dmem_->cur_offset()); + ASSERT_EQ(1U, loc_regs.size()); + auto location = loc_regs.find(3); + ASSERT_NE(loc_regs.end(), location); + ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type); + ASSERT_EQ(32U, location->second.values[0]); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + this->memory_.SetMemory(0x2100, std::vector{0x83, 0x84, 0x01}); + loc_regs.clear(); + + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2100, 0x2103, &loc_regs)); + ASSERT_EQ(0x2103U, this->dmem_->cur_offset()); + ASSERT_EQ(1U, loc_regs.size()); + location = loc_regs.find(3); + ASSERT_NE(loc_regs.end(), location); + ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type); + ASSERT_EQ(1056U, location->second.values[0]); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaTest, cfa_offset_extended) { + this->memory_.SetMemory(0x500, std::vector{0x05, 0x03, 0x02}); + dwarf_loc_regs_t loc_regs; + + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x500, 0x503, &loc_regs)); + ASSERT_EQ(0x503U, this->dmem_->cur_offset()); + ASSERT_EQ(1U, loc_regs.size()); + auto location = loc_regs.find(3); + ASSERT_NE(loc_regs.end(), location); + ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type); + ASSERT_EQ(2U, location->second.values[0]); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + loc_regs.clear(); + this->memory_.SetMemory(0x1500, std::vector{0x05, 0x81, 0x01, 0x82, 0x12}); + + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x1500, 0x1505, &loc_regs)); + ASSERT_EQ(0x1505U, this->dmem_->cur_offset()); + ASSERT_EQ(1U, loc_regs.size()); + location = loc_regs.find(129); + ASSERT_NE(loc_regs.end(), location); + ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type); + ASSERT_EQ(2306U, location->second.values[0]); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaTest, cfa_offset_extended_sf) { + this->memory_.SetMemory(0x500, std::vector{0x11, 0x05, 0x10}); + dwarf_loc_regs_t loc_regs; + + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x500, 0x503, &loc_regs)); + ASSERT_EQ(0x503U, this->dmem_->cur_offset()); + ASSERT_EQ(1U, loc_regs.size()); + auto location = loc_regs.find(5); + ASSERT_NE(loc_regs.end(), location); + ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type); + ASSERT_EQ(0x80U, location->second.values[0]); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + // Check a negative value for the offset. + ResetLogs(); + loc_regs.clear(); + this->memory_.SetMemory(0x1500, std::vector{0x11, 0x86, 0x01, 0xff, 0x7f}); + + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x1500, 0x1505, &loc_regs)); + ASSERT_EQ(0x1505U, this->dmem_->cur_offset()); + ASSERT_EQ(1U, loc_regs.size()); + location = loc_regs.find(134); + ASSERT_NE(loc_regs.end(), location); + ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type); + ASSERT_EQ(static_cast(-8), location->second.values[0]); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaTest, cfa_restore) { + this->memory_.SetMemory(0x2000, std::vector{0xc2}); + dwarf_loc_regs_t loc_regs; + + ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2001, &loc_regs)); + ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->last_error()); + ASSERT_EQ(0x2001U, this->dmem_->cur_offset()); + ASSERT_EQ(0U, loc_regs.size()); + + ASSERT_EQ("4 unwind restore while processing cie\n", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + dwarf_loc_regs_t cie_loc_regs; + cie_loc_regs[2] = {.type = DWARF_LOCATION_REGISTER, .values = {0, 0}}; + this->cfa_->set_cie_loc_regs(&cie_loc_regs); + this->memory_.SetMemory(0x3000, std::vector{0x82, 0x04, 0xc2}); + + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x3000, 0x3003, &loc_regs)); + ASSERT_EQ(0x3003U, this->dmem_->cur_offset()); + ASSERT_EQ(1U, loc_regs.size()); + auto location = loc_regs.find(2); + ASSERT_NE(loc_regs.end(), location); + ASSERT_EQ(DWARF_LOCATION_REGISTER, location->second.type); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaTest, cfa_restore_extended) { + this->memory_.SetMemory(0x4000, std::vector{0x06, 0x08}); + dwarf_loc_regs_t loc_regs; + + ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x4000, 0x4002, &loc_regs)); + ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->last_error()); + ASSERT_EQ(0x4002U, this->dmem_->cur_offset()); + ASSERT_EQ(0U, loc_regs.size()); + + ASSERT_EQ("4 unwind restore while processing cie\n", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + loc_regs.clear(); + this->memory_.SetMemory(0x5000, std::vector{0x05, 0x82, 0x02, 0x04, 0x06, 0x82, 0x02}); + dwarf_loc_regs_t cie_loc_regs; + cie_loc_regs[258] = {.type = DWARF_LOCATION_REGISTER, .values = {0, 0}}; + this->cfa_->set_cie_loc_regs(&cie_loc_regs); + + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x5000, 0x5007, &loc_regs)); + ASSERT_EQ(0x5007U, this->dmem_->cur_offset()); + ASSERT_EQ(1U, loc_regs.size()); + auto location = loc_regs.find(258); + ASSERT_NE(loc_regs.end(), location); + ASSERT_EQ(DWARF_LOCATION_REGISTER, location->second.type); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaTest, cfa_set_loc) { + uint8_t buffer[1 + sizeof(TypeParam)]; + buffer[0] = 0x1; + TypeParam address; + std::string raw_data("Raw Data: 0x01 "); + std::string address_str; + if (sizeof(TypeParam) == 4) { + address = 0x81234578U; + address_str = "0x81234578"; + raw_data += "0x78 0x45 0x23 0x81"; + } else { + address = 0x8123456712345678ULL; + address_str = "0x8123456712345678"; + raw_data += "0x78 0x56 0x34 0x12 0x67 0x45 0x23 0x81"; + } + memcpy(&buffer[1], &address, sizeof(address)); + + this->memory_.SetMemory(0x50, buffer, sizeof(buffer)); + ResetLogs(); + dwarf_loc_regs_t loc_regs; + ASSERT_TRUE( + this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x50, 0x51 + sizeof(TypeParam), &loc_regs)); + ASSERT_EQ(0x51 + sizeof(TypeParam), this->dmem_->cur_offset()); + ASSERT_EQ(address, this->cfa_->cur_pc()); + ASSERT_EQ(0U, loc_regs.size()); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + // Check for a set going back. + ResetLogs(); + loc_regs.clear(); + this->fde_.pc_start = address + 0x10; + ASSERT_TRUE( + this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x50, 0x51 + sizeof(TypeParam), &loc_regs)); + ASSERT_EQ(0x51 + sizeof(TypeParam), this->dmem_->cur_offset()); + ASSERT_EQ(address, this->cfa_->cur_pc()); + ASSERT_EQ(0U, loc_regs.size()); + + std::string cur_address_str(address_str); + cur_address_str[cur_address_str.size() - 2] = '8'; + std::string expected = "4 unwind Warning: PC is moving backwards: old " + cur_address_str + + " new " + address_str + "\n"; + ASSERT_EQ(expected, GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaTest, cfa_advance_loc1) { + this->memory_.SetMemory(0x200, std::vector{0x02, 0x04}); + dwarf_loc_regs_t loc_regs; + + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x202, &loc_regs)); + ASSERT_EQ(0x202U, this->dmem_->cur_offset()); + ASSERT_EQ(this->fde_.pc_start + 0x10, this->cfa_->cur_pc()); + ASSERT_EQ(0U, loc_regs.size()); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaTest, cfa_advance_loc2) { + this->memory_.SetMemory(0x600, std::vector{0x03, 0x04, 0x03}); + dwarf_loc_regs_t loc_regs; + + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x600, 0x603, &loc_regs)); + ASSERT_EQ(0x603U, this->dmem_->cur_offset()); + ASSERT_EQ(this->fde_.pc_start + 0xc10U, this->cfa_->cur_pc()); + ASSERT_EQ(0U, loc_regs.size()); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaTest, cfa_advance_loc4) { + this->memory_.SetMemory(0x500, std::vector{0x04, 0x04, 0x03, 0x02, 0x01}); + dwarf_loc_regs_t loc_regs; + + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x500, 0x505, &loc_regs)); + ASSERT_EQ(0x505U, this->dmem_->cur_offset()); + ASSERT_EQ(this->fde_.pc_start + 0x4080c10, this->cfa_->cur_pc()); + ASSERT_EQ(0U, loc_regs.size()); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaTest, cfa_undefined) { + this->memory_.SetMemory(0xa00, std::vector{0x07, 0x09}); + dwarf_loc_regs_t loc_regs; + + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0xa00, 0xa02, &loc_regs)); + ASSERT_EQ(0xa02U, this->dmem_->cur_offset()); + ASSERT_EQ(1U, loc_regs.size()); + auto location = loc_regs.find(9); + ASSERT_NE(loc_regs.end(), location); + ASSERT_EQ(DWARF_LOCATION_UNDEFINED, location->second.type); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + loc_regs.clear(); + this->memory_.SetMemory(0x1a00, std::vector{0x07, 0x81, 0x01}); + + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x1a00, 0x1a03, &loc_regs)); + ASSERT_EQ(0x1a03U, this->dmem_->cur_offset()); + ASSERT_EQ(1U, loc_regs.size()); + location = loc_regs.find(129); + ASSERT_NE(loc_regs.end(), location); + ASSERT_EQ(DWARF_LOCATION_UNDEFINED, location->second.type); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaTest, cfa_same) { + this->memory_.SetMemory(0x100, std::vector{0x08, 0x7f}); + dwarf_loc_regs_t loc_regs; + + loc_regs[127] = {.type = DWARF_LOCATION_REGISTER, .values = {0, 0}}; + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs)); + ASSERT_EQ(0x102U, this->dmem_->cur_offset()); + ASSERT_EQ(0U, loc_regs.size()); + ASSERT_EQ(0U, loc_regs.count(127)); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + loc_regs.clear(); + this->memory_.SetMemory(0x2100, std::vector{0x08, 0xff, 0x01}); + + loc_regs[255] = {.type = DWARF_LOCATION_REGISTER, .values = {0, 0}}; + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2100, 0x2103, &loc_regs)); + ASSERT_EQ(0x2103U, this->dmem_->cur_offset()); + ASSERT_EQ(0U, loc_regs.size()); + ASSERT_EQ(0U, loc_regs.count(255)); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaTest, cfa_register) { + this->memory_.SetMemory(0x300, std::vector{0x09, 0x02, 0x01}); + dwarf_loc_regs_t loc_regs; + + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x300, 0x303, &loc_regs)); + ASSERT_EQ(0x303U, this->dmem_->cur_offset()); + ASSERT_EQ(1U, loc_regs.size()); + auto location = loc_regs.find(2); + ASSERT_NE(loc_regs.end(), location); + ASSERT_EQ(DWARF_LOCATION_REGISTER, location->second.type); + ASSERT_EQ(1U, location->second.values[0]); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + loc_regs.clear(); + this->memory_.SetMemory(0x4300, std::vector{0x09, 0xff, 0x01, 0xff, 0x03}); + + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x4300, 0x4305, &loc_regs)); + ASSERT_EQ(0x4305U, this->dmem_->cur_offset()); + ASSERT_EQ(1U, loc_regs.size()); + location = loc_regs.find(255); + ASSERT_NE(loc_regs.end(), location); + ASSERT_EQ(DWARF_LOCATION_REGISTER, location->second.type); + ASSERT_EQ(511U, location->second.values[0]); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaTest, cfa_state) { + this->memory_.SetMemory(0x300, std::vector{0x0a}); + dwarf_loc_regs_t loc_regs; + + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x300, 0x301, &loc_regs)); + ASSERT_EQ(0x301U, this->dmem_->cur_offset()); + ASSERT_EQ(0U, loc_regs.size()); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + this->memory_.SetMemory(0x4300, std::vector{0x0b}); + + loc_regs.clear(); + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x4300, 0x4301, &loc_regs)); + ASSERT_EQ(0x4301U, this->dmem_->cur_offset()); + ASSERT_EQ(0U, loc_regs.size()); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + this->memory_.SetMemory(0x2000, std::vector{0x85, 0x02, 0x0a, 0x86, 0x04, 0x0b}); + + loc_regs.clear(); + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2005, &loc_regs)); + ASSERT_EQ(0x2005U, this->dmem_->cur_offset()); + ASSERT_EQ(2U, loc_regs.size()); + ASSERT_NE(loc_regs.end(), loc_regs.find(5)); + ASSERT_NE(loc_regs.end(), loc_regs.find(6)); + + loc_regs.clear(); + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2006, &loc_regs)); + ASSERT_EQ(0x2006U, this->dmem_->cur_offset()); + ASSERT_EQ(1U, loc_regs.size()); + ASSERT_NE(loc_regs.end(), loc_regs.find(5)); + + ResetLogs(); + this->memory_.SetMemory( + 0x6000, std::vector{0x0a, 0x85, 0x02, 0x0a, 0x86, 0x04, 0x0a, 0x87, 0x01, 0x0a, 0x89, + 0x05, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b}); + + loc_regs.clear(); + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x6000, 0x600c, &loc_regs)); + ASSERT_EQ(0x600cU, this->dmem_->cur_offset()); + ASSERT_EQ(4U, loc_regs.size()); + ASSERT_NE(loc_regs.end(), loc_regs.find(5)); + ASSERT_NE(loc_regs.end(), loc_regs.find(6)); + ASSERT_NE(loc_regs.end(), loc_regs.find(7)); + ASSERT_NE(loc_regs.end(), loc_regs.find(9)); + + loc_regs.clear(); + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x6000, 0x600d, &loc_regs)); + ASSERT_EQ(0x600dU, this->dmem_->cur_offset()); + ASSERT_EQ(3U, loc_regs.size()); + ASSERT_NE(loc_regs.end(), loc_regs.find(5)); + ASSERT_NE(loc_regs.end(), loc_regs.find(6)); + ASSERT_NE(loc_regs.end(), loc_regs.find(7)); + + loc_regs.clear(); + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x6000, 0x600e, &loc_regs)); + ASSERT_EQ(0x600eU, this->dmem_->cur_offset()); + ASSERT_EQ(2U, loc_regs.size()); + ASSERT_NE(loc_regs.end(), loc_regs.find(5)); + ASSERT_NE(loc_regs.end(), loc_regs.find(6)); + + loc_regs.clear(); + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x6000, 0x600f, &loc_regs)); + ASSERT_EQ(0x600fU, this->dmem_->cur_offset()); + ASSERT_EQ(1U, loc_regs.size()); + ASSERT_NE(loc_regs.end(), loc_regs.find(5)); + + loc_regs.clear(); + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x6000, 0x6010, &loc_regs)); + ASSERT_EQ(0x6010U, this->dmem_->cur_offset()); + ASSERT_EQ(0U, loc_regs.size()); + + loc_regs.clear(); + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x6000, 0x6011, &loc_regs)); + ASSERT_EQ(0x6011U, this->dmem_->cur_offset()); + ASSERT_EQ(0U, loc_regs.size()); +} + +// This test verifies that the cfa offset is saved and restored properly. +// Even though the spec is not clear about whether the offset is also +// restored, the gcc unwinder does, and libunwind does too. +TYPED_TEST_P(DwarfCfaTest, cfa_state_cfa_offset_restore) { + this->memory_.SetMemory(0x3000, std::vector{0x0a, 0x0e, 0x40, 0x0b}); + dwarf_loc_regs_t loc_regs; + loc_regs[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {5, 100}}; + + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x3000, 0x3004, &loc_regs)); + ASSERT_EQ(0x3004U, this->dmem_->cur_offset()); + ASSERT_EQ(1U, loc_regs.size()); + ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type); + ASSERT_EQ(5U, loc_regs[CFA_REG].values[0]); + ASSERT_EQ(100U, loc_regs[CFA_REG].values[1]); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaTest, cfa_def_cfa) { + this->memory_.SetMemory(0x100, std::vector{0x0c, 0x7f, 0x74}); + dwarf_loc_regs_t loc_regs; + + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x103, &loc_regs)); + ASSERT_EQ(0x103U, this->dmem_->cur_offset()); + ASSERT_EQ(1U, loc_regs.size()); + ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type); + ASSERT_EQ(0x7fU, loc_regs[CFA_REG].values[0]); + ASSERT_EQ(0x74U, loc_regs[CFA_REG].values[1]); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + loc_regs.clear(); + this->memory_.SetMemory(0x200, std::vector{0x0c, 0xff, 0x02, 0xf4, 0x04}); + + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x205, &loc_regs)); + ASSERT_EQ(0x205U, this->dmem_->cur_offset()); + ASSERT_EQ(1U, loc_regs.size()); + ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type); + ASSERT_EQ(0x17fU, loc_regs[CFA_REG].values[0]); + ASSERT_EQ(0x274U, loc_regs[CFA_REG].values[1]); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaTest, cfa_def_cfa_sf) { + this->memory_.SetMemory(0x100, std::vector{0x12, 0x30, 0x25}); + dwarf_loc_regs_t loc_regs; + + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x103, &loc_regs)); + ASSERT_EQ(0x103U, this->dmem_->cur_offset()); + ASSERT_EQ(1U, loc_regs.size()); + ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type); + ASSERT_EQ(0x30U, loc_regs[CFA_REG].values[0]); + ASSERT_EQ(0x128U, loc_regs[CFA_REG].values[1]); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + // Test a negative value. + ResetLogs(); + loc_regs.clear(); + this->memory_.SetMemory(0x200, std::vector{0x12, 0xa3, 0x01, 0xfa, 0x7f}); + + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x205, &loc_regs)); + ASSERT_EQ(0x205U, this->dmem_->cur_offset()); + ASSERT_EQ(1U, loc_regs.size()); + ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type); + ASSERT_EQ(0xa3U, loc_regs[CFA_REG].values[0]); + ASSERT_EQ(static_cast(-48), loc_regs[CFA_REG].values[1]); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaTest, cfa_def_cfa_register) { + this->memory_.SetMemory(0x100, std::vector{0x0d, 0x72}); + dwarf_loc_regs_t loc_regs; + + // This fails because the cfa is not defined as a register. + ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs)); + ASSERT_EQ(0U, loc_regs.size()); + ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->last_error()); + + ASSERT_EQ("4 unwind Attempt to set new register, but cfa is not already set to a register.\n", + GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + loc_regs.clear(); + loc_regs[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {3, 20}}; + + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs)); + ASSERT_EQ(0x102U, this->dmem_->cur_offset()); + ASSERT_EQ(1U, loc_regs.size()); + ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type); + ASSERT_EQ(0x72U, loc_regs[CFA_REG].values[0]); + ASSERT_EQ(20U, loc_regs[CFA_REG].values[1]); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + this->memory_.SetMemory(0x200, std::vector{0x0d, 0xf9, 0x20}); + loc_regs.clear(); + loc_regs[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {3, 60}}; + + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x203, &loc_regs)); + ASSERT_EQ(0x203U, this->dmem_->cur_offset()); + ASSERT_EQ(1U, loc_regs.size()); + ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type); + ASSERT_EQ(0x1079U, loc_regs[CFA_REG].values[0]); + ASSERT_EQ(60U, loc_regs[CFA_REG].values[1]); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaTest, cfa_def_cfa_offset) { + this->memory_.SetMemory(0x100, std::vector{0x0e, 0x59}); + dwarf_loc_regs_t loc_regs; + + // This fails because the cfa is not defined as a register. + ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs)); + ASSERT_EQ(0U, loc_regs.size()); + ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->last_error()); + + ASSERT_EQ("4 unwind Attempt to set offset, but cfa is not set to a register.\n", + GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + loc_regs.clear(); + loc_regs[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {3}}; + + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs)); + ASSERT_EQ(0x102U, this->dmem_->cur_offset()); + ASSERT_EQ(1U, loc_regs.size()); + ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type); + ASSERT_EQ(3U, loc_regs[CFA_REG].values[0]); + ASSERT_EQ(0x59U, loc_regs[CFA_REG].values[1]); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + this->memory_.SetMemory(0x200, std::vector{0x0e, 0xd4, 0x0a}); + loc_regs.clear(); + loc_regs[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {3}}; + + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x203, &loc_regs)); + ASSERT_EQ(0x203U, this->dmem_->cur_offset()); + ASSERT_EQ(1U, loc_regs.size()); + ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type); + ASSERT_EQ(3U, loc_regs[CFA_REG].values[0]); + ASSERT_EQ(0x554U, loc_regs[CFA_REG].values[1]); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaTest, cfa_def_cfa_offset_sf) { + this->memory_.SetMemory(0x100, std::vector{0x13, 0x23}); + dwarf_loc_regs_t loc_regs; + + // This fails because the cfa is not defined as a register. + ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs)); + ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->last_error()); + + ASSERT_EQ("4 unwind Attempt to set offset, but cfa is not set to a register.\n", + GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + loc_regs.clear(); + loc_regs[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {3}}; + + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs)); + ASSERT_EQ(0x102U, this->dmem_->cur_offset()); + ASSERT_EQ(1U, loc_regs.size()); + ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type); + ASSERT_EQ(3U, loc_regs[CFA_REG].values[0]); + ASSERT_EQ(0x118U, loc_regs[CFA_REG].values[1]); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + // Negative offset. + ResetLogs(); + this->memory_.SetMemory(0x200, std::vector{0x13, 0xf6, 0x7f}); + loc_regs.clear(); + loc_regs[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {3}}; + + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x203, &loc_regs)); + ASSERT_EQ(0x203U, this->dmem_->cur_offset()); + ASSERT_EQ(1U, loc_regs.size()); + ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type); + ASSERT_EQ(3U, loc_regs[CFA_REG].values[0]); + ASSERT_EQ(static_cast(-80), static_cast(loc_regs[CFA_REG].values[1])); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaTest, cfa_def_cfa_expression) { + this->memory_.SetMemory(0x100, std::vector{0x0f, 0x04, 0x01, 0x02, 0x03, 0x04}); + dwarf_loc_regs_t loc_regs; + + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x106, &loc_regs)); + ASSERT_EQ(0x106U, this->dmem_->cur_offset()); + ASSERT_EQ(1U, loc_regs.size()); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + std::vector ops{0x0f, 0x81, 0x01}; + for (uint8_t i = 3; i < 132; i++) { + ops.push_back(i - 1); + } + this->memory_.SetMemory(0x200, ops); + loc_regs.clear(); + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x284, &loc_regs)); + ASSERT_EQ(0x284U, this->dmem_->cur_offset()); + ASSERT_EQ(1U, loc_regs.size()); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaTest, cfa_expression) { + this->memory_.SetMemory(0x100, std::vector{0x10, 0x04, 0x02, 0x40, 0x20}); + dwarf_loc_regs_t loc_regs; + + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x105, &loc_regs)); + ASSERT_EQ(0x105U, this->dmem_->cur_offset()); + ASSERT_EQ(1U, loc_regs.size()); + auto location = loc_regs.find(4); + ASSERT_NE(loc_regs.end(), location); + ASSERT_EQ(DWARF_LOCATION_EXPRESSION, location->second.type); + ASSERT_EQ(2U, location->second.values[0]); + ASSERT_EQ(0x105U, location->second.values[1]); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + std::vector ops{0x10, 0xff, 0x01, 0x82, 0x01}; + for (uint8_t i = 5; i < 135; i++) { + ops.push_back(i - 4); + } + + this->memory_.SetMemory(0x200, ops); + loc_regs.clear(); + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x287, &loc_regs)); + ASSERT_EQ(0x287U, this->dmem_->cur_offset()); + ASSERT_EQ(1U, loc_regs.size()); + location = loc_regs.find(255); + ASSERT_NE(loc_regs.end(), location); + ASSERT_EQ(DWARF_LOCATION_EXPRESSION, location->second.type); + ASSERT_EQ(130U, location->second.values[0]); + ASSERT_EQ(0x287U, location->second.values[1]); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaTest, cfa_val_offset) { + this->memory_.SetMemory(0x100, std::vector{0x14, 0x45, 0x54}); + dwarf_loc_regs_t loc_regs; + + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x103, &loc_regs)); + ASSERT_EQ(0x103U, this->dmem_->cur_offset()); + ASSERT_EQ(1U, loc_regs.size()); + auto location = loc_regs.find(69); + ASSERT_NE(loc_regs.end(), location); + ASSERT_EQ(DWARF_LOCATION_VAL_OFFSET, location->second.type); + ASSERT_EQ(0x2a0U, location->second.values[0]); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + loc_regs.clear(); + this->memory_.SetMemory(0x400, std::vector{0x14, 0xa2, 0x02, 0xb4, 0x05}); + + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x400, 0x405, &loc_regs)); + ASSERT_EQ(0x405U, this->dmem_->cur_offset()); + ASSERT_EQ(1U, loc_regs.size()); + location = loc_regs.find(290); + ASSERT_NE(loc_regs.end(), location); + ASSERT_EQ(DWARF_LOCATION_VAL_OFFSET, location->second.type); + ASSERT_EQ(0x15a0U, location->second.values[0]); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaTest, cfa_val_offset_sf) { + this->memory_.SetMemory(0x100, std::vector{0x15, 0x56, 0x12}); + dwarf_loc_regs_t loc_regs; + + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x103, &loc_regs)); + ASSERT_EQ(0x103U, this->dmem_->cur_offset()); + ASSERT_EQ(1U, loc_regs.size()); + auto location = loc_regs.find(86); + ASSERT_NE(loc_regs.end(), location); + ASSERT_EQ(DWARF_LOCATION_VAL_OFFSET, location->second.type); + ASSERT_EQ(0x90U, location->second.values[0]); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + // Negative value. + ResetLogs(); + loc_regs.clear(); + this->memory_.SetMemory(0xa00, std::vector{0x15, 0xff, 0x01, 0xc0, 0x7f}); + + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0xa00, 0xa05, &loc_regs)); + ASSERT_EQ(0xa05U, this->dmem_->cur_offset()); + ASSERT_EQ(1U, loc_regs.size()); + location = loc_regs.find(255); + ASSERT_NE(loc_regs.end(), location); + ASSERT_EQ(DWARF_LOCATION_VAL_OFFSET, location->second.type); + ASSERT_EQ(static_cast(-512), location->second.values[0]); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaTest, cfa_val_expression) { + this->memory_.SetMemory(0x100, std::vector{0x16, 0x05, 0x02, 0x10, 0x20}); + dwarf_loc_regs_t loc_regs; + + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x105, &loc_regs)); + ASSERT_EQ(0x105U, this->dmem_->cur_offset()); + ASSERT_EQ(1U, loc_regs.size()); + auto location = loc_regs.find(5); + ASSERT_NE(loc_regs.end(), location); + ASSERT_EQ(DWARF_LOCATION_VAL_EXPRESSION, location->second.type); + ASSERT_EQ(2U, location->second.values[0]); + ASSERT_EQ(0x105U, location->second.values[1]); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + std::vector ops{0x16, 0x83, 0x10, 0xa8, 0x01}; + for (uint8_t i = 0; i < 168; i++) { + ops.push_back(i); + } + + this->memory_.SetMemory(0xa00, ops); + loc_regs.clear(); + + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0xa00, 0xaad, &loc_regs)); + ASSERT_EQ(0xaadU, this->dmem_->cur_offset()); + ASSERT_EQ(1U, loc_regs.size()); + location = loc_regs.find(2051); + ASSERT_NE(loc_regs.end(), location); + ASSERT_EQ(DWARF_LOCATION_VAL_EXPRESSION, location->second.type); + ASSERT_EQ(168U, location->second.values[0]); + ASSERT_EQ(0xaadU, location->second.values[1]); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaTest, cfa_gnu_args_size) { + this->memory_.SetMemory(0x2000, std::vector{0x2e, 0x04}); + dwarf_loc_regs_t loc_regs; + + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2002, &loc_regs)); + ASSERT_EQ(0x2002U, this->dmem_->cur_offset()); + ASSERT_EQ(0U, loc_regs.size()); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + loc_regs.clear(); + this->memory_.SetMemory(0x5000, std::vector{0x2e, 0xa4, 0x80, 0x04}); + + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x5000, 0x5004, &loc_regs)); + ASSERT_EQ(0x5004U, this->dmem_->cur_offset()); + ASSERT_EQ(0U, loc_regs.size()); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaTest, cfa_gnu_negative_offset_extended) { + this->memory_.SetMemory(0x500, std::vector{0x2f, 0x08, 0x10}); + dwarf_loc_regs_t loc_regs; + + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x500, 0x503, &loc_regs)); + ASSERT_EQ(0x503U, this->dmem_->cur_offset()); + ASSERT_EQ(1U, loc_regs.size()); + auto location = loc_regs.find(8); + ASSERT_NE(loc_regs.end(), location); + ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type); + ASSERT_EQ(static_cast(-16), location->second.values[0]); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); + + ResetLogs(); + loc_regs.clear(); + this->memory_.SetMemory(0x1500, std::vector{0x2f, 0x81, 0x02, 0xff, 0x01}); + + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x1500, 0x1505, &loc_regs)); + ASSERT_EQ(0x1505U, this->dmem_->cur_offset()); + ASSERT_EQ(1U, loc_regs.size()); + location = loc_regs.find(257); + ASSERT_NE(loc_regs.end(), location); + ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type); + ASSERT_EQ(static_cast(-255), location->second.values[0]); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +TYPED_TEST_P(DwarfCfaTest, cfa_register_override) { + this->memory_.SetMemory(0x300, std::vector{0x09, 0x02, 0x01, 0x09, 0x02, 0x04}); + dwarf_loc_regs_t loc_regs; + + ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x300, 0x306, &loc_regs)); + ASSERT_EQ(0x306U, this->dmem_->cur_offset()); + ASSERT_EQ(1U, loc_regs.size()); + auto location = loc_regs.find(2); + ASSERT_NE(loc_regs.end(), location); + ASSERT_EQ(DWARF_LOCATION_REGISTER, location->second.type); + ASSERT_EQ(4U, location->second.values[0]); + + ASSERT_EQ("", GetFakeLogPrint()); + ASSERT_EQ("", GetFakeLogBuf()); +} + +REGISTER_TYPED_TEST_CASE_P(DwarfCfaTest, cfa_illegal, cfa_nop, cfa_offset, cfa_offset_extended, + cfa_offset_extended_sf, cfa_restore, cfa_restore_extended, cfa_set_loc, + cfa_advance_loc1, cfa_advance_loc2, cfa_advance_loc4, cfa_undefined, + cfa_same, cfa_register, cfa_state, cfa_state_cfa_offset_restore, + cfa_def_cfa, cfa_def_cfa_sf, cfa_def_cfa_register, cfa_def_cfa_offset, + cfa_def_cfa_offset_sf, cfa_def_cfa_expression, cfa_expression, + cfa_val_offset, cfa_val_offset_sf, cfa_val_expression, cfa_gnu_args_size, + cfa_gnu_negative_offset_extended, cfa_register_override); + +typedef ::testing::Types DwarfCfaTestTypes; +INSTANTIATE_TYPED_TEST_CASE_P(, DwarfCfaTest, DwarfCfaTestTypes);