4cc36d2b43
It turns out that for the dwarf information, if a FDE indicates it's pc relative, then pc has to be incremented by the load bias. If not, then it should not be incremented. Previously, the code always subtracted load bias values from pcs, and assumed that all fdes were incremented by load bias values. The new code actually reads the fdes and adjusted the pcs in the fde and in the eh frame hdr so that load bias values are already handled properly. In addition, add dumping of arm exidx values in unwind_reg_info. This allowed verifying that the debug frame in those elf files was being handled properly. Added a new unit test that only has a debug frame that has a non-zero load bias and has fde entries that do not have pc relative encoding. Fix a couple of other small bugs. Bug: 109824792 Test: All libbacktrace/libunwindstack unit tests pass. Test: Ran ART 137-cfi test and 004-ThreadStress. Test: Verify that displaying the fde start and end pc actually match the Test: real data for fde that have pc relative set, and that don't. Test: Verified that the unwind information for arm exidx matches the Test: debug frame data. Change-Id: I707555286b5cb05df9f25489e8c5ede753cfe0fb
862 lines
22 KiB
C++
862 lines
22 KiB
C++
/*
|
|
* Copyright (C) 2016 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <deque>
|
|
#include <string>
|
|
|
|
#include <android-base/stringprintf.h>
|
|
|
|
#include <unwindstack/Log.h>
|
|
#include <unwindstack/MachineArm.h>
|
|
#include <unwindstack/Memory.h>
|
|
#include <unwindstack/RegsArm.h>
|
|
|
|
#include "ArmExidx.h"
|
|
#include "Check.h"
|
|
|
|
namespace unwindstack {
|
|
|
|
static constexpr uint8_t LOG_CFA_REG = 64;
|
|
|
|
void ArmExidx::LogRawData() {
|
|
std::string log_str("Raw Data:");
|
|
for (const uint8_t data : data_) {
|
|
log_str += android::base::StringPrintf(" 0x%02x", data);
|
|
}
|
|
log(log_indent_, log_str.c_str());
|
|
}
|
|
|
|
bool ArmExidx::ExtractEntryData(uint32_t entry_offset) {
|
|
data_.clear();
|
|
status_ = ARM_STATUS_NONE;
|
|
|
|
if (entry_offset & 1) {
|
|
// The offset needs to be at least two byte aligned.
|
|
status_ = ARM_STATUS_INVALID_ALIGNMENT;
|
|
return false;
|
|
}
|
|
|
|
// Each entry is a 32 bit prel31 offset followed by 32 bits
|
|
// of unwind information. If bit 31 of the unwind data is zero,
|
|
// then this is a prel31 offset to the start of the unwind data.
|
|
// If the unwind data is 1, then this is a cant unwind entry.
|
|
// Otherwise, this data is the compact form of the unwind information.
|
|
uint32_t data;
|
|
if (!elf_memory_->Read32(entry_offset + 4, &data)) {
|
|
status_ = ARM_STATUS_READ_FAILED;
|
|
status_address_ = entry_offset + 4;
|
|
return false;
|
|
}
|
|
if (data == 1) {
|
|
// This is a CANT UNWIND entry.
|
|
status_ = ARM_STATUS_NO_UNWIND;
|
|
if (log_type_ != ARM_LOG_NONE) {
|
|
if (log_type_ == ARM_LOG_FULL) {
|
|
log(log_indent_, "Raw Data: 0x00 0x00 0x00 0x01");
|
|
}
|
|
log(log_indent_, "[cantunwind]");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if (data & (1UL << 31)) {
|
|
// This is a compact table entry.
|
|
if ((data >> 24) & 0xf) {
|
|
// This is a non-zero index, this code doesn't support
|
|
// other formats.
|
|
status_ = ARM_STATUS_INVALID_PERSONALITY;
|
|
return false;
|
|
}
|
|
data_.push_back((data >> 16) & 0xff);
|
|
data_.push_back((data >> 8) & 0xff);
|
|
uint8_t last_op = data & 0xff;
|
|
data_.push_back(last_op);
|
|
if (last_op != ARM_OP_FINISH) {
|
|
// If this didn't end with a finish op, add one.
|
|
data_.push_back(ARM_OP_FINISH);
|
|
}
|
|
if (log_type_ == ARM_LOG_FULL) {
|
|
LogRawData();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Get the address of the ops.
|
|
// Sign extend the data value if necessary.
|
|
int32_t signed_data = static_cast<int32_t>(data << 1) >> 1;
|
|
uint32_t addr = (entry_offset + 4) + signed_data;
|
|
if (!elf_memory_->Read32(addr, &data)) {
|
|
status_ = ARM_STATUS_READ_FAILED;
|
|
status_address_ = addr;
|
|
return false;
|
|
}
|
|
|
|
size_t num_table_words;
|
|
if (data & (1UL << 31)) {
|
|
// Compact model.
|
|
switch ((data >> 24) & 0xf) {
|
|
case 0:
|
|
num_table_words = 0;
|
|
data_.push_back((data >> 16) & 0xff);
|
|
break;
|
|
case 1:
|
|
case 2:
|
|
num_table_words = (data >> 16) & 0xff;
|
|
addr += 4;
|
|
break;
|
|
default:
|
|
// Only a personality of 0, 1, 2 is valid.
|
|
status_ = ARM_STATUS_INVALID_PERSONALITY;
|
|
return false;
|
|
}
|
|
data_.push_back((data >> 8) & 0xff);
|
|
data_.push_back(data & 0xff);
|
|
} else {
|
|
// Generic model.
|
|
|
|
// Skip the personality routine data, it doesn't contain any data
|
|
// needed to decode the unwind information.
|
|
addr += 4;
|
|
if (!elf_memory_->Read32(addr, &data)) {
|
|
status_ = ARM_STATUS_READ_FAILED;
|
|
status_address_ = addr;
|
|
return false;
|
|
}
|
|
num_table_words = (data >> 24) & 0xff;
|
|
data_.push_back((data >> 16) & 0xff);
|
|
data_.push_back((data >> 8) & 0xff);
|
|
data_.push_back(data & 0xff);
|
|
addr += 4;
|
|
}
|
|
|
|
if (num_table_words > 5) {
|
|
status_ = ARM_STATUS_MALFORMED;
|
|
return false;
|
|
}
|
|
|
|
for (size_t i = 0; i < num_table_words; i++) {
|
|
if (!elf_memory_->Read32(addr, &data)) {
|
|
status_ = ARM_STATUS_READ_FAILED;
|
|
status_address_ = addr;
|
|
return false;
|
|
}
|
|
data_.push_back((data >> 24) & 0xff);
|
|
data_.push_back((data >> 16) & 0xff);
|
|
data_.push_back((data >> 8) & 0xff);
|
|
data_.push_back(data & 0xff);
|
|
addr += 4;
|
|
}
|
|
|
|
if (data_.back() != ARM_OP_FINISH) {
|
|
// If this didn't end with a finish op, add one.
|
|
data_.push_back(ARM_OP_FINISH);
|
|
}
|
|
|
|
if (log_type_ == ARM_LOG_FULL) {
|
|
LogRawData();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
inline bool ArmExidx::GetByte(uint8_t* byte) {
|
|
if (data_.empty()) {
|
|
status_ = ARM_STATUS_TRUNCATED;
|
|
return false;
|
|
}
|
|
*byte = data_.front();
|
|
data_.pop_front();
|
|
return true;
|
|
}
|
|
|
|
inline bool ArmExidx::DecodePrefix_10_00(uint8_t byte) {
|
|
CHECK((byte >> 4) == 0x8);
|
|
|
|
uint16_t registers = (byte & 0xf) << 8;
|
|
if (!GetByte(&byte)) {
|
|
return false;
|
|
}
|
|
|
|
registers |= byte;
|
|
if (registers == 0) {
|
|
// 10000000 00000000: Refuse to unwind
|
|
if (log_type_ != ARM_LOG_NONE) {
|
|
log(log_indent_, "Refuse to unwind");
|
|
}
|
|
status_ = ARM_STATUS_NO_UNWIND;
|
|
return false;
|
|
}
|
|
// 1000iiii iiiiiiii: Pop up to 12 integer registers under masks {r15-r12}, {r11-r4}
|
|
registers <<= 4;
|
|
|
|
if (log_type_ != ARM_LOG_NONE) {
|
|
if (log_type_ == ARM_LOG_FULL) {
|
|
bool add_comma = false;
|
|
std::string msg = "pop {";
|
|
for (size_t reg = 4; reg < 16; reg++) {
|
|
if (registers & (1 << reg)) {
|
|
if (add_comma) {
|
|
msg += ", ";
|
|
}
|
|
msg += android::base::StringPrintf("r%zu", reg);
|
|
add_comma = true;
|
|
}
|
|
}
|
|
log(log_indent_, "%s}", msg.c_str());
|
|
} else {
|
|
uint32_t cfa_offset = __builtin_popcount(registers) * 4;
|
|
log_cfa_offset_ += cfa_offset;
|
|
for (size_t reg = 4; reg < 16; reg++) {
|
|
if (registers & (1 << reg)) {
|
|
log_regs_[reg] = cfa_offset;
|
|
cfa_offset -= 4;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (log_skip_execution_) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
for (size_t reg = 4; reg < 16; reg++) {
|
|
if (registers & (1 << reg)) {
|
|
if (!process_memory_->Read32(cfa_, &(*regs_)[reg])) {
|
|
status_ = ARM_STATUS_READ_FAILED;
|
|
status_address_ = cfa_;
|
|
return false;
|
|
}
|
|
cfa_ += 4;
|
|
}
|
|
}
|
|
|
|
// If the sp register is modified, change the cfa value.
|
|
if (registers & (1 << ARM_REG_SP)) {
|
|
cfa_ = (*regs_)[ARM_REG_SP];
|
|
}
|
|
|
|
// Indicate if the pc register was set.
|
|
if (registers & (1 << ARM_REG_PC)) {
|
|
pc_set_ = true;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
inline bool ArmExidx::DecodePrefix_10_01(uint8_t byte) {
|
|
CHECK((byte >> 4) == 0x9);
|
|
|
|
uint8_t bits = byte & 0xf;
|
|
if (bits == 13 || bits == 15) {
|
|
// 10011101: Reserved as prefix for ARM register to register moves
|
|
// 10011111: Reserved as prefix for Intel Wireless MMX register to register moves
|
|
if (log_type_ != ARM_LOG_NONE) {
|
|
log(log_indent_, "[Reserved]");
|
|
}
|
|
status_ = ARM_STATUS_RESERVED;
|
|
return false;
|
|
}
|
|
// 1001nnnn: Set vsp = r[nnnn] (nnnn != 13, 15)
|
|
if (log_type_ != ARM_LOG_NONE) {
|
|
if (log_type_ == ARM_LOG_FULL) {
|
|
log(log_indent_, "vsp = r%d", bits);
|
|
} else {
|
|
log_regs_[LOG_CFA_REG] = bits;
|
|
}
|
|
|
|
if (log_skip_execution_) {
|
|
return true;
|
|
}
|
|
}
|
|
// It is impossible for bits to be larger than the total number of
|
|
// arm registers, so don't bother checking if bits is a valid register.
|
|
cfa_ = (*regs_)[bits];
|
|
return true;
|
|
}
|
|
|
|
inline bool ArmExidx::DecodePrefix_10_10(uint8_t byte) {
|
|
CHECK((byte >> 4) == 0xa);
|
|
|
|
// 10100nnn: Pop r4-r[4+nnn]
|
|
// 10101nnn: Pop r4-r[4+nnn], r14
|
|
if (log_type_ != ARM_LOG_NONE) {
|
|
uint8_t end_reg = byte & 0x7;
|
|
if (log_type_ == ARM_LOG_FULL) {
|
|
std::string msg = "pop {r4";
|
|
if (end_reg) {
|
|
msg += android::base::StringPrintf("-r%d", 4 + end_reg);
|
|
}
|
|
if (byte & 0x8) {
|
|
log(log_indent_, "%s, r14}", msg.c_str());
|
|
} else {
|
|
log(log_indent_, "%s}", msg.c_str());
|
|
}
|
|
} else {
|
|
end_reg += 4;
|
|
uint32_t cfa_offset = (end_reg - 3) * 4;
|
|
if (byte & 0x8) {
|
|
cfa_offset += 4;
|
|
}
|
|
log_cfa_offset_ += cfa_offset;
|
|
|
|
for (uint8_t reg = 4; reg <= end_reg; reg++) {
|
|
log_regs_[reg] = cfa_offset;
|
|
cfa_offset -= 4;
|
|
}
|
|
|
|
if (byte & 0x8) {
|
|
log_regs_[14] = cfa_offset;
|
|
}
|
|
}
|
|
|
|
if (log_skip_execution_) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
for (size_t i = 4; i <= 4 + (byte & 0x7); i++) {
|
|
if (!process_memory_->Read32(cfa_, &(*regs_)[i])) {
|
|
status_ = ARM_STATUS_READ_FAILED;
|
|
status_address_ = cfa_;
|
|
return false;
|
|
}
|
|
cfa_ += 4;
|
|
}
|
|
if (byte & 0x8) {
|
|
if (!process_memory_->Read32(cfa_, &(*regs_)[ARM_REG_R14])) {
|
|
status_ = ARM_STATUS_READ_FAILED;
|
|
status_address_ = cfa_;
|
|
return false;
|
|
}
|
|
cfa_ += 4;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
inline bool ArmExidx::DecodePrefix_10_11_0000() {
|
|
// 10110000: Finish
|
|
if (log_type_ != ARM_LOG_NONE) {
|
|
if (log_type_ == ARM_LOG_FULL) {
|
|
log(log_indent_, "finish");
|
|
}
|
|
|
|
if (log_skip_execution_) {
|
|
status_ = ARM_STATUS_FINISH;
|
|
return false;
|
|
}
|
|
}
|
|
status_ = ARM_STATUS_FINISH;
|
|
return false;
|
|
}
|
|
|
|
inline bool ArmExidx::DecodePrefix_10_11_0001() {
|
|
uint8_t byte;
|
|
if (!GetByte(&byte)) {
|
|
return false;
|
|
}
|
|
|
|
if (byte == 0) {
|
|
// 10110001 00000000: Spare
|
|
if (log_type_ != ARM_LOG_NONE) {
|
|
log(log_indent_, "Spare");
|
|
}
|
|
status_ = ARM_STATUS_SPARE;
|
|
return false;
|
|
}
|
|
if (byte >> 4) {
|
|
// 10110001 xxxxyyyy: Spare (xxxx != 0000)
|
|
if (log_type_ != ARM_LOG_NONE) {
|
|
log(log_indent_, "Spare");
|
|
}
|
|
status_ = ARM_STATUS_SPARE;
|
|
return false;
|
|
}
|
|
|
|
// 10110001 0000iiii: Pop integer registers under mask {r3, r2, r1, r0}
|
|
if (log_type_ != ARM_LOG_NONE) {
|
|
if (log_type_ == ARM_LOG_FULL) {
|
|
bool add_comma = false;
|
|
std::string msg = "pop {";
|
|
for (size_t i = 0; i < 4; i++) {
|
|
if (byte & (1 << i)) {
|
|
if (add_comma) {
|
|
msg += ", ";
|
|
}
|
|
msg += android::base::StringPrintf("r%zu", i);
|
|
add_comma = true;
|
|
}
|
|
}
|
|
log(log_indent_, "%s}", msg.c_str());
|
|
} else {
|
|
byte &= 0xf;
|
|
uint32_t cfa_offset = __builtin_popcount(byte) * 4;
|
|
log_cfa_offset_ += cfa_offset;
|
|
for (size_t reg = 0; reg < 4; reg++) {
|
|
if (byte & (1 << reg)) {
|
|
log_regs_[reg] = cfa_offset;
|
|
cfa_offset -= 4;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (log_skip_execution_) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
for (size_t reg = 0; reg < 4; reg++) {
|
|
if (byte & (1 << reg)) {
|
|
if (!process_memory_->Read32(cfa_, &(*regs_)[reg])) {
|
|
status_ = ARM_STATUS_READ_FAILED;
|
|
status_address_ = cfa_;
|
|
return false;
|
|
}
|
|
cfa_ += 4;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
inline void ArmExidx::AdjustRegisters(int32_t offset) {
|
|
for (auto& entry : log_regs_) {
|
|
if (entry.first >= LOG_CFA_REG) {
|
|
break;
|
|
}
|
|
entry.second += offset;
|
|
}
|
|
}
|
|
|
|
inline bool ArmExidx::DecodePrefix_10_11_0010() {
|
|
// 10110010 uleb128: vsp = vsp + 0x204 + (uleb128 << 2)
|
|
uint32_t result = 0;
|
|
uint32_t shift = 0;
|
|
uint8_t byte;
|
|
do {
|
|
if (!GetByte(&byte)) {
|
|
return false;
|
|
}
|
|
|
|
result |= (byte & 0x7f) << shift;
|
|
shift += 7;
|
|
} while (byte & 0x80);
|
|
result <<= 2;
|
|
if (log_type_ != ARM_LOG_NONE) {
|
|
int32_t cfa_offset = 0x204 + result;
|
|
if (log_type_ == ARM_LOG_FULL) {
|
|
log(log_indent_, "vsp = vsp + %d", cfa_offset);
|
|
} else {
|
|
log_cfa_offset_ += cfa_offset;
|
|
}
|
|
AdjustRegisters(cfa_offset);
|
|
|
|
if (log_skip_execution_) {
|
|
return true;
|
|
}
|
|
}
|
|
cfa_ += 0x204 + result;
|
|
return true;
|
|
}
|
|
|
|
inline bool ArmExidx::DecodePrefix_10_11_0011() {
|
|
// 10110011 sssscccc: Pop VFP double precision registers D[ssss]-D[ssss+cccc] by FSTMFDX
|
|
uint8_t byte;
|
|
if (!GetByte(&byte)) {
|
|
return false;
|
|
}
|
|
|
|
if (log_type_ != ARM_LOG_NONE) {
|
|
uint8_t start_reg = byte >> 4;
|
|
uint8_t end_reg = start_reg + (byte & 0xf);
|
|
|
|
if (log_type_ == ARM_LOG_FULL) {
|
|
std::string msg = android::base::StringPrintf("pop {d%d", start_reg);
|
|
if (end_reg) {
|
|
msg += android::base::StringPrintf("-d%d", end_reg);
|
|
}
|
|
log(log_indent_, "%s}", msg.c_str());
|
|
} else {
|
|
log(log_indent_, "Unsupported DX register display");
|
|
}
|
|
|
|
if (log_skip_execution_) {
|
|
return true;
|
|
}
|
|
}
|
|
cfa_ += (byte & 0xf) * 8 + 12;
|
|
return true;
|
|
}
|
|
|
|
inline bool ArmExidx::DecodePrefix_10_11_01nn() {
|
|
// 101101nn: Spare
|
|
if (log_type_ != ARM_LOG_NONE) {
|
|
log(log_indent_, "Spare");
|
|
}
|
|
status_ = ARM_STATUS_SPARE;
|
|
return false;
|
|
}
|
|
|
|
inline bool ArmExidx::DecodePrefix_10_11_1nnn(uint8_t byte) {
|
|
CHECK((byte & ~0x07) == 0xb8);
|
|
|
|
// 10111nnn: Pop VFP double-precision registers D[8]-D[8+nnn] by FSTMFDX
|
|
if (log_type_ != ARM_LOG_NONE) {
|
|
if (log_type_ == ARM_LOG_FULL) {
|
|
uint8_t last_reg = (byte & 0x7);
|
|
std::string msg = "pop {d8";
|
|
if (last_reg) {
|
|
msg += android::base::StringPrintf("-d%d", last_reg + 8);
|
|
}
|
|
log(log_indent_, "%s}", msg.c_str());
|
|
} else {
|
|
log(log_indent_, "Unsupported DX register display");
|
|
}
|
|
|
|
if (log_skip_execution_) {
|
|
return true;
|
|
}
|
|
}
|
|
// Only update the cfa.
|
|
cfa_ += (byte & 0x7) * 8 + 12;
|
|
return true;
|
|
}
|
|
|
|
inline bool ArmExidx::DecodePrefix_10(uint8_t byte) {
|
|
CHECK((byte >> 6) == 0x2);
|
|
|
|
switch ((byte >> 4) & 0x3) {
|
|
case 0:
|
|
return DecodePrefix_10_00(byte);
|
|
case 1:
|
|
return DecodePrefix_10_01(byte);
|
|
case 2:
|
|
return DecodePrefix_10_10(byte);
|
|
default:
|
|
switch (byte & 0xf) {
|
|
case 0:
|
|
return DecodePrefix_10_11_0000();
|
|
case 1:
|
|
return DecodePrefix_10_11_0001();
|
|
case 2:
|
|
return DecodePrefix_10_11_0010();
|
|
case 3:
|
|
return DecodePrefix_10_11_0011();
|
|
default:
|
|
if (byte & 0x8) {
|
|
return DecodePrefix_10_11_1nnn(byte);
|
|
} else {
|
|
return DecodePrefix_10_11_01nn();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
inline bool ArmExidx::DecodePrefix_11_000(uint8_t byte) {
|
|
CHECK((byte & ~0x07) == 0xc0);
|
|
|
|
uint8_t bits = byte & 0x7;
|
|
if (bits == 6) {
|
|
if (!GetByte(&byte)) {
|
|
return false;
|
|
}
|
|
|
|
// 11000110 sssscccc: Intel Wireless MMX pop wR[ssss]-wR[ssss+cccc]
|
|
if (log_type_ != ARM_LOG_NONE) {
|
|
if (log_type_ == ARM_LOG_FULL) {
|
|
uint8_t start_reg = byte >> 4;
|
|
std::string msg = android::base::StringPrintf("pop {wR%d", start_reg);
|
|
uint8_t end_reg = byte & 0xf;
|
|
if (end_reg) {
|
|
msg += android::base::StringPrintf("-wR%d", start_reg + end_reg);
|
|
}
|
|
log(log_indent_, "%s}", msg.c_str());
|
|
} else {
|
|
log(log_indent_, "Unsupported wRX register display");
|
|
}
|
|
|
|
if (log_skip_execution_) {
|
|
return true;
|
|
}
|
|
}
|
|
// Only update the cfa.
|
|
cfa_ += (byte & 0xf) * 8 + 8;
|
|
} else if (bits == 7) {
|
|
if (!GetByte(&byte)) {
|
|
return false;
|
|
}
|
|
|
|
if (byte == 0) {
|
|
// 11000111 00000000: Spare
|
|
if (log_type_ != ARM_LOG_NONE) {
|
|
log(log_indent_, "Spare");
|
|
}
|
|
status_ = ARM_STATUS_SPARE;
|
|
return false;
|
|
} else if ((byte >> 4) == 0) {
|
|
// 11000111 0000iiii: Intel Wireless MMX pop wCGR registers {wCGR0,1,2,3}
|
|
if (log_type_ != ARM_LOG_NONE) {
|
|
if (log_type_ == ARM_LOG_FULL) {
|
|
bool add_comma = false;
|
|
std::string msg = "pop {";
|
|
for (size_t i = 0; i < 4; i++) {
|
|
if (byte & (1 << i)) {
|
|
if (add_comma) {
|
|
msg += ", ";
|
|
}
|
|
msg += android::base::StringPrintf("wCGR%zu", i);
|
|
add_comma = true;
|
|
}
|
|
}
|
|
log(log_indent_, "%s}", msg.c_str());
|
|
} else {
|
|
log(log_indent_, "Unsupported wCGR register display");
|
|
}
|
|
|
|
if (log_skip_execution_) {
|
|
return true;
|
|
}
|
|
}
|
|
// Only update the cfa.
|
|
cfa_ += __builtin_popcount(byte) * 4;
|
|
} else {
|
|
// 11000111 xxxxyyyy: Spare (xxxx != 0000)
|
|
if (log_type_ != ARM_LOG_NONE) {
|
|
log(log_indent_, "Spare");
|
|
}
|
|
status_ = ARM_STATUS_SPARE;
|
|
return false;
|
|
}
|
|
} else {
|
|
// 11000nnn: Intel Wireless MMX pop wR[10]-wR[10+nnn] (nnn != 6, 7)
|
|
if (log_type_ != ARM_LOG_NONE) {
|
|
if (log_type_ == ARM_LOG_FULL) {
|
|
std::string msg = "pop {wR10";
|
|
uint8_t nnn = byte & 0x7;
|
|
if (nnn) {
|
|
msg += android::base::StringPrintf("-wR%d", 10 + nnn);
|
|
}
|
|
log(log_indent_, "%s}", msg.c_str());
|
|
} else {
|
|
log(log_indent_, "Unsupported wRX register display");
|
|
}
|
|
|
|
if (log_skip_execution_) {
|
|
return true;
|
|
}
|
|
}
|
|
// Only update the cfa.
|
|
cfa_ += (byte & 0x7) * 8 + 8;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
inline bool ArmExidx::DecodePrefix_11_001(uint8_t byte) {
|
|
CHECK((byte & ~0x07) == 0xc8);
|
|
|
|
uint8_t bits = byte & 0x7;
|
|
if (bits == 0) {
|
|
// 11001000 sssscccc: Pop VFP double precision registers D[16+ssss]-D[16+ssss+cccc] by VPUSH
|
|
if (!GetByte(&byte)) {
|
|
return false;
|
|
}
|
|
|
|
if (log_type_ != ARM_LOG_NONE) {
|
|
if (log_type_ == ARM_LOG_FULL) {
|
|
uint8_t start_reg = byte >> 4;
|
|
std::string msg = android::base::StringPrintf("pop {d%d", 16 + start_reg);
|
|
uint8_t end_reg = byte & 0xf;
|
|
if (end_reg) {
|
|
msg += android::base::StringPrintf("-d%d", 16 + start_reg + end_reg);
|
|
}
|
|
log(log_indent_, "%s}", msg.c_str());
|
|
} else {
|
|
log(log_indent_, "Unsupported DX register display");
|
|
}
|
|
|
|
if (log_skip_execution_) {
|
|
return true;
|
|
}
|
|
}
|
|
// Only update the cfa.
|
|
cfa_ += (byte & 0xf) * 8 + 8;
|
|
} else if (bits == 1) {
|
|
// 11001001 sssscccc: Pop VFP double precision registers D[ssss]-D[ssss+cccc] by VPUSH
|
|
if (!GetByte(&byte)) {
|
|
return false;
|
|
}
|
|
|
|
if (log_type_ != ARM_LOG_NONE) {
|
|
if (log_type_ == ARM_LOG_FULL) {
|
|
uint8_t start_reg = byte >> 4;
|
|
std::string msg = android::base::StringPrintf("pop {d%d", start_reg);
|
|
uint8_t end_reg = byte & 0xf;
|
|
if (end_reg) {
|
|
msg += android::base::StringPrintf("-d%d", start_reg + end_reg);
|
|
}
|
|
log(log_indent_, "%s}", msg.c_str());
|
|
} else {
|
|
log(log_indent_, "Unsupported DX register display");
|
|
}
|
|
|
|
if (log_skip_execution_) {
|
|
return true;
|
|
}
|
|
}
|
|
// Only update the cfa.
|
|
cfa_ += (byte & 0xf) * 8 + 8;
|
|
} else {
|
|
// 11001yyy: Spare (yyy != 000, 001)
|
|
if (log_type_ != ARM_LOG_NONE) {
|
|
log(log_indent_, "Spare");
|
|
}
|
|
status_ = ARM_STATUS_SPARE;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
inline bool ArmExidx::DecodePrefix_11_010(uint8_t byte) {
|
|
CHECK((byte & ~0x07) == 0xd0);
|
|
|
|
// 11010nnn: Pop VFP double precision registers D[8]-D[8+nnn] by VPUSH
|
|
if (log_type_ != ARM_LOG_NONE) {
|
|
if (log_type_ == ARM_LOG_FULL) {
|
|
std::string msg = "pop {d8";
|
|
uint8_t end_reg = byte & 0x7;
|
|
if (end_reg) {
|
|
msg += android::base::StringPrintf("-d%d", 8 + end_reg);
|
|
}
|
|
log(log_indent_, "%s}", msg.c_str());
|
|
} else {
|
|
log(log_indent_, "Unsupported DX register display");
|
|
}
|
|
|
|
if (log_skip_execution_) {
|
|
return true;
|
|
}
|
|
}
|
|
cfa_ += (byte & 0x7) * 8 + 8;
|
|
return true;
|
|
}
|
|
|
|
inline bool ArmExidx::DecodePrefix_11(uint8_t byte) {
|
|
CHECK((byte >> 6) == 0x3);
|
|
|
|
switch ((byte >> 3) & 0x7) {
|
|
case 0:
|
|
return DecodePrefix_11_000(byte);
|
|
case 1:
|
|
return DecodePrefix_11_001(byte);
|
|
case 2:
|
|
return DecodePrefix_11_010(byte);
|
|
default:
|
|
// 11xxxyyy: Spare (xxx != 000, 001, 010)
|
|
if (log_type_ != ARM_LOG_NONE) {
|
|
log(log_indent_, "Spare");
|
|
}
|
|
status_ = ARM_STATUS_SPARE;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool ArmExidx::Decode() {
|
|
status_ = ARM_STATUS_NONE;
|
|
uint8_t byte;
|
|
if (!GetByte(&byte)) {
|
|
return false;
|
|
}
|
|
|
|
switch (byte >> 6) {
|
|
case 0:
|
|
// 00xxxxxx: vsp = vsp + (xxxxxxx << 2) + 4
|
|
if (log_type_ != ARM_LOG_NONE) {
|
|
int32_t cfa_offset = ((byte & 0x3f) << 2) + 4;
|
|
if (log_type_ == ARM_LOG_FULL) {
|
|
log(log_indent_, "vsp = vsp + %d", cfa_offset);
|
|
} else {
|
|
log_cfa_offset_ += cfa_offset;
|
|
}
|
|
AdjustRegisters(cfa_offset);
|
|
|
|
if (log_skip_execution_) {
|
|
break;
|
|
}
|
|
}
|
|
cfa_ += ((byte & 0x3f) << 2) + 4;
|
|
break;
|
|
case 1:
|
|
// 01xxxxxx: vsp = vsp - (xxxxxxx << 2) + 4
|
|
if (log_type_ != ARM_LOG_NONE) {
|
|
uint32_t cfa_offset = ((byte & 0x3f) << 2) + 4;
|
|
if (log_type_ == ARM_LOG_FULL) {
|
|
log(log_indent_, "vsp = vsp - %d", cfa_offset);
|
|
} else {
|
|
log_cfa_offset_ -= cfa_offset;
|
|
}
|
|
AdjustRegisters(-cfa_offset);
|
|
|
|
if (log_skip_execution_) {
|
|
break;
|
|
}
|
|
}
|
|
cfa_ -= ((byte & 0x3f) << 2) + 4;
|
|
break;
|
|
case 2:
|
|
return DecodePrefix_10(byte);
|
|
default:
|
|
return DecodePrefix_11(byte);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ArmExidx::Eval() {
|
|
pc_set_ = false;
|
|
while (Decode());
|
|
return status_ == ARM_STATUS_FINISH;
|
|
}
|
|
|
|
void ArmExidx::LogByReg() {
|
|
if (log_type_ != ARM_LOG_BY_REG) {
|
|
return;
|
|
}
|
|
|
|
uint8_t cfa_reg;
|
|
if (log_regs_.count(LOG_CFA_REG) == 0) {
|
|
cfa_reg = 13;
|
|
} else {
|
|
cfa_reg = log_regs_[LOG_CFA_REG];
|
|
}
|
|
|
|
if (log_cfa_offset_ != 0) {
|
|
char sign = (log_cfa_offset_ > 0) ? '+' : '-';
|
|
log(log_indent_, "cfa = r%zu %c %d", cfa_reg, sign, abs(log_cfa_offset_));
|
|
} else {
|
|
log(log_indent_, "cfa = r%zu", cfa_reg);
|
|
}
|
|
|
|
for (const auto& entry : log_regs_) {
|
|
if (entry.first >= LOG_CFA_REG) {
|
|
break;
|
|
}
|
|
if (entry.second == 0) {
|
|
log(log_indent_, "r%zu = [cfa]", entry.first);
|
|
} else {
|
|
char sign = (entry.second > 0) ? '-' : '+';
|
|
log(log_indent_, "r%zu = [cfa %c %d]", entry.first, sign, abs(entry.second));
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace unwindstack
|