|  | /* | 
|  | * 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 <inttypes.h> | 
|  | #include <stdint.h> | 
|  |  | 
|  | #include <string> | 
|  | #include <type_traits> | 
|  | #include <vector> | 
|  |  | 
|  | #include <android-base/macros.h> | 
|  | #include <android-base/stringprintf.h> | 
|  |  | 
|  | #include <unwindstack/DwarfError.h> | 
|  | #include <unwindstack/DwarfLocation.h> | 
|  | #include <unwindstack/Log.h> | 
|  |  | 
|  | #include "DwarfCfa.h" | 
|  | #include "DwarfEncoding.h" | 
|  | #include "DwarfOp.h" | 
|  |  | 
|  | namespace unwindstack { | 
|  |  | 
|  | template <typename AddressType> | 
|  | constexpr typename DwarfCfa<AddressType>::process_func DwarfCfa<AddressType>::kCallbackTable[64]; | 
|  |  | 
|  | template <typename AddressType> | 
|  | bool DwarfCfa<AddressType>::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_.code = DWARF_ERROR_NONE; | 
|  | last_error_.address = 0; | 
|  |  | 
|  | memory_->set_cur_offset(start_offset); | 
|  | uint64_t cfa_offset; | 
|  | cur_pc_ = fde_->pc_start; | 
|  | loc_regs->pc_start = cur_pc_; | 
|  | while (true) { | 
|  | if (cur_pc_ > pc) { | 
|  | loc_regs->pc_end = cur_pc_; | 
|  | return true; | 
|  | } | 
|  | if ((cfa_offset = memory_->cur_offset()) >= end_offset) { | 
|  | loc_regs->pc_end = fde_->pc_end; | 
|  | return true; | 
|  | } | 
|  | loc_regs->pc_start = cur_pc_; | 
|  | operands_.clear(); | 
|  | // Read the cfa information. | 
|  | uint8_t cfa_value; | 
|  | if (!memory_->ReadBytes(&cfa_value, 1)) { | 
|  | last_error_.code = DWARF_ERROR_MEMORY_INVALID; | 
|  | last_error_.address = memory_->cur_offset(); | 
|  | 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_.code = DWARF_ERROR_MEMORY_INVALID; | 
|  | last_error_.address = memory_->cur_offset(); | 
|  | return false; | 
|  | } | 
|  | SignedType signed_offset = | 
|  | static_cast<SignedType>(offset) * fde_->cie->data_alignment_factor; | 
|  | (*loc_regs)[cfa_low] = {.type = DWARF_LOCATION_OFFSET, | 
|  | .values = {static_cast<uint64_t>(signed_offset)}}; | 
|  | break; | 
|  | } | 
|  | case 3: { | 
|  | if (cie_loc_regs_ == nullptr) { | 
|  | log(0, "restore while processing cie"); | 
|  | last_error_.code = 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<AddressType>::kCallbackTable[cfa_low]; | 
|  | if (handle_func == nullptr) { | 
|  | last_error_.code = 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_.code = DWARF_ERROR_MEMORY_INVALID; | 
|  | last_error_.address = memory_->cur_offset(); | 
|  | return false; | 
|  | } | 
|  | operands_.push_back(block_length); | 
|  | memory_->set_cur_offset(memory_->cur_offset() + block_length); | 
|  | continue; | 
|  | } | 
|  | uint64_t value; | 
|  | if (!memory_->ReadEncodedValue<AddressType>(cfa->operands[i], &value)) { | 
|  | last_error_.code = DWARF_ERROR_MEMORY_INVALID; | 
|  | last_error_.address = memory_->cur_offset(); | 
|  | return false; | 
|  | } | 
|  | operands_.push_back(value); | 
|  | } | 
|  |  | 
|  | if (!(this->*handle_func)(loc_regs)) { | 
|  | return false; | 
|  | } | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | template <typename AddressType> | 
|  | std::string DwarfCfa<AddressType>::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<SignedType>(value)); | 
|  | break; | 
|  | case DwarfCfaInfo::DWARF_DISPLAY_ADVANCE_LOC: | 
|  | *cur_pc += value; | 
|  | FALLTHROUGH_INTENDED; | 
|  | // 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; | 
|  | FALLTHROUGH_INTENDED; | 
|  | // Fall through to log the value. | 
|  | case DwarfCfaInfo::DWARF_DISPLAY_ADDRESS: | 
|  | if (std::is_same<AddressType, uint32_t>::value) { | 
|  | string += android::base::StringPrintf(" 0x%" PRIx32, static_cast<uint32_t>(value)); | 
|  | } else { | 
|  | string += android::base::StringPrintf(" 0x%" PRIx64, static_cast<uint64_t>(value)); | 
|  | } | 
|  | break; | 
|  | default: | 
|  | string = " unknown"; | 
|  | } | 
|  | return string; | 
|  | } | 
|  |  | 
|  | template <typename AddressType> | 
|  | bool DwarfCfa<AddressType>::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 <typename AddressType> | 
|  | bool DwarfCfa<AddressType>::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<std::string> 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<AddressType> 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<AddressType>(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 <typename AddressType> | 
|  | bool DwarfCfa<AddressType>::Log(uint32_t indent, uint64_t pc, 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(0, ""); | 
|  | log(indent, "PC 0x%" PRIx64, cur_pc); | 
|  | } | 
|  | old_pc = cur_pc; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Static data. | 
|  | template <typename AddressType> | 
|  | bool DwarfCfa<AddressType>::cfa_nop(dwarf_loc_regs_t*) { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | template <typename AddressType> | 
|  | bool DwarfCfa<AddressType>::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<AddressType, uint32_t>::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 <typename AddressType> | 
|  | bool DwarfCfa<AddressType>::cfa_advance_loc(dwarf_loc_regs_t*) { | 
|  | cur_pc_ += operands_[0] * fde_->cie->code_alignment_factor; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | template <typename AddressType> | 
|  | bool DwarfCfa<AddressType>::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 <typename AddressType> | 
|  | bool DwarfCfa<AddressType>::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_.code = 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 <typename AddressType> | 
|  | bool DwarfCfa<AddressType>::cfa_undefined(dwarf_loc_regs_t* loc_regs) { | 
|  | AddressType reg = operands_[0]; | 
|  | (*loc_regs)[reg] = {.type = DWARF_LOCATION_UNDEFINED}; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | template <typename AddressType> | 
|  | bool DwarfCfa<AddressType>::cfa_same_value(dwarf_loc_regs_t* loc_regs) { | 
|  | AddressType reg = operands_[0]; | 
|  | loc_regs->erase(reg); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | template <typename AddressType> | 
|  | bool DwarfCfa<AddressType>::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 <typename AddressType> | 
|  | bool DwarfCfa<AddressType>::cfa_remember_state(dwarf_loc_regs_t* loc_regs) { | 
|  | loc_reg_state_.push(*loc_regs); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | template <typename AddressType> | 
|  | bool DwarfCfa<AddressType>::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 <typename AddressType> | 
|  | bool DwarfCfa<AddressType>::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 <typename AddressType> | 
|  | bool DwarfCfa<AddressType>::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_.code = DWARF_ERROR_ILLEGAL_STATE; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | cfa_location->second.values[0] = operands_[0]; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | template <typename AddressType> | 
|  | bool DwarfCfa<AddressType>::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_.code = DWARF_ERROR_ILLEGAL_STATE; | 
|  | return false; | 
|  | } | 
|  | cfa_location->second.values[1] = operands_[0]; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | template <typename AddressType> | 
|  | bool DwarfCfa<AddressType>::cfa_def_cfa_expression(dwarf_loc_regs_t* loc_regs) { | 
|  | // There is only one type of expression for CFA evaluation and the DWARF | 
|  | // specification is unclear whether it returns the address or the | 
|  | // dereferenced value. GDB expects the value, so will we. | 
|  | (*loc_regs)[CFA_REG] = {.type = DWARF_LOCATION_VAL_EXPRESSION, | 
|  | .values = {operands_[0], memory_->cur_offset()}}; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | template <typename AddressType> | 
|  | bool DwarfCfa<AddressType>::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 <typename AddressType> | 
|  | bool DwarfCfa<AddressType>::cfa_offset_extended_sf(dwarf_loc_regs_t* loc_regs) { | 
|  | AddressType reg = operands_[0]; | 
|  | SignedType value = static_cast<SignedType>(operands_[1]) * fde_->cie->data_alignment_factor; | 
|  | (*loc_regs)[reg] = {.type = DWARF_LOCATION_OFFSET, .values = {static_cast<uint64_t>(value)}}; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | template <typename AddressType> | 
|  | bool DwarfCfa<AddressType>::cfa_def_cfa_sf(dwarf_loc_regs_t* loc_regs) { | 
|  | SignedType offset = static_cast<SignedType>(operands_[1]) * fde_->cie->data_alignment_factor; | 
|  | (*loc_regs)[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, | 
|  | .values = {operands_[0], static_cast<uint64_t>(offset)}}; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | template <typename AddressType> | 
|  | bool DwarfCfa<AddressType>::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_.code = DWARF_ERROR_ILLEGAL_STATE; | 
|  | return false; | 
|  | } | 
|  | SignedType offset = static_cast<SignedType>(operands_[0]) * fde_->cie->data_alignment_factor; | 
|  | cfa_location->second.values[1] = static_cast<uint64_t>(offset); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | template <typename AddressType> | 
|  | bool DwarfCfa<AddressType>::cfa_val_offset(dwarf_loc_regs_t* loc_regs) { | 
|  | AddressType reg = operands_[0]; | 
|  | SignedType offset = static_cast<SignedType>(operands_[1]) * fde_->cie->data_alignment_factor; | 
|  | (*loc_regs)[reg] = {.type = DWARF_LOCATION_VAL_OFFSET, .values = {static_cast<uint64_t>(offset)}}; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | template <typename AddressType> | 
|  | bool DwarfCfa<AddressType>::cfa_val_offset_sf(dwarf_loc_regs_t* loc_regs) { | 
|  | AddressType reg = operands_[0]; | 
|  | SignedType offset = static_cast<SignedType>(operands_[1]) * fde_->cie->data_alignment_factor; | 
|  | (*loc_regs)[reg] = {.type = DWARF_LOCATION_VAL_OFFSET, .values = {static_cast<uint64_t>(offset)}}; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | template <typename AddressType> | 
|  | bool DwarfCfa<AddressType>::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 <typename AddressType> | 
|  | bool DwarfCfa<AddressType>::cfa_gnu_negative_offset_extended(dwarf_loc_regs_t* loc_regs) { | 
|  | AddressType reg = operands_[0]; | 
|  | SignedType offset = -static_cast<SignedType>(operands_[1]); | 
|  | (*loc_regs)[reg] = {.type = DWARF_LOCATION_OFFSET, .values = {static_cast<uint64_t>(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<uint32_t>; | 
|  | template class DwarfCfa<uint64_t>; | 
|  |  | 
|  | }  // namespace unwindstack |