Merge "New version of unwinder."
This commit is contained in:
commit
e69b1b9831
|
@ -0,0 +1,129 @@
|
|||
//
|
||||
// Copyright (C) 2017 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
cc_defaults {
|
||||
name: "libunwindstack_flags",
|
||||
|
||||
host_supported: true,
|
||||
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Werror",
|
||||
"-Wextra",
|
||||
],
|
||||
}
|
||||
|
||||
cc_defaults {
|
||||
name: "libunwindstack_common",
|
||||
defaults: ["libunwindstack_flags"],
|
||||
|
||||
srcs: [
|
||||
"ArmExidx.cpp",
|
||||
"Memory.cpp",
|
||||
"Log.cpp",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"liblog",
|
||||
],
|
||||
}
|
||||
|
||||
cc_library {
|
||||
name: "libunwindstack",
|
||||
defaults: ["libunwindstack_common"],
|
||||
}
|
||||
|
||||
cc_library {
|
||||
name: "libunwindstack_debug",
|
||||
defaults: ["libunwindstack_common"],
|
||||
|
||||
cflags: [
|
||||
"-UNDEBUG",
|
||||
"-O0",
|
||||
"-g",
|
||||
],
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Unit Tests
|
||||
//-------------------------------------------------------------------------
|
||||
cc_defaults {
|
||||
name: "libunwindstack_test_common",
|
||||
defaults: ["libunwindstack_flags"],
|
||||
|
||||
srcs: [
|
||||
"tests/ArmExidxDecodeTest.cpp",
|
||||
"tests/ArmExidxExtractTest.cpp",
|
||||
"tests/LogFake.cpp",
|
||||
"tests/MemoryFake.cpp",
|
||||
"tests/MemoryFileTest.cpp",
|
||||
"tests/MemoryLocalTest.cpp",
|
||||
"tests/MemoryRangeTest.cpp",
|
||||
"tests/MemoryRemoteTest.cpp",
|
||||
"tests/RegsTest.cpp",
|
||||
],
|
||||
|
||||
cflags: [
|
||||
"-O0",
|
||||
"-g",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"liblog",
|
||||
],
|
||||
|
||||
multilib: {
|
||||
lib32: {
|
||||
suffix: "32",
|
||||
},
|
||||
lib64: {
|
||||
suffix: "64",
|
||||
},
|
||||
},
|
||||
|
||||
target: {
|
||||
darwin: {
|
||||
enabled: false,
|
||||
},
|
||||
linux: {
|
||||
host_ldlibs: [
|
||||
"-lrt",
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// These unit tests run against the shared library.
|
||||
cc_test {
|
||||
name: "libunwindstack_test",
|
||||
defaults: ["libunwindstack_test_common"],
|
||||
|
||||
shared_libs: [
|
||||
"libunwindstack",
|
||||
],
|
||||
}
|
||||
|
||||
// These unit tests run against the static debug library.
|
||||
cc_test {
|
||||
name: "libunwindstack_test_debug",
|
||||
defaults: ["libunwindstack_test_common"],
|
||||
|
||||
static_libs: [
|
||||
"libunwindstack_debug",
|
||||
],
|
||||
}
|
|
@ -0,0 +1,680 @@
|
|||
/*
|
||||
* 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 <assert.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <deque>
|
||||
#include <string>
|
||||
|
||||
#include <android-base/stringprintf.h>
|
||||
|
||||
#include "ArmExidx.h"
|
||||
#include "Log.h"
|
||||
#include "Machine.h"
|
||||
|
||||
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;
|
||||
return false;
|
||||
}
|
||||
if (data == 1) {
|
||||
// This is a CANT UNWIND entry.
|
||||
status_ = ARM_STATUS_NO_UNWIND;
|
||||
if (log_) {
|
||||
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_) {
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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_) {
|
||||
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) {
|
||||
assert((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_) {
|
||||
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}
|
||||
if (log_) {
|
||||
bool add_comma = false;
|
||||
std::string msg = "pop {";
|
||||
for (size_t i = 0; i < 12; i++) {
|
||||
if (registers & (1 << i)) {
|
||||
if (add_comma) {
|
||||
msg += ", ";
|
||||
}
|
||||
msg += android::base::StringPrintf("r%zu", i + 4);
|
||||
add_comma = true;
|
||||
}
|
||||
}
|
||||
log(log_indent_, "%s}", msg.c_str());
|
||||
if (log_skip_execution_) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
registers <<= 4;
|
||||
for (size_t reg = 4; reg < 16; reg++) {
|
||||
if (registers & (1 << reg)) {
|
||||
if (!process_memory_->Read32(cfa_, &(*regs_)[reg])) {
|
||||
status_ = ARM_STATUS_READ_FAILED;
|
||||
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];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool ArmExidx::DecodePrefix_10_01(uint8_t byte) {
|
||||
assert((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_) {
|
||||
log(log_indent_, "[Reserved]");
|
||||
}
|
||||
status_ = ARM_STATUS_RESERVED;
|
||||
return false;
|
||||
}
|
||||
// 1001nnnn: Set vsp = r[nnnn] (nnnn != 13, 15)
|
||||
if (log_) {
|
||||
log(log_indent_, "vsp = r%d", 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) {
|
||||
assert((byte >> 4) == 0xa);
|
||||
|
||||
// 10100nnn: Pop r4-r[4+nnn]
|
||||
// 10101nnn: Pop r4-r[4+nnn], r14
|
||||
if (log_) {
|
||||
std::string msg = "pop {r4";
|
||||
uint8_t end_reg = byte & 0x7;
|
||||
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());
|
||||
}
|
||||
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;
|
||||
return false;
|
||||
}
|
||||
cfa_ += 4;
|
||||
}
|
||||
if (byte & 0x8) {
|
||||
if (!process_memory_->Read32(cfa_, &(*regs_)[ARM_REG_R14])) {
|
||||
status_ = ARM_STATUS_READ_FAILED;
|
||||
return false;
|
||||
}
|
||||
cfa_ += 4;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool ArmExidx::DecodePrefix_10_11_0000() {
|
||||
// 10110000: Finish
|
||||
if (log_) {
|
||||
log(log_indent_, "finish");
|
||||
if (log_skip_execution_) {
|
||||
status_ = ARM_STATUS_FINISH;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!(*regs_)[ARM_REG_PC]) {
|
||||
(*regs_)[ARM_REG_PC] = (*regs_)[ARM_REG_LR];
|
||||
}
|
||||
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_) {
|
||||
log(log_indent_, "Spare");
|
||||
}
|
||||
status_ = ARM_STATUS_SPARE;
|
||||
return false;
|
||||
}
|
||||
if (byte >> 4) {
|
||||
// 10110001 xxxxyyyy: Spare (xxxx != 0000)
|
||||
if (log_) {
|
||||
log(log_indent_, "Spare");
|
||||
}
|
||||
status_ = ARM_STATUS_SPARE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 10110001 0000iiii: Pop integer registers under mask {r3, r2, r1, r0}
|
||||
if (log_) {
|
||||
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());
|
||||
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;
|
||||
return false;
|
||||
}
|
||||
cfa_ += 4;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
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_) {
|
||||
log(log_indent_, "vsp = vsp + %d", 0x204 + result);
|
||||
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_) {
|
||||
uint8_t start_reg = byte >> 4;
|
||||
std::string msg = android::base::StringPrintf("pop {d%d", start_reg);
|
||||
uint8_t end_reg = start_reg + (byte & 0xf);
|
||||
if (end_reg) {
|
||||
msg += android::base::StringPrintf("-d%d", end_reg);
|
||||
}
|
||||
log(log_indent_, "%s}", msg.c_str());
|
||||
if (log_skip_execution_) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
cfa_ += (byte & 0xf) * 8 + 12;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool ArmExidx::DecodePrefix_10_11_01nn() {
|
||||
// 101101nn: Spare
|
||||
if (log_) {
|
||||
log(log_indent_, "Spare");
|
||||
}
|
||||
status_ = ARM_STATUS_SPARE;
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool ArmExidx::DecodePrefix_10_11_1nnn(uint8_t byte) {
|
||||
assert((byte & ~0x07) == 0xb8);
|
||||
|
||||
// 10111nnn: Pop VFP double-precision registers D[8]-D[8+nnn] by FSTMFDX
|
||||
if (log_) {
|
||||
std::string msg = "pop {d8";
|
||||
uint8_t last_reg = (byte & 0x7);
|
||||
if (last_reg) {
|
||||
msg += android::base::StringPrintf("-d%d", last_reg + 8);
|
||||
}
|
||||
log(log_indent_, "%s}", msg.c_str());
|
||||
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) {
|
||||
assert((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) {
|
||||
assert((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_) {
|
||||
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());
|
||||
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_) {
|
||||
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_) {
|
||||
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());
|
||||
}
|
||||
// Only update the cfa.
|
||||
cfa_ += __builtin_popcount(byte) * 4;
|
||||
} else {
|
||||
// 11000111 xxxxyyyy: Spare (xxxx != 0000)
|
||||
if (log_) {
|
||||
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_) {
|
||||
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());
|
||||
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) {
|
||||
assert((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_) {
|
||||
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());
|
||||
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_) {
|
||||
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());
|
||||
if (log_skip_execution_) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Only update the cfa.
|
||||
cfa_ += (byte & 0xf) * 8 + 8;
|
||||
} else {
|
||||
// 11001yyy: Spare (yyy != 000, 001)
|
||||
if (log_) {
|
||||
log(log_indent_, "Spare");
|
||||
}
|
||||
status_ = ARM_STATUS_SPARE;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool ArmExidx::DecodePrefix_11_010(uint8_t byte) {
|
||||
assert((byte & ~0x07) == 0xd0);
|
||||
|
||||
// 11010nnn: Pop VFP double precision registers D[8]-D[8+nnn] by VPUSH
|
||||
if (log_) {
|
||||
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());
|
||||
if (log_skip_execution_) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
cfa_ += (byte & 0x7) * 8 + 8;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool ArmExidx::DecodePrefix_11(uint8_t byte) {
|
||||
assert((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_) {
|
||||
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_) {
|
||||
log(log_indent_, "vsp = vsp + %d", ((byte & 0x3f) << 2) + 4);
|
||||
if (log_skip_execution_) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
cfa_ += ((byte & 0x3f) << 2) + 4;
|
||||
break;
|
||||
case 1:
|
||||
// 01xxxxxx: vsp = vsp - (xxxxxxx << 2) + 4
|
||||
if (log_) {
|
||||
log(log_indent_, "vsp = vsp - %d", ((byte & 0x3f) << 2) + 4);
|
||||
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() {
|
||||
while (Decode());
|
||||
return status_ == ARM_STATUS_FINISH;
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* 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_ARM_EXIDX_H
|
||||
#define _LIBUNWINDSTACK_ARM_EXIDX_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <deque>
|
||||
|
||||
#include "Memory.h"
|
||||
#include "Regs.h"
|
||||
|
||||
enum ArmStatus : size_t {
|
||||
ARM_STATUS_NONE = 0,
|
||||
ARM_STATUS_NO_UNWIND,
|
||||
ARM_STATUS_FINISH,
|
||||
ARM_STATUS_RESERVED,
|
||||
ARM_STATUS_SPARE,
|
||||
ARM_STATUS_TRUNCATED,
|
||||
ARM_STATUS_READ_FAILED,
|
||||
ARM_STATUS_MALFORMED,
|
||||
ARM_STATUS_INVALID_ALIGNMENT,
|
||||
ARM_STATUS_INVALID_PERSONALITY,
|
||||
};
|
||||
|
||||
enum ArmOp : uint8_t {
|
||||
ARM_OP_FINISH = 0xb0,
|
||||
};
|
||||
|
||||
class ArmExidx {
|
||||
public:
|
||||
ArmExidx(Regs32* regs, Memory* elf_memory, Memory* process_memory)
|
||||
: regs_(regs), elf_memory_(elf_memory), process_memory_(process_memory) {}
|
||||
virtual ~ArmExidx() {}
|
||||
|
||||
void LogRawData();
|
||||
|
||||
bool ExtractEntryData(uint32_t entry_offset);
|
||||
|
||||
bool Eval();
|
||||
|
||||
bool Decode();
|
||||
|
||||
std::deque<uint8_t>* data() { return &data_; }
|
||||
|
||||
ArmStatus status() { return status_; }
|
||||
|
||||
Regs32* regs() { return regs_; }
|
||||
|
||||
uint32_t cfa() { return cfa_; }
|
||||
void set_cfa(uint32_t cfa) { cfa_ = cfa; }
|
||||
|
||||
void set_log(bool log) { log_ = log; }
|
||||
void set_log_skip_execution(bool skip_execution) { log_skip_execution_ = skip_execution; }
|
||||
void set_log_indent(uint8_t indent) { log_indent_ = indent; }
|
||||
|
||||
private:
|
||||
bool GetByte(uint8_t* byte);
|
||||
|
||||
bool DecodePrefix_10_00(uint8_t byte);
|
||||
bool DecodePrefix_10_01(uint8_t byte);
|
||||
bool DecodePrefix_10_10(uint8_t byte);
|
||||
bool DecodePrefix_10_11_0000();
|
||||
bool DecodePrefix_10_11_0001();
|
||||
bool DecodePrefix_10_11_0010();
|
||||
bool DecodePrefix_10_11_0011();
|
||||
bool DecodePrefix_10_11_01nn();
|
||||
bool DecodePrefix_10_11_1nnn(uint8_t byte);
|
||||
bool DecodePrefix_10(uint8_t byte);
|
||||
|
||||
bool DecodePrefix_11_000(uint8_t byte);
|
||||
bool DecodePrefix_11_001(uint8_t byte);
|
||||
bool DecodePrefix_11_010(uint8_t byte);
|
||||
bool DecodePrefix_11(uint8_t byte);
|
||||
|
||||
Regs32* regs_ = nullptr;
|
||||
uint32_t cfa_ = 0;
|
||||
std::deque<uint8_t> data_;
|
||||
ArmStatus status_ = ARM_STATUS_NONE;
|
||||
|
||||
Memory* elf_memory_;
|
||||
Memory* process_memory_;
|
||||
|
||||
bool log_ = false;
|
||||
uint8_t log_indent_ = 0;
|
||||
bool log_skip_execution_ = false;
|
||||
};
|
||||
|
||||
#endif // _LIBUNWINDSTACK_ARM_EXIDX_H
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* 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 <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#define LOG_TAG "unwind"
|
||||
#include <log/log.h>
|
||||
|
||||
#include <android-base/stringprintf.h>
|
||||
|
||||
#include "Log.h"
|
||||
|
||||
static bool g_print_to_stdout = false;
|
||||
|
||||
void log_to_stdout(bool enable) {
|
||||
g_print_to_stdout = enable;
|
||||
}
|
||||
|
||||
// Send the data to the log.
|
||||
void log(uint8_t indent, const char* format, ...) {
|
||||
std::string real_format;
|
||||
if (indent > 0) {
|
||||
real_format = android::base::StringPrintf("%*s%s", 2 * indent, " ", format);
|
||||
} else {
|
||||
real_format = format;
|
||||
}
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
if (g_print_to_stdout) {
|
||||
real_format += '\n';
|
||||
vprintf(real_format.c_str(), args);
|
||||
} else {
|
||||
LOG_PRI_VA(ANDROID_LOG_INFO, LOG_TAG, real_format.c_str(), args);
|
||||
}
|
||||
va_end(args);
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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_LOG_H
|
||||
#define _LIBUNWINDSTACK_LOG_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void log_to_stdout(bool enable);
|
||||
void log(uint8_t indent, const char* format, ...);
|
||||
|
||||
#endif // _LIBUNWINDSTACK_LOG_H
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* 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_MACHINE_H
|
||||
#define _LIBUNWINDSTACK_MACHINE_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
class Regs;
|
||||
|
||||
enum ArmReg : uint16_t {
|
||||
ARM_REG_R0 = 0,
|
||||
ARM_REG_R1,
|
||||
ARM_REG_R2,
|
||||
ARM_REG_R3,
|
||||
ARM_REG_R4,
|
||||
ARM_REG_R5,
|
||||
ARM_REG_R6,
|
||||
ARM_REG_R7,
|
||||
ARM_REG_R8,
|
||||
ARM_REG_R9,
|
||||
ARM_REG_R10,
|
||||
ARM_REG_R11,
|
||||
ARM_REG_R12,
|
||||
ARM_REG_R13,
|
||||
ARM_REG_R14,
|
||||
ARM_REG_R15,
|
||||
ARM_REG_LAST,
|
||||
|
||||
ARM_REG_SP = ARM_REG_R13,
|
||||
ARM_REG_LR = ARM_REG_R14,
|
||||
ARM_REG_PC = ARM_REG_R15,
|
||||
};
|
||||
|
||||
enum Arm64Reg : uint16_t {
|
||||
ARM64_REG_R0 = 0,
|
||||
ARM64_REG_R1,
|
||||
ARM64_REG_R2,
|
||||
ARM64_REG_R3,
|
||||
ARM64_REG_R4,
|
||||
ARM64_REG_R5,
|
||||
ARM64_REG_R6,
|
||||
ARM64_REG_R7,
|
||||
ARM64_REG_R8,
|
||||
ARM64_REG_R9,
|
||||
ARM64_REG_R10,
|
||||
ARM64_REG_R11,
|
||||
ARM64_REG_R12,
|
||||
ARM64_REG_R13,
|
||||
ARM64_REG_R14,
|
||||
ARM64_REG_R15,
|
||||
ARM64_REG_R16,
|
||||
ARM64_REG_R17,
|
||||
ARM64_REG_R18,
|
||||
ARM64_REG_R19,
|
||||
ARM64_REG_R20,
|
||||
ARM64_REG_R21,
|
||||
ARM64_REG_R22,
|
||||
ARM64_REG_R23,
|
||||
ARM64_REG_R24,
|
||||
ARM64_REG_R25,
|
||||
ARM64_REG_R26,
|
||||
ARM64_REG_R27,
|
||||
ARM64_REG_R28,
|
||||
ARM64_REG_R29,
|
||||
ARM64_REG_R30,
|
||||
ARM64_REG_R31,
|
||||
ARM64_REG_PC,
|
||||
ARM64_REG_LAST,
|
||||
|
||||
ARM64_REG_SP = ARM64_REG_R31,
|
||||
ARM64_REG_LR = ARM64_REG_R30,
|
||||
};
|
||||
|
||||
enum X86Reg : uint16_t {
|
||||
X86_REG_EAX = 0,
|
||||
X86_REG_ECX,
|
||||
X86_REG_EDX,
|
||||
X86_REG_EBX,
|
||||
X86_REG_ESP,
|
||||
X86_REG_EBP,
|
||||
X86_REG_ESI,
|
||||
X86_REG_EDI,
|
||||
X86_REG_EIP,
|
||||
X86_REG_EFL,
|
||||
X86_REG_CS,
|
||||
X86_REG_SS,
|
||||
X86_REG_DS,
|
||||
X86_REG_ES,
|
||||
X86_REG_FS,
|
||||
X86_REG_GS,
|
||||
X86_REG_LAST,
|
||||
|
||||
X86_REG_SP = X86_REG_ESP,
|
||||
X86_REG_PC = X86_REG_EIP,
|
||||
};
|
||||
|
||||
enum X86_64Reg : uint16_t {
|
||||
X86_64_REG_RAX = 0,
|
||||
X86_64_REG_RDX,
|
||||
X86_64_REG_RCX,
|
||||
X86_64_REG_RBX,
|
||||
X86_64_REG_RSI,
|
||||
X86_64_REG_RDI,
|
||||
X86_64_REG_RBP,
|
||||
X86_64_REG_RSP,
|
||||
X86_64_REG_R8,
|
||||
X86_64_REG_R9,
|
||||
X86_64_REG_R10,
|
||||
X86_64_REG_R11,
|
||||
X86_64_REG_R12,
|
||||
X86_64_REG_R13,
|
||||
X86_64_REG_R14,
|
||||
X86_64_REG_R15,
|
||||
X86_64_REG_RIP,
|
||||
X86_64_REG_LAST,
|
||||
|
||||
X86_64_REG_SP = X86_64_REG_RSP,
|
||||
X86_64_REG_PC = X86_64_REG_RIP,
|
||||
};
|
||||
|
||||
#endif // _LIBUNWINDSTACK_MACHINE_H
|
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
* 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 <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/uio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#include <android-base/unique_fd.h>
|
||||
|
||||
#include "Memory.h"
|
||||
|
||||
bool Memory::ReadString(uint64_t addr, std::string* string, uint64_t max_read) {
|
||||
string->clear();
|
||||
uint64_t bytes_read = 0;
|
||||
while (bytes_read < max_read) {
|
||||
uint8_t value;
|
||||
if (!Read(addr, &value, sizeof(value))) {
|
||||
return false;
|
||||
}
|
||||
if (value == '\0') {
|
||||
return true;
|
||||
}
|
||||
string->push_back(value);
|
||||
addr++;
|
||||
bytes_read++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
MemoryFileAtOffset::~MemoryFileAtOffset() {
|
||||
if (data_) {
|
||||
munmap(&data_[-offset_], size_ + offset_);
|
||||
data_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool MemoryFileAtOffset::Init(const std::string& file, uint64_t offset) {
|
||||
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(file.c_str(), O_RDONLY | O_CLOEXEC)));
|
||||
if (fd == -1) {
|
||||
return false;
|
||||
}
|
||||
struct stat buf;
|
||||
if (fstat(fd, &buf) == -1) {
|
||||
return false;
|
||||
}
|
||||
if (offset >= static_cast<uint64_t>(buf.st_size)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
offset_ = offset & (getpagesize() - 1);
|
||||
uint64_t aligned_offset = offset & ~(getpagesize() - 1);
|
||||
size_ = buf.st_size - aligned_offset;
|
||||
void* map = mmap(nullptr, size_, PROT_READ, MAP_PRIVATE, fd, aligned_offset);
|
||||
if (map == MAP_FAILED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
data_ = &reinterpret_cast<uint8_t*>(map)[offset_];
|
||||
size_ -= offset_;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MemoryFileAtOffset::Read(uint64_t addr, void* dst, size_t size) {
|
||||
if (addr + size > size_) {
|
||||
return false;
|
||||
}
|
||||
memcpy(dst, &data_[addr], size);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool PtraceRead(pid_t pid, uint64_t addr, long* value) {
|
||||
// ptrace() returns -1 and sets errno when the operation fails.
|
||||
// To disambiguate -1 from a valid result, we clear errno beforehand.
|
||||
errno = 0;
|
||||
*value = ptrace(PTRACE_PEEKTEXT, pid, reinterpret_cast<void*>(addr), nullptr);
|
||||
if (*value == -1 && errno) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MemoryRemote::Read(uint64_t addr, void* dst, size_t bytes) {
|
||||
size_t bytes_read = 0;
|
||||
long data;
|
||||
size_t align_bytes = addr & (sizeof(long) - 1);
|
||||
if (align_bytes != 0) {
|
||||
if (!PtraceRead(pid_, addr & ~(sizeof(long) - 1), &data)) {
|
||||
return false;
|
||||
}
|
||||
size_t copy_bytes = std::min(sizeof(long) - align_bytes, bytes);
|
||||
memcpy(dst, reinterpret_cast<uint8_t*>(&data) + align_bytes, copy_bytes);
|
||||
addr += copy_bytes;
|
||||
dst = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(dst) + copy_bytes);
|
||||
bytes -= copy_bytes;
|
||||
bytes_read += copy_bytes;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < bytes / sizeof(long); i++) {
|
||||
if (!PtraceRead(pid_, addr, &data)) {
|
||||
return false;
|
||||
}
|
||||
memcpy(dst, &data, sizeof(long));
|
||||
dst = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(dst) + sizeof(long));
|
||||
addr += sizeof(long);
|
||||
bytes_read += sizeof(long);
|
||||
}
|
||||
|
||||
size_t left_over = bytes & (sizeof(long) - 1);
|
||||
if (left_over) {
|
||||
if (!PtraceRead(pid_, addr, &data)) {
|
||||
return false;
|
||||
}
|
||||
memcpy(dst, &data, left_over);
|
||||
bytes_read += left_over;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MemoryLocal::Read(uint64_t addr, void* dst, size_t size) {
|
||||
// The process_vm_readv call does will not always work on remote
|
||||
// processes, so only use it for reads from the current pid.
|
||||
// Use this method to avoid crashes if an address is invalid since
|
||||
// unwind data could try to access any part of the address space.
|
||||
struct iovec local_io;
|
||||
local_io.iov_base = dst;
|
||||
local_io.iov_len = size;
|
||||
|
||||
struct iovec remote_io;
|
||||
remote_io.iov_base = reinterpret_cast<void*>(static_cast<uintptr_t>(addr));
|
||||
remote_io.iov_len = size;
|
||||
|
||||
ssize_t bytes_read = process_vm_readv(getpid(), &local_io, 1, &remote_io, 1, 0);
|
||||
if (bytes_read == -1) {
|
||||
return false;
|
||||
}
|
||||
return static_cast<size_t>(bytes_read) == size;
|
||||
}
|
||||
|
||||
bool MemoryOffline::Init(const std::string& file, uint64_t offset) {
|
||||
if (!MemoryFileAtOffset::Init(file, offset)) {
|
||||
return false;
|
||||
}
|
||||
// The first uint64_t value is the start of memory.
|
||||
if (!MemoryFileAtOffset::Read(0, &start_, sizeof(start_))) {
|
||||
return false;
|
||||
}
|
||||
// Subtract the first 64 bit value from the total size.
|
||||
size_ -= sizeof(start_);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MemoryOffline::Read(uint64_t addr, void* dst, size_t size) {
|
||||
if (addr < start_ || addr + size > start_ + offset_ + size_) {
|
||||
return false;
|
||||
}
|
||||
memcpy(dst, &data_[addr + offset_ - start_ + sizeof(start_)], size);
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* 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_MEMORY_H
|
||||
#define _LIBUNWINDSTACK_MEMORY_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <android-base/unique_fd.h>
|
||||
|
||||
constexpr bool kMemoryStatsEnabled = true;
|
||||
|
||||
class Memory {
|
||||
public:
|
||||
Memory() = default;
|
||||
virtual ~Memory() = default;
|
||||
|
||||
virtual bool ReadString(uint64_t addr, std::string* string, uint64_t max_read = UINT64_MAX);
|
||||
|
||||
virtual bool Read(uint64_t addr, void* dst, size_t size) = 0;
|
||||
|
||||
inline bool Read(uint64_t addr, void* start, void* field, size_t size) {
|
||||
return Read(addr + reinterpret_cast<uintptr_t>(field) - reinterpret_cast<uintptr_t>(start),
|
||||
field, size);
|
||||
}
|
||||
|
||||
inline bool Read32(uint64_t addr, uint32_t* dst) {
|
||||
return Read(addr, dst, sizeof(uint32_t));
|
||||
}
|
||||
|
||||
inline bool Read64(uint64_t addr, uint64_t* dst) {
|
||||
return Read(addr, dst, sizeof(uint64_t));
|
||||
}
|
||||
};
|
||||
|
||||
class MemoryFileAtOffset : public Memory {
|
||||
public:
|
||||
MemoryFileAtOffset() = default;
|
||||
virtual ~MemoryFileAtOffset();
|
||||
|
||||
bool Init(const std::string& file, uint64_t offset);
|
||||
|
||||
bool Read(uint64_t addr, void* dst, size_t size) override;
|
||||
|
||||
protected:
|
||||
size_t size_ = 0;
|
||||
size_t offset_ = 0;
|
||||
uint8_t* data_ = nullptr;
|
||||
};
|
||||
|
||||
class MemoryOffline : public MemoryFileAtOffset {
|
||||
public:
|
||||
MemoryOffline() = default;
|
||||
virtual ~MemoryOffline() = default;
|
||||
|
||||
bool Init(const std::string& file, uint64_t offset);
|
||||
|
||||
bool Read(uint64_t addr, void* dst, size_t size) override;
|
||||
|
||||
private:
|
||||
uint64_t start_;
|
||||
};
|
||||
|
||||
class MemoryRemote : public Memory {
|
||||
public:
|
||||
MemoryRemote(pid_t pid) : pid_(pid) {}
|
||||
virtual ~MemoryRemote() = default;
|
||||
|
||||
bool Read(uint64_t addr, void* dst, size_t size) override;
|
||||
|
||||
pid_t pid() { return pid_; }
|
||||
|
||||
private:
|
||||
pid_t pid_;
|
||||
};
|
||||
|
||||
class MemoryLocal : public Memory {
|
||||
public:
|
||||
MemoryLocal() = default;
|
||||
virtual ~MemoryLocal() = default;
|
||||
|
||||
bool Read(uint64_t addr, void* dst, size_t size) override;
|
||||
};
|
||||
|
||||
class MemoryRange : public Memory {
|
||||
public:
|
||||
MemoryRange(Memory* memory, uint64_t begin, uint64_t end)
|
||||
: memory_(memory), begin_(begin), length_(end - begin_) {}
|
||||
virtual ~MemoryRange() { delete memory_; }
|
||||
|
||||
inline bool Read(uint64_t addr, void* dst, size_t size) override {
|
||||
if (addr + size <= length_) {
|
||||
return memory_->Read(addr + begin_, dst, size);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
Memory* memory_;
|
||||
uint64_t begin_;
|
||||
uint64_t length_;
|
||||
};
|
||||
|
||||
#endif // _LIBUNWINDSTACK_MEMORY_H
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* 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_REGS_H
|
||||
#define _LIBUNWINDSTACK_REGS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
class Regs {
|
||||
public:
|
||||
Regs(uint16_t pc_reg, uint16_t sp_reg, uint16_t total_regs)
|
||||
: pc_reg_(pc_reg), sp_reg_(sp_reg), total_regs_(total_regs) {
|
||||
}
|
||||
virtual ~Regs() = default;
|
||||
|
||||
uint16_t pc_reg() { return pc_reg_; }
|
||||
uint16_t sp_reg() { return sp_reg_; }
|
||||
uint16_t total_regs() { return total_regs_; }
|
||||
|
||||
virtual void* raw_data() = 0;
|
||||
virtual uint64_t pc() = 0;
|
||||
virtual uint64_t sp() = 0;
|
||||
|
||||
protected:
|
||||
uint16_t pc_reg_;
|
||||
uint16_t sp_reg_;
|
||||
uint16_t total_regs_;
|
||||
};
|
||||
|
||||
template <typename AddressType>
|
||||
class RegsTmpl : public Regs {
|
||||
public:
|
||||
RegsTmpl(uint16_t pc_reg, uint16_t sp_reg, uint16_t total_regs)
|
||||
: Regs(pc_reg, sp_reg, total_regs), regs_(total_regs) {}
|
||||
virtual ~RegsTmpl() = default;
|
||||
|
||||
uint64_t pc() override { return regs_[pc_reg_]; }
|
||||
uint64_t sp() override { return regs_[sp_reg_]; }
|
||||
|
||||
inline AddressType& operator[](size_t reg) { return regs_[reg]; }
|
||||
|
||||
void* raw_data() override { return regs_.data(); }
|
||||
|
||||
private:
|
||||
std::vector<AddressType> regs_;
|
||||
};
|
||||
|
||||
class Regs32 : public RegsTmpl<uint32_t> {
|
||||
public:
|
||||
Regs32(uint16_t pc_reg, uint16_t sp_reg, uint16_t total_regs)
|
||||
: RegsTmpl(pc_reg, sp_reg, total_regs) {}
|
||||
virtual ~Regs32() = default;
|
||||
};
|
||||
|
||||
class Regs64 : public RegsTmpl<uint64_t> {
|
||||
public:
|
||||
Regs64(uint16_t pc_reg, uint16_t sp_reg, uint16_t total_regs)
|
||||
: RegsTmpl(pc_reg, sp_reg, total_regs) {}
|
||||
virtual ~Regs64() = default;
|
||||
};
|
||||
|
||||
#endif // _LIBUNWINDSTACK_REGS_H
|
|
@ -0,0 +1,992 @@
|
|||
/*
|
||||
* 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 <ios>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "ArmExidx.h"
|
||||
#include "Log.h"
|
||||
|
||||
#include "LogFake.h"
|
||||
#include "MemoryFake.h"
|
||||
|
||||
class ArmExidxDecodeTest : public ::testing::TestWithParam<std::string> {
|
||||
protected:
|
||||
void Init(Memory* process_memory = nullptr) {
|
||||
TearDown();
|
||||
|
||||
if (process_memory == nullptr) {
|
||||
process_memory = &process_memory_;
|
||||
}
|
||||
|
||||
regs32_.reset(new Regs32(0, 1, 32));
|
||||
for (size_t i = 0; i < 32; i++) {
|
||||
(*regs32_)[i] = 0;
|
||||
}
|
||||
|
||||
exidx_.reset(new ArmExidx(regs32_.get(), &elf_memory_, process_memory));
|
||||
if (log_) {
|
||||
exidx_->set_log(true);
|
||||
exidx_->set_log_indent(0);
|
||||
exidx_->set_log_skip_execution(false);
|
||||
}
|
||||
data_ = exidx_->data();
|
||||
exidx_->set_cfa(0x10000);
|
||||
}
|
||||
|
||||
void SetUp() override {
|
||||
if (GetParam() != "no_logging") {
|
||||
log_ = false;
|
||||
} else {
|
||||
log_ = true;
|
||||
}
|
||||
ResetLogs();
|
||||
elf_memory_.Clear();
|
||||
process_memory_.Clear();
|
||||
Init();
|
||||
}
|
||||
|
||||
std::unique_ptr<ArmExidx> exidx_;
|
||||
std::unique_ptr<Regs32> regs32_;
|
||||
std::deque<uint8_t>* data_;
|
||||
|
||||
MemoryFake elf_memory_;
|
||||
MemoryFake process_memory_;
|
||||
bool log_;
|
||||
};
|
||||
|
||||
TEST_P(ArmExidxDecodeTest, vsp_incr) {
|
||||
// 00xxxxxx: vsp = vsp + (xxxxxx << 2) + 4
|
||||
data_->push_back(0x00);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind vsp = vsp + 4\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x10004U, exidx_->cfa());
|
||||
|
||||
ResetLogs();
|
||||
data_->clear();
|
||||
data_->push_back(0x01);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind vsp = vsp + 8\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x1000cU, exidx_->cfa());
|
||||
|
||||
ResetLogs();
|
||||
data_->clear();
|
||||
data_->push_back(0x3f);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind vsp = vsp + 256\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x1010cU, exidx_->cfa());
|
||||
}
|
||||
|
||||
TEST_P(ArmExidxDecodeTest, vsp_decr) {
|
||||
// 01xxxxxx: vsp = vsp - (xxxxxx << 2) + 4
|
||||
data_->push_back(0x40);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind vsp = vsp - 4\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0xfffcU, exidx_->cfa());
|
||||
|
||||
ResetLogs();
|
||||
data_->clear();
|
||||
data_->push_back(0x41);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind vsp = vsp - 8\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0xfff4U, exidx_->cfa());
|
||||
|
||||
ResetLogs();
|
||||
data_->clear();
|
||||
data_->push_back(0x7f);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind vsp = vsp - 256\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0xfef4U, exidx_->cfa());
|
||||
}
|
||||
|
||||
TEST_P(ArmExidxDecodeTest, refuse_unwind) {
|
||||
// 10000000 00000000: Refuse to unwind
|
||||
data_->push_back(0x80);
|
||||
data_->push_back(0x00);
|
||||
ASSERT_FALSE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind Refuse to unwind\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(ARM_STATUS_NO_UNWIND, exidx_->status());
|
||||
}
|
||||
|
||||
TEST_P(ArmExidxDecodeTest, pop_up_to_12) {
|
||||
// 1000iiii iiiiiiii: Pop up to 12 integer registers
|
||||
data_->push_back(0x80);
|
||||
data_->push_back(0x01);
|
||||
process_memory_.SetData(0x10000, 0x10);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind pop {r4}\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x10004U, exidx_->cfa());
|
||||
ASSERT_EQ(0x10U, (*exidx_->regs())[4]);
|
||||
|
||||
ResetLogs();
|
||||
data_->push_back(0x8f);
|
||||
data_->push_back(0xff);
|
||||
for (size_t i = 0; i < 12; i++) {
|
||||
process_memory_.SetData(0x10004 + i * 4, i + 0x20);
|
||||
}
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind pop {r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15}\n",
|
||||
GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
// Popping r13 results in a modified cfa.
|
||||
ASSERT_EQ(0x29U, exidx_->cfa());
|
||||
|
||||
ASSERT_EQ(0x20U, (*exidx_->regs())[4]);
|
||||
ASSERT_EQ(0x21U, (*exidx_->regs())[5]);
|
||||
ASSERT_EQ(0x22U, (*exidx_->regs())[6]);
|
||||
ASSERT_EQ(0x23U, (*exidx_->regs())[7]);
|
||||
ASSERT_EQ(0x24U, (*exidx_->regs())[8]);
|
||||
ASSERT_EQ(0x25U, (*exidx_->regs())[9]);
|
||||
ASSERT_EQ(0x26U, (*exidx_->regs())[10]);
|
||||
ASSERT_EQ(0x27U, (*exidx_->regs())[11]);
|
||||
ASSERT_EQ(0x28U, (*exidx_->regs())[12]);
|
||||
ASSERT_EQ(0x29U, (*exidx_->regs())[13]);
|
||||
ASSERT_EQ(0x2aU, (*exidx_->regs())[14]);
|
||||
ASSERT_EQ(0x2bU, (*exidx_->regs())[15]);
|
||||
|
||||
ResetLogs();
|
||||
exidx_->set_cfa(0x10034);
|
||||
data_->push_back(0x81);
|
||||
data_->push_back(0x28);
|
||||
process_memory_.SetData(0x10034, 0x11);
|
||||
process_memory_.SetData(0x10038, 0x22);
|
||||
process_memory_.SetData(0x1003c, 0x33);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind pop {r7, r9, r12}\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x10040U, exidx_->cfa());
|
||||
ASSERT_EQ(0x11U, (*exidx_->regs())[7]);
|
||||
ASSERT_EQ(0x22U, (*exidx_->regs())[9]);
|
||||
ASSERT_EQ(0x33U, (*exidx_->regs())[12]);
|
||||
}
|
||||
|
||||
TEST_P(ArmExidxDecodeTest, set_vsp_from_register) {
|
||||
// 1001nnnn: Set vsp = r[nnnn] (nnnn != 13, 15)
|
||||
exidx_->set_cfa(0x100);
|
||||
for (size_t i = 0; i < 15; i++) {
|
||||
(*regs32_)[i] = i + 1;
|
||||
}
|
||||
|
||||
data_->push_back(0x90);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind vsp = r0\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(1U, exidx_->cfa());
|
||||
|
||||
ResetLogs();
|
||||
data_->push_back(0x93);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind vsp = r3\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(4U, exidx_->cfa());
|
||||
|
||||
ResetLogs();
|
||||
data_->push_back(0x9e);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind vsp = r14\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(15U, exidx_->cfa());
|
||||
}
|
||||
|
||||
TEST_P(ArmExidxDecodeTest, reserved_prefix) {
|
||||
// 10011101: Reserved as prefix for ARM register to register moves
|
||||
data_->push_back(0x9d);
|
||||
ASSERT_FALSE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind [Reserved]\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(ARM_STATUS_RESERVED, exidx_->status());
|
||||
|
||||
// 10011111: Reserved as prefix for Intel Wireless MMX register to register moves
|
||||
ResetLogs();
|
||||
data_->push_back(0x9f);
|
||||
ASSERT_FALSE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind [Reserved]\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(ARM_STATUS_RESERVED, exidx_->status());
|
||||
}
|
||||
|
||||
TEST_P(ArmExidxDecodeTest, pop_registers) {
|
||||
// 10100nnn: Pop r4-r[4+nnn]
|
||||
data_->push_back(0xa0);
|
||||
process_memory_.SetData(0x10000, 0x14);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind pop {r4}\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x10004U, exidx_->cfa());
|
||||
ASSERT_EQ(0x14U, (*exidx_->regs())[4]);
|
||||
|
||||
ResetLogs();
|
||||
data_->push_back(0xa3);
|
||||
process_memory_.SetData(0x10004, 0x20);
|
||||
process_memory_.SetData(0x10008, 0x30);
|
||||
process_memory_.SetData(0x1000c, 0x40);
|
||||
process_memory_.SetData(0x10010, 0x50);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind pop {r4-r7}\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x10014U, exidx_->cfa());
|
||||
ASSERT_EQ(0x20U, (*exidx_->regs())[4]);
|
||||
ASSERT_EQ(0x30U, (*exidx_->regs())[5]);
|
||||
ASSERT_EQ(0x40U, (*exidx_->regs())[6]);
|
||||
ASSERT_EQ(0x50U, (*exidx_->regs())[7]);
|
||||
|
||||
ResetLogs();
|
||||
data_->push_back(0xa7);
|
||||
process_memory_.SetData(0x10014, 0x41);
|
||||
process_memory_.SetData(0x10018, 0x51);
|
||||
process_memory_.SetData(0x1001c, 0x61);
|
||||
process_memory_.SetData(0x10020, 0x71);
|
||||
process_memory_.SetData(0x10024, 0x81);
|
||||
process_memory_.SetData(0x10028, 0x91);
|
||||
process_memory_.SetData(0x1002c, 0xa1);
|
||||
process_memory_.SetData(0x10030, 0xb1);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind pop {r4-r11}\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x10034U, exidx_->cfa());
|
||||
ASSERT_EQ(0x41U, (*exidx_->regs())[4]);
|
||||
ASSERT_EQ(0x51U, (*exidx_->regs())[5]);
|
||||
ASSERT_EQ(0x61U, (*exidx_->regs())[6]);
|
||||
ASSERT_EQ(0x71U, (*exidx_->regs())[7]);
|
||||
ASSERT_EQ(0x81U, (*exidx_->regs())[8]);
|
||||
ASSERT_EQ(0x91U, (*exidx_->regs())[9]);
|
||||
ASSERT_EQ(0xa1U, (*exidx_->regs())[10]);
|
||||
ASSERT_EQ(0xb1U, (*exidx_->regs())[11]);
|
||||
}
|
||||
|
||||
TEST_P(ArmExidxDecodeTest, pop_registers_with_r14) {
|
||||
// 10101nnn: Pop r4-r[4+nnn], r14
|
||||
data_->push_back(0xa8);
|
||||
process_memory_.SetData(0x10000, 0x12);
|
||||
process_memory_.SetData(0x10004, 0x22);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind pop {r4, r14}\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x10008U, exidx_->cfa());
|
||||
ASSERT_EQ(0x12U, (*exidx_->regs())[4]);
|
||||
ASSERT_EQ(0x22U, (*exidx_->regs())[14]);
|
||||
|
||||
ResetLogs();
|
||||
data_->push_back(0xab);
|
||||
process_memory_.SetData(0x10008, 0x1);
|
||||
process_memory_.SetData(0x1000c, 0x2);
|
||||
process_memory_.SetData(0x10010, 0x3);
|
||||
process_memory_.SetData(0x10014, 0x4);
|
||||
process_memory_.SetData(0x10018, 0x5);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind pop {r4-r7, r14}\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x1001cU, exidx_->cfa());
|
||||
ASSERT_EQ(0x1U, (*exidx_->regs())[4]);
|
||||
ASSERT_EQ(0x2U, (*exidx_->regs())[5]);
|
||||
ASSERT_EQ(0x3U, (*exidx_->regs())[6]);
|
||||
ASSERT_EQ(0x4U, (*exidx_->regs())[7]);
|
||||
ASSERT_EQ(0x5U, (*exidx_->regs())[14]);
|
||||
|
||||
ResetLogs();
|
||||
data_->push_back(0xaf);
|
||||
process_memory_.SetData(0x1001c, 0x1a);
|
||||
process_memory_.SetData(0x10020, 0x2a);
|
||||
process_memory_.SetData(0x10024, 0x3a);
|
||||
process_memory_.SetData(0x10028, 0x4a);
|
||||
process_memory_.SetData(0x1002c, 0x5a);
|
||||
process_memory_.SetData(0x10030, 0x6a);
|
||||
process_memory_.SetData(0x10034, 0x7a);
|
||||
process_memory_.SetData(0x10038, 0x8a);
|
||||
process_memory_.SetData(0x1003c, 0x9a);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind pop {r4-r11, r14}\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x10040U, exidx_->cfa());
|
||||
ASSERT_EQ(0x1aU, (*exidx_->regs())[4]);
|
||||
ASSERT_EQ(0x2aU, (*exidx_->regs())[5]);
|
||||
ASSERT_EQ(0x3aU, (*exidx_->regs())[6]);
|
||||
ASSERT_EQ(0x4aU, (*exidx_->regs())[7]);
|
||||
ASSERT_EQ(0x5aU, (*exidx_->regs())[8]);
|
||||
ASSERT_EQ(0x6aU, (*exidx_->regs())[9]);
|
||||
ASSERT_EQ(0x7aU, (*exidx_->regs())[10]);
|
||||
ASSERT_EQ(0x8aU, (*exidx_->regs())[11]);
|
||||
ASSERT_EQ(0x9aU, (*exidx_->regs())[14]);
|
||||
}
|
||||
|
||||
TEST_P(ArmExidxDecodeTest, finish) {
|
||||
// 10110000: Finish
|
||||
data_->push_back(0xb0);
|
||||
ASSERT_FALSE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind finish\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x10000U, exidx_->cfa());
|
||||
ASSERT_EQ(ARM_STATUS_FINISH, exidx_->status());
|
||||
}
|
||||
|
||||
TEST_P(ArmExidxDecodeTest, spare) {
|
||||
// 10110001 00000000: Spare
|
||||
data_->push_back(0xb1);
|
||||
data_->push_back(0x00);
|
||||
ASSERT_FALSE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x10000U, exidx_->cfa());
|
||||
ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
|
||||
|
||||
// 10110001 xxxxyyyy: Spare (xxxx != 0000)
|
||||
for (size_t x = 1; x < 16; x++) {
|
||||
for (size_t y = 0; y < 16; y++) {
|
||||
ResetLogs();
|
||||
data_->push_back(0xb1);
|
||||
data_->push_back((x << 4) | y);
|
||||
ASSERT_FALSE(exidx_->Decode()) << "x, y = " << x << ", " << y;
|
||||
ASSERT_EQ("", GetFakeLogBuf()) << "x, y = " << x << ", " << y;
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x10000U, exidx_->cfa()) << "x, y = " << x << ", " << y;
|
||||
ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
|
||||
}
|
||||
}
|
||||
|
||||
// 101101nn: Spare
|
||||
for (size_t n = 0; n < 4; n++) {
|
||||
ResetLogs();
|
||||
data_->push_back(0xb4 | n);
|
||||
ASSERT_FALSE(exidx_->Decode()) << "n = " << n;
|
||||
ASSERT_EQ("", GetFakeLogBuf()) << "n = " << n;
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "n = " << n;
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x10000U, exidx_->cfa()) << "n = " << n;
|
||||
ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
|
||||
}
|
||||
|
||||
// 11000111 00000000: Spare
|
||||
ResetLogs();
|
||||
data_->push_back(0xc7);
|
||||
data_->push_back(0x00);
|
||||
ASSERT_FALSE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x10000U, exidx_->cfa());
|
||||
ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
|
||||
|
||||
// 11000111 xxxxyyyy: Spare (xxxx != 0000)
|
||||
for (size_t x = 1; x < 16; x++) {
|
||||
for (size_t y = 0; y < 16; y++) {
|
||||
ResetLogs();
|
||||
data_->push_back(0xc7);
|
||||
data_->push_back(0x10);
|
||||
ASSERT_FALSE(exidx_->Decode()) << "x, y = " << x << ", " << y;
|
||||
ASSERT_EQ("", GetFakeLogBuf()) << "x, y = " << x << ", " << y;
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x10000U, exidx_->cfa()) << "x, y = " << x << ", " << y;
|
||||
ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
|
||||
}
|
||||
}
|
||||
|
||||
// 11001yyy: Spare (yyy != 000, 001)
|
||||
for (size_t y = 2; y < 8; y++) {
|
||||
ResetLogs();
|
||||
data_->push_back(0xc8 | y);
|
||||
ASSERT_FALSE(exidx_->Decode()) << "y = " << y;
|
||||
ASSERT_EQ("", GetFakeLogBuf()) << "y = " << y;
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "y = " << y;
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x10000U, exidx_->cfa()) << "y = " << y;
|
||||
ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
|
||||
}
|
||||
|
||||
// 11xxxyyy: Spare (xxx != 000, 001, 010)
|
||||
for (size_t x = 3; x < 8; x++) {
|
||||
for (size_t y = 0; y < 8; y++) {
|
||||
ResetLogs();
|
||||
data_->push_back(0xc0 | (x << 3) | y);
|
||||
ASSERT_FALSE(exidx_->Decode()) << "x, y = " << x << ", " << y;
|
||||
ASSERT_EQ("", GetFakeLogBuf()) << "x, y = " << x << ", " << y;
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x10000U, exidx_->cfa()) << "x, y = " << x << ", " << y;
|
||||
ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(ArmExidxDecodeTest, pop_registers_under_mask) {
|
||||
// 10110001 0000iiii: Pop integer registers {r0, r1, r2, r3}
|
||||
data_->push_back(0xb1);
|
||||
data_->push_back(0x01);
|
||||
process_memory_.SetData(0x10000, 0x45);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind pop {r0}\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x10004U, exidx_->cfa());
|
||||
ASSERT_EQ(0x45U, (*exidx_->regs())[0]);
|
||||
|
||||
ResetLogs();
|
||||
data_->push_back(0xb1);
|
||||
data_->push_back(0x0a);
|
||||
process_memory_.SetData(0x10004, 0x23);
|
||||
process_memory_.SetData(0x10008, 0x24);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind pop {r1, r3}\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x1000cU, exidx_->cfa());
|
||||
ASSERT_EQ(0x23U, (*exidx_->regs())[1]);
|
||||
ASSERT_EQ(0x24U, (*exidx_->regs())[3]);
|
||||
|
||||
ResetLogs();
|
||||
data_->push_back(0xb1);
|
||||
data_->push_back(0x0f);
|
||||
process_memory_.SetData(0x1000c, 0x65);
|
||||
process_memory_.SetData(0x10010, 0x54);
|
||||
process_memory_.SetData(0x10014, 0x43);
|
||||
process_memory_.SetData(0x10018, 0x32);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind pop {r0, r1, r2, r3}\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x1001cU, exidx_->cfa());
|
||||
ASSERT_EQ(0x65U, (*exidx_->regs())[0]);
|
||||
ASSERT_EQ(0x54U, (*exidx_->regs())[1]);
|
||||
ASSERT_EQ(0x43U, (*exidx_->regs())[2]);
|
||||
ASSERT_EQ(0x32U, (*exidx_->regs())[3]);
|
||||
}
|
||||
|
||||
TEST_P(ArmExidxDecodeTest, vsp_large_incr) {
|
||||
// 10110010 uleb128: vsp = vsp + 0x204 + (uleb128 << 2)
|
||||
data_->push_back(0xb2);
|
||||
data_->push_back(0x7f);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind vsp = vsp + 1024\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x10400U, exidx_->cfa());
|
||||
|
||||
ResetLogs();
|
||||
data_->push_back(0xb2);
|
||||
data_->push_back(0xff);
|
||||
data_->push_back(0x02);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind vsp = vsp + 2048\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x10c00U, exidx_->cfa());
|
||||
|
||||
ResetLogs();
|
||||
data_->push_back(0xb2);
|
||||
data_->push_back(0xff);
|
||||
data_->push_back(0x82);
|
||||
data_->push_back(0x30);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind vsp = vsp + 3147776\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x311400U, exidx_->cfa());
|
||||
}
|
||||
|
||||
TEST_P(ArmExidxDecodeTest, pop_vfp_fstmfdx) {
|
||||
// 10110011 sssscccc: Pop VFP double precision registers D[ssss]-D[ssss+cccc] by FSTMFDX
|
||||
data_->push_back(0xb3);
|
||||
data_->push_back(0x00);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind pop {d0}\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x1000cU, exidx_->cfa());
|
||||
|
||||
ResetLogs();
|
||||
data_->push_back(0xb3);
|
||||
data_->push_back(0x48);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind pop {d4-d12}\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x10058U, exidx_->cfa());
|
||||
}
|
||||
|
||||
TEST_P(ArmExidxDecodeTest, pop_vfp8_fstmfdx) {
|
||||
// 10111nnn: Pop VFP double precision registers D[8]-D[8+nnn] by FSTMFDX
|
||||
data_->push_back(0xb8);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind pop {d8}\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x1000cU, exidx_->cfa());
|
||||
|
||||
ResetLogs();
|
||||
data_->push_back(0xbb);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind pop {d8-d11}\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x10030U, exidx_->cfa());
|
||||
|
||||
ResetLogs();
|
||||
data_->push_back(0xbf);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind pop {d8-d15}\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x10074U, exidx_->cfa());
|
||||
}
|
||||
|
||||
TEST_P(ArmExidxDecodeTest, pop_mmx_wr10) {
|
||||
// 11000nnn: Intel Wireless MMX pop wR[10]-wR[10+nnn] (nnn != 6, 7)
|
||||
data_->push_back(0xc0);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind pop {wR10}\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x10008U, exidx_->cfa());
|
||||
|
||||
ResetLogs();
|
||||
data_->push_back(0xc2);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind pop {wR10-wR12}\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x10020U, exidx_->cfa());
|
||||
|
||||
ResetLogs();
|
||||
data_->push_back(0xc5);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind pop {wR10-wR15}\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x10050U, exidx_->cfa());
|
||||
}
|
||||
|
||||
TEST_P(ArmExidxDecodeTest, pop_mmx_wr) {
|
||||
// 11000110 sssscccc: Intel Wireless MMX pop wR[ssss]-wR[ssss+cccc]
|
||||
data_->push_back(0xc6);
|
||||
data_->push_back(0x00);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind pop {wR0}\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x10008U, exidx_->cfa());
|
||||
|
||||
ResetLogs();
|
||||
data_->push_back(0xc6);
|
||||
data_->push_back(0x25);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind pop {wR2-wR7}\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x10038U, exidx_->cfa());
|
||||
|
||||
ResetLogs();
|
||||
data_->push_back(0xc6);
|
||||
data_->push_back(0xff);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind pop {wR15-wR30}\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x100b8U, exidx_->cfa());
|
||||
}
|
||||
|
||||
TEST_P(ArmExidxDecodeTest, pop_mmx_wcgr) {
|
||||
// 11000111 0000iiii: Intel Wireless MMX pop wCGR registes {wCGR0,1,2,3}
|
||||
data_->push_back(0xc7);
|
||||
data_->push_back(0x01);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind pop {wCGR0}\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x10004U, exidx_->cfa());
|
||||
|
||||
ResetLogs();
|
||||
data_->push_back(0xc7);
|
||||
data_->push_back(0x0a);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind pop {wCGR1, wCGR3}\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x1000cU, exidx_->cfa());
|
||||
|
||||
ResetLogs();
|
||||
data_->push_back(0xc7);
|
||||
data_->push_back(0x0f);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind pop {wCGR0, wCGR1, wCGR2, wCGR3}\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x1001cU, exidx_->cfa());
|
||||
}
|
||||
|
||||
TEST_P(ArmExidxDecodeTest, pop_vfp16_vpush) {
|
||||
// 11001000 sssscccc: Pop VFP double precision registers d[16+ssss]-D[16+ssss+cccc] by VPUSH
|
||||
data_->push_back(0xc8);
|
||||
data_->push_back(0x00);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind pop {d16}\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x10008U, exidx_->cfa());
|
||||
|
||||
ResetLogs();
|
||||
data_->push_back(0xc8);
|
||||
data_->push_back(0x14);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind pop {d17-d21}\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x10030U, exidx_->cfa());
|
||||
|
||||
ResetLogs();
|
||||
data_->push_back(0xc8);
|
||||
data_->push_back(0xff);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind pop {d31-d46}\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x100b0U, exidx_->cfa());
|
||||
}
|
||||
|
||||
TEST_P(ArmExidxDecodeTest, pop_vfp_vpush) {
|
||||
// 11001001 sssscccc: Pop VFP double precision registers d[ssss]-D[ssss+cccc] by VPUSH
|
||||
data_->push_back(0xc9);
|
||||
data_->push_back(0x00);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind pop {d0}\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x10008U, exidx_->cfa());
|
||||
|
||||
ResetLogs();
|
||||
data_->push_back(0xc9);
|
||||
data_->push_back(0x23);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind pop {d2-d5}\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x10028U, exidx_->cfa());
|
||||
|
||||
ResetLogs();
|
||||
data_->push_back(0xc9);
|
||||
data_->push_back(0xff);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind pop {d15-d30}\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x100a8U, exidx_->cfa());
|
||||
}
|
||||
|
||||
TEST_P(ArmExidxDecodeTest, pop_vfp8_vpush) {
|
||||
// 11010nnn: Pop VFP double precision registers D[8]-D[8+nnn] by VPUSH
|
||||
data_->push_back(0xd0);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind pop {d8}\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x10008U, exidx_->cfa());
|
||||
|
||||
ResetLogs();
|
||||
data_->push_back(0xd2);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind pop {d8-d10}\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x10020U, exidx_->cfa());
|
||||
|
||||
ResetLogs();
|
||||
data_->push_back(0xd7);
|
||||
ASSERT_TRUE(exidx_->Decode());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
if (log_) {
|
||||
ASSERT_EQ("4 unwind pop {d8-d15}\n", GetFakeLogPrint());
|
||||
} else {
|
||||
ASSERT_EQ("", GetFakeLogPrint());
|
||||
}
|
||||
ASSERT_EQ(0x10060U, exidx_->cfa());
|
||||
}
|
||||
|
||||
TEST_P(ArmExidxDecodeTest, expect_truncated) {
|
||||
// This test verifies that any op that requires extra ops will
|
||||
// fail if the data is not present.
|
||||
data_->push_back(0x80);
|
||||
ASSERT_FALSE(exidx_->Decode());
|
||||
ASSERT_EQ(ARM_STATUS_TRUNCATED, exidx_->status());
|
||||
|
||||
data_->clear();
|
||||
data_->push_back(0xb1);
|
||||
ASSERT_FALSE(exidx_->Decode());
|
||||
ASSERT_EQ(ARM_STATUS_TRUNCATED, exidx_->status());
|
||||
|
||||
data_->clear();
|
||||
data_->push_back(0xb2);
|
||||
ASSERT_FALSE(exidx_->Decode());
|
||||
ASSERT_EQ(ARM_STATUS_TRUNCATED, exidx_->status());
|
||||
|
||||
data_->clear();
|
||||
data_->push_back(0xb3);
|
||||
ASSERT_FALSE(exidx_->Decode());
|
||||
ASSERT_EQ(ARM_STATUS_TRUNCATED, exidx_->status());
|
||||
|
||||
data_->clear();
|
||||
data_->push_back(0xc6);
|
||||
ASSERT_FALSE(exidx_->Decode());
|
||||
ASSERT_EQ(ARM_STATUS_TRUNCATED, exidx_->status());
|
||||
|
||||
data_->clear();
|
||||
data_->push_back(0xc7);
|
||||
ASSERT_FALSE(exidx_->Decode());
|
||||
ASSERT_EQ(ARM_STATUS_TRUNCATED, exidx_->status());
|
||||
|
||||
data_->clear();
|
||||
data_->push_back(0xc8);
|
||||
ASSERT_FALSE(exidx_->Decode());
|
||||
ASSERT_EQ(ARM_STATUS_TRUNCATED, exidx_->status());
|
||||
|
||||
data_->clear();
|
||||
data_->push_back(0xc9);
|
||||
ASSERT_FALSE(exidx_->Decode());
|
||||
ASSERT_EQ(ARM_STATUS_TRUNCATED, exidx_->status());
|
||||
}
|
||||
|
||||
TEST_P(ArmExidxDecodeTest, verify_no_truncated) {
|
||||
// This test verifies that no pattern results in a crash or truncation.
|
||||
MemoryFakeAlwaysReadZero memory_zero;
|
||||
Init(&memory_zero);
|
||||
|
||||
for (size_t x = 0; x < 256; x++) {
|
||||
if (x == 0xb2) {
|
||||
// This opcode is followed by an uleb128, so just skip this one.
|
||||
continue;
|
||||
}
|
||||
for (size_t y = 0; y < 256; y++) {
|
||||
data_->clear();
|
||||
data_->push_back(x);
|
||||
data_->push_back(y);
|
||||
if (!exidx_->Decode()) {
|
||||
ASSERT_NE(ARM_STATUS_TRUNCATED, exidx_->status())
|
||||
<< "x y = 0x" << std::hex << x << " 0x" << y;
|
||||
ASSERT_NE(ARM_STATUS_READ_FAILED, exidx_->status())
|
||||
<< "x y = 0x" << std::hex << x << " 0x" << y;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(, ArmExidxDecodeTest, ::testing::Values("logging", "no_logging"));
|
|
@ -0,0 +1,331 @@
|
|||
/*
|
||||
* 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 <vector>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "ArmExidx.h"
|
||||
#include "Log.h"
|
||||
|
||||
#include "LogFake.h"
|
||||
#include "MemoryFake.h"
|
||||
|
||||
class ArmExidxExtractTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
ResetLogs();
|
||||
elf_memory_.Clear();
|
||||
exidx_ = new ArmExidx(nullptr, &elf_memory_, nullptr);
|
||||
data_ = exidx_->data();
|
||||
data_->clear();
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
delete exidx_;
|
||||
}
|
||||
|
||||
ArmExidx* exidx_ = nullptr;
|
||||
std::deque<uint8_t>* data_;
|
||||
MemoryFake elf_memory_;
|
||||
};
|
||||
|
||||
TEST_F(ArmExidxExtractTest, bad_alignment) {
|
||||
ASSERT_FALSE(exidx_->ExtractEntryData(0x1001));
|
||||
ASSERT_EQ(ARM_STATUS_INVALID_ALIGNMENT, exidx_->status());
|
||||
ASSERT_TRUE(data_->empty());
|
||||
}
|
||||
|
||||
TEST_F(ArmExidxExtractTest, cant_unwind) {
|
||||
elf_memory_.SetData(0x1000, 0x7fff2340);
|
||||
elf_memory_.SetData(0x1004, 1);
|
||||
ASSERT_FALSE(exidx_->ExtractEntryData(0x1000));
|
||||
ASSERT_EQ(ARM_STATUS_NO_UNWIND, exidx_->status());
|
||||
ASSERT_TRUE(data_->empty());
|
||||
}
|
||||
|
||||
TEST_F(ArmExidxExtractTest, compact) {
|
||||
elf_memory_.SetData(0x4000, 0x7ffa3000);
|
||||
elf_memory_.SetData(0x4004, 0x80a8b0b0);
|
||||
ASSERT_TRUE(exidx_->ExtractEntryData(0x4000));
|
||||
ASSERT_EQ(3U, data_->size());
|
||||
ASSERT_EQ(0xa8, data_->at(0));
|
||||
ASSERT_EQ(0xb0, data_->at(1));
|
||||
ASSERT_EQ(0xb0, data_->at(2));
|
||||
|
||||
// Missing finish gets added.
|
||||
elf_memory_.Clear();
|
||||
elf_memory_.SetData(0x534, 0x7ffa3000);
|
||||
elf_memory_.SetData(0x538, 0x80a1a2a3);
|
||||
ASSERT_TRUE(exidx_->ExtractEntryData(0x534));
|
||||
ASSERT_EQ(4U, data_->size());
|
||||
ASSERT_EQ(0xa1, data_->at(0));
|
||||
ASSERT_EQ(0xa2, data_->at(1));
|
||||
ASSERT_EQ(0xa3, data_->at(2));
|
||||
ASSERT_EQ(0xb0, data_->at(3));
|
||||
}
|
||||
|
||||
TEST_F(ArmExidxExtractTest, compact_non_zero_personality) {
|
||||
elf_memory_.SetData(0x4000, 0x7ffa3000);
|
||||
|
||||
uint32_t compact_value = 0x80a8b0b0;
|
||||
for (size_t i = 1; i < 16; i++) {
|
||||
elf_memory_.SetData(0x4004, compact_value | (i << 24));
|
||||
ASSERT_FALSE(exidx_->ExtractEntryData(0x4000));
|
||||
ASSERT_EQ(ARM_STATUS_INVALID_PERSONALITY, exidx_->status());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ArmExidxExtractTest, second_read_compact_personality_1_2) {
|
||||
elf_memory_.SetData(0x5000, 0x1234);
|
||||
elf_memory_.SetData(0x5004, 0x00001230);
|
||||
elf_memory_.SetData(0x6234, 0x8100f3b0);
|
||||
ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
|
||||
ASSERT_EQ(2U, data_->size());
|
||||
ASSERT_EQ(0xf3, data_->at(0));
|
||||
ASSERT_EQ(0xb0, data_->at(1));
|
||||
|
||||
elf_memory_.Clear();
|
||||
elf_memory_.SetData(0x5000, 0x1234);
|
||||
elf_memory_.SetData(0x5004, 0x00001230);
|
||||
elf_memory_.SetData(0x6234, 0x8200f3f4);
|
||||
ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
|
||||
ASSERT_EQ(3U, data_->size());
|
||||
ASSERT_EQ(0xf3, data_->at(0));
|
||||
ASSERT_EQ(0xf4, data_->at(1));
|
||||
ASSERT_EQ(0xb0, data_->at(2));
|
||||
|
||||
elf_memory_.Clear();
|
||||
elf_memory_.SetData(0x5000, 0x1234);
|
||||
elf_memory_.SetData(0x5004, 0x00001230);
|
||||
elf_memory_.SetData(0x6234, 0x8201f3f4);
|
||||
elf_memory_.SetData(0x6238, 0x102030b0);
|
||||
ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
|
||||
ASSERT_EQ(6U, data_->size());
|
||||
ASSERT_EQ(0xf3, data_->at(0));
|
||||
ASSERT_EQ(0xf4, data_->at(1));
|
||||
ASSERT_EQ(0x10, data_->at(2));
|
||||
ASSERT_EQ(0x20, data_->at(3));
|
||||
ASSERT_EQ(0x30, data_->at(4));
|
||||
ASSERT_EQ(0xb0, data_->at(5));
|
||||
|
||||
elf_memory_.Clear();
|
||||
elf_memory_.SetData(0x5000, 0x1234);
|
||||
elf_memory_.SetData(0x5004, 0x00001230);
|
||||
elf_memory_.SetData(0x6234, 0x8103f3f4);
|
||||
elf_memory_.SetData(0x6238, 0x10203040);
|
||||
elf_memory_.SetData(0x623c, 0x50607080);
|
||||
elf_memory_.SetData(0x6240, 0x90a0c0d0);
|
||||
ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
|
||||
ASSERT_EQ(15U, data_->size());
|
||||
ASSERT_EQ(0xf3, data_->at(0));
|
||||
ASSERT_EQ(0xf4, data_->at(1));
|
||||
ASSERT_EQ(0x10, data_->at(2));
|
||||
ASSERT_EQ(0x20, data_->at(3));
|
||||
ASSERT_EQ(0x30, data_->at(4));
|
||||
ASSERT_EQ(0x40, data_->at(5));
|
||||
ASSERT_EQ(0x50, data_->at(6));
|
||||
ASSERT_EQ(0x60, data_->at(7));
|
||||
ASSERT_EQ(0x70, data_->at(8));
|
||||
ASSERT_EQ(0x80, data_->at(9));
|
||||
ASSERT_EQ(0x90, data_->at(10));
|
||||
ASSERT_EQ(0xa0, data_->at(11));
|
||||
ASSERT_EQ(0xc0, data_->at(12));
|
||||
ASSERT_EQ(0xd0, data_->at(13));
|
||||
ASSERT_EQ(0xb0, data_->at(14));
|
||||
}
|
||||
|
||||
TEST_F(ArmExidxExtractTest, second_read_compact_personality_illegal) {
|
||||
elf_memory_.SetData(0x5000, 0x7ffa1e48);
|
||||
elf_memory_.SetData(0x5004, 0x1230);
|
||||
elf_memory_.SetData(0x6234, 0x832132b0);
|
||||
ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
|
||||
ASSERT_EQ(ARM_STATUS_INVALID_PERSONALITY, exidx_->status());
|
||||
|
||||
elf_memory_.Clear();
|
||||
elf_memory_.SetData(0x5000, 0x7ffa1e48);
|
||||
elf_memory_.SetData(0x5004, 0x1230);
|
||||
elf_memory_.SetData(0x6234, 0x842132b0);
|
||||
ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
|
||||
ASSERT_EQ(ARM_STATUS_INVALID_PERSONALITY, exidx_->status());
|
||||
}
|
||||
|
||||
TEST_F(ArmExidxExtractTest, second_read_offset_is_negative) {
|
||||
elf_memory_.SetData(0x5000, 0x7ffa1e48);
|
||||
elf_memory_.SetData(0x5004, 0x7fffb1e0);
|
||||
elf_memory_.SetData(0x1e4, 0x842132b0);
|
||||
ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
|
||||
ASSERT_EQ(ARM_STATUS_INVALID_PERSONALITY, exidx_->status());
|
||||
}
|
||||
|
||||
TEST_F(ArmExidxExtractTest, second_read_not_compact) {
|
||||
elf_memory_.SetData(0x5000, 0x1234);
|
||||
elf_memory_.SetData(0x5004, 0x00001230);
|
||||
elf_memory_.SetData(0x6234, 0x1);
|
||||
elf_memory_.SetData(0x6238, 0x001122b0);
|
||||
ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
|
||||
ASSERT_EQ(3U, data_->size());
|
||||
ASSERT_EQ(0x11, data_->at(0));
|
||||
ASSERT_EQ(0x22, data_->at(1));
|
||||
ASSERT_EQ(0xb0, data_->at(2));
|
||||
|
||||
elf_memory_.Clear();
|
||||
elf_memory_.SetData(0x5000, 0x1234);
|
||||
elf_memory_.SetData(0x5004, 0x00001230);
|
||||
elf_memory_.SetData(0x6234, 0x2);
|
||||
elf_memory_.SetData(0x6238, 0x00112233);
|
||||
ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
|
||||
ASSERT_EQ(4U, data_->size());
|
||||
ASSERT_EQ(0x11, data_->at(0));
|
||||
ASSERT_EQ(0x22, data_->at(1));
|
||||
ASSERT_EQ(0x33, data_->at(2));
|
||||
ASSERT_EQ(0xb0, data_->at(3));
|
||||
|
||||
elf_memory_.Clear();
|
||||
elf_memory_.SetData(0x5000, 0x1234);
|
||||
elf_memory_.SetData(0x5004, 0x00001230);
|
||||
elf_memory_.SetData(0x6234, 0x3);
|
||||
elf_memory_.SetData(0x6238, 0x01112233);
|
||||
elf_memory_.SetData(0x623c, 0x445566b0);
|
||||
ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
|
||||
ASSERT_EQ(7U, data_->size());
|
||||
ASSERT_EQ(0x11, data_->at(0));
|
||||
ASSERT_EQ(0x22, data_->at(1));
|
||||
ASSERT_EQ(0x33, data_->at(2));
|
||||
ASSERT_EQ(0x44, data_->at(3));
|
||||
ASSERT_EQ(0x55, data_->at(4));
|
||||
ASSERT_EQ(0x66, data_->at(5));
|
||||
ASSERT_EQ(0xb0, data_->at(6));
|
||||
|
||||
elf_memory_.Clear();
|
||||
elf_memory_.SetData(0x5000, 0x1234);
|
||||
elf_memory_.SetData(0x5004, 0x00001230);
|
||||
elf_memory_.SetData(0x6234, 0x3);
|
||||
elf_memory_.SetData(0x6238, 0x05112233);
|
||||
elf_memory_.SetData(0x623c, 0x01020304);
|
||||
elf_memory_.SetData(0x6240, 0x05060708);
|
||||
elf_memory_.SetData(0x6244, 0x090a0b0c);
|
||||
elf_memory_.SetData(0x6248, 0x0d0e0f10);
|
||||
elf_memory_.SetData(0x624c, 0x11121314);
|
||||
ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
|
||||
ASSERT_EQ(24U, data_->size());
|
||||
ASSERT_EQ(0x11, data_->at(0));
|
||||
ASSERT_EQ(0x22, data_->at(1));
|
||||
ASSERT_EQ(0x33, data_->at(2));
|
||||
ASSERT_EQ(0x01, data_->at(3));
|
||||
ASSERT_EQ(0x02, data_->at(4));
|
||||
ASSERT_EQ(0x03, data_->at(5));
|
||||
ASSERT_EQ(0x04, data_->at(6));
|
||||
ASSERT_EQ(0x05, data_->at(7));
|
||||
ASSERT_EQ(0x06, data_->at(8));
|
||||
ASSERT_EQ(0x07, data_->at(9));
|
||||
ASSERT_EQ(0x08, data_->at(10));
|
||||
ASSERT_EQ(0x09, data_->at(11));
|
||||
ASSERT_EQ(0x0a, data_->at(12));
|
||||
ASSERT_EQ(0x0b, data_->at(13));
|
||||
ASSERT_EQ(0x0c, data_->at(14));
|
||||
ASSERT_EQ(0x0d, data_->at(15));
|
||||
ASSERT_EQ(0x0e, data_->at(16));
|
||||
ASSERT_EQ(0x0f, data_->at(17));
|
||||
ASSERT_EQ(0x10, data_->at(18));
|
||||
ASSERT_EQ(0x11, data_->at(19));
|
||||
ASSERT_EQ(0x12, data_->at(20));
|
||||
ASSERT_EQ(0x13, data_->at(21));
|
||||
ASSERT_EQ(0x14, data_->at(22));
|
||||
ASSERT_EQ(0xb0, data_->at(23));
|
||||
}
|
||||
|
||||
TEST_F(ArmExidxExtractTest, read_failures) {
|
||||
ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
|
||||
ASSERT_EQ(ARM_STATUS_READ_FAILED, exidx_->status());
|
||||
|
||||
elf_memory_.SetData(0x5000, 0x100);
|
||||
ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
|
||||
ASSERT_EQ(ARM_STATUS_READ_FAILED, exidx_->status());
|
||||
|
||||
elf_memory_.SetData(0x5004, 0x100);
|
||||
ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
|
||||
ASSERT_EQ(ARM_STATUS_READ_FAILED, exidx_->status());
|
||||
|
||||
elf_memory_.SetData(0x5104, 0x1);
|
||||
ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
|
||||
ASSERT_EQ(ARM_STATUS_READ_FAILED, exidx_->status());
|
||||
|
||||
elf_memory_.SetData(0x5108, 0x01010203);
|
||||
ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
|
||||
ASSERT_EQ(ARM_STATUS_READ_FAILED, exidx_->status());
|
||||
}
|
||||
|
||||
TEST_F(ArmExidxExtractTest, malformed) {
|
||||
elf_memory_.SetData(0x5000, 0x100);
|
||||
elf_memory_.SetData(0x5004, 0x100);
|
||||
elf_memory_.SetData(0x5104, 0x1);
|
||||
elf_memory_.SetData(0x5108, 0x06010203);
|
||||
ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
|
||||
ASSERT_EQ(ARM_STATUS_MALFORMED, exidx_->status());
|
||||
|
||||
elf_memory_.Clear();
|
||||
elf_memory_.SetData(0x5000, 0x100);
|
||||
elf_memory_.SetData(0x5004, 0x100);
|
||||
elf_memory_.SetData(0x5104, 0x1);
|
||||
elf_memory_.SetData(0x5108, 0x81060203);
|
||||
ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
|
||||
ASSERT_EQ(ARM_STATUS_MALFORMED, exidx_->status());
|
||||
}
|
||||
|
||||
TEST_F(ArmExidxExtractTest, cant_unwind_log) {
|
||||
elf_memory_.SetData(0x1000, 0x7fff2340);
|
||||
elf_memory_.SetData(0x1004, 1);
|
||||
|
||||
exidx_->set_log(true);
|
||||
exidx_->set_log_indent(0);
|
||||
exidx_->set_log_skip_execution(false);
|
||||
|
||||
ASSERT_FALSE(exidx_->ExtractEntryData(0x1000));
|
||||
ASSERT_EQ(ARM_STATUS_NO_UNWIND, exidx_->status());
|
||||
|
||||
ASSERT_EQ("4 unwind Raw Data: 0x00 0x00 0x00 0x01\n"
|
||||
"4 unwind [cantunwind]\n", GetFakeLogPrint());
|
||||
}
|
||||
|
||||
TEST_F(ArmExidxExtractTest, raw_data_compact) {
|
||||
elf_memory_.SetData(0x4000, 0x7ffa3000);
|
||||
elf_memory_.SetData(0x4004, 0x80a8b0b0);
|
||||
|
||||
exidx_->set_log(true);
|
||||
exidx_->set_log_indent(0);
|
||||
exidx_->set_log_skip_execution(false);
|
||||
|
||||
ASSERT_TRUE(exidx_->ExtractEntryData(0x4000));
|
||||
ASSERT_EQ("4 unwind Raw Data: 0xa8 0xb0 0xb0\n", GetFakeLogPrint());
|
||||
}
|
||||
|
||||
TEST_F(ArmExidxExtractTest, raw_data_non_compact) {
|
||||
elf_memory_.SetData(0x5000, 0x1234);
|
||||
elf_memory_.SetData(0x5004, 0x00001230);
|
||||
elf_memory_.SetData(0x6234, 0x2);
|
||||
elf_memory_.SetData(0x6238, 0x00112233);
|
||||
|
||||
exidx_->set_log(true);
|
||||
exidx_->set_log_indent(0);
|
||||
exidx_->set_log_skip_execution(false);
|
||||
|
||||
ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
|
||||
ASSERT_EQ("4 unwind Raw Data: 0x11 0x22 0x33 0xb0\n", GetFakeLogPrint());
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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 <errno.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <log/log.h>
|
||||
#include <log/logger.h>
|
||||
|
||||
#include "LogFake.h"
|
||||
|
||||
// Forward declarations.
|
||||
class Backtrace;
|
||||
struct EventTagMap;
|
||||
struct AndroidLogEntry;
|
||||
|
||||
std::string g_fake_log_buf;
|
||||
|
||||
std::string g_fake_log_print;
|
||||
|
||||
void ResetLogs() {
|
||||
g_fake_log_buf = "";
|
||||
g_fake_log_print = "";
|
||||
}
|
||||
|
||||
std::string GetFakeLogBuf() {
|
||||
return g_fake_log_buf;
|
||||
}
|
||||
|
||||
std::string GetFakeLogPrint() {
|
||||
return g_fake_log_print;
|
||||
}
|
||||
|
||||
extern "C" int __android_log_buf_write(int bufId, int prio, const char* tag, const char* msg) {
|
||||
g_fake_log_buf += std::to_string(bufId) + ' ' + std::to_string(prio) + ' ';
|
||||
g_fake_log_buf += tag;
|
||||
g_fake_log_buf += ' ';
|
||||
g_fake_log_buf += msg;
|
||||
return 1;
|
||||
}
|
||||
|
||||
extern "C" int __android_log_print(int prio, const char* tag, const char* fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
int val = __android_log_vprint(prio, tag, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
extern "C" int __android_log_vprint(int prio, const char* tag, const char* fmt, va_list ap) {
|
||||
g_fake_log_print += std::to_string(prio) + ' ';
|
||||
g_fake_log_print += tag;
|
||||
g_fake_log_print += ' ';
|
||||
|
||||
android::base::StringAppendV(&g_fake_log_print, fmt, ap);
|
||||
|
||||
g_fake_log_print += '\n';
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
extern "C" log_id_t android_name_to_log_id(const char*) {
|
||||
return LOG_ID_SYSTEM;
|
||||
}
|
||||
|
||||
extern "C" struct logger_list* android_logger_list_open(log_id_t, int, unsigned int, pid_t) {
|
||||
errno = EACCES;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
extern "C" int android_logger_list_read(struct logger_list*, struct log_msg*) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" EventTagMap* android_openEventTagMap(const char*) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
extern "C" int android_log_processBinaryLogBuffer(
|
||||
struct logger_entry*,
|
||||
AndroidLogEntry*, const EventTagMap*, char*, int) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" void android_logger_list_free(struct logger_list*) {
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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_TESTS_LOG_FAKE_H
|
||||
#define _LIBUNWINDSTACK_TESTS_LOG_FAKE_H
|
||||
|
||||
#include <string>
|
||||
|
||||
void ResetLogs();
|
||||
std::string GetFakeLogBuf();
|
||||
std::string GetFakeLogPrint();
|
||||
|
||||
#endif // _LIBUNWINDSTACK_TESTS_LOG_FAKE_H
|
|
@ -0,0 +1,219 @@
|
|||
/*
|
||||
* 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 <sys/mman.h>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/test_utils.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "Maps.h"
|
||||
|
||||
#include "LogFake.h"
|
||||
|
||||
class MapsTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
ResetLogs();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(MapsTest, parse_permissions) {
|
||||
MapsBuffer maps("1000-2000 ---- 00000000 00:00 0\n"
|
||||
"2000-3000 r--- 00000000 00:00 0\n"
|
||||
"3000-4000 -w-- 00000000 00:00 0\n"
|
||||
"4000-5000 --x- 00000000 00:00 0\n"
|
||||
"5000-6000 rwx- 00000000 00:00 0\n");
|
||||
|
||||
ASSERT_TRUE(maps.Parse());
|
||||
ASSERT_EQ(5U, maps.Total());
|
||||
auto it = maps.begin();
|
||||
ASSERT_EQ(PROT_NONE, it->flags);
|
||||
ASSERT_EQ(0x1000U, it->start);
|
||||
ASSERT_EQ(0x2000U, it->end);
|
||||
ASSERT_EQ(0U, it->offset);
|
||||
ASSERT_EQ("", it->name);
|
||||
++it;
|
||||
ASSERT_EQ(PROT_READ, it->flags);
|
||||
ASSERT_EQ(0x2000U, it->start);
|
||||
ASSERT_EQ(0x3000U, it->end);
|
||||
ASSERT_EQ(0U, it->offset);
|
||||
ASSERT_EQ("", it->name);
|
||||
++it;
|
||||
ASSERT_EQ(PROT_WRITE, it->flags);
|
||||
ASSERT_EQ(0x3000U, it->start);
|
||||
ASSERT_EQ(0x4000U, it->end);
|
||||
ASSERT_EQ(0U, it->offset);
|
||||
ASSERT_EQ("", it->name);
|
||||
++it;
|
||||
ASSERT_EQ(PROT_EXEC, it->flags);
|
||||
ASSERT_EQ(0x4000U, it->start);
|
||||
ASSERT_EQ(0x5000U, it->end);
|
||||
ASSERT_EQ(0U, it->offset);
|
||||
ASSERT_EQ("", it->name);
|
||||
++it;
|
||||
ASSERT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, it->flags);
|
||||
ASSERT_EQ(0x5000U, it->start);
|
||||
ASSERT_EQ(0x6000U, it->end);
|
||||
ASSERT_EQ(0U, it->offset);
|
||||
ASSERT_EQ("", it->name);
|
||||
++it;
|
||||
ASSERT_EQ(it, maps.end());
|
||||
}
|
||||
|
||||
TEST_F(MapsTest, parse_name) {
|
||||
MapsBuffer maps("720b29b000-720b29e000 rw-p 00000000 00:00 0\n"
|
||||
"720b29e000-720b29f000 rw-p 00000000 00:00 0 /system/lib/fake.so\n"
|
||||
"720b29f000-720b2a0000 rw-p 00000000 00:00 0");
|
||||
|
||||
ASSERT_TRUE(maps.Parse());
|
||||
ASSERT_EQ(3U, maps.Total());
|
||||
auto it = maps.begin();
|
||||
ASSERT_EQ("", it->name);
|
||||
ASSERT_EQ(0x720b29b000U, it->start);
|
||||
ASSERT_EQ(0x720b29e000U, it->end);
|
||||
ASSERT_EQ(0U, it->offset);
|
||||
ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
|
||||
++it;
|
||||
ASSERT_EQ("/system/lib/fake.so", it->name);
|
||||
ASSERT_EQ(0x720b29e000U, it->start);
|
||||
ASSERT_EQ(0x720b29f000U, it->end);
|
||||
ASSERT_EQ(0U, it->offset);
|
||||
ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
|
||||
++it;
|
||||
ASSERT_EQ("", it->name);
|
||||
ASSERT_EQ(0x720b29f000U, it->start);
|
||||
ASSERT_EQ(0x720b2a0000U, it->end);
|
||||
ASSERT_EQ(0U, it->offset);
|
||||
ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
|
||||
++it;
|
||||
ASSERT_EQ(it, maps.end());
|
||||
}
|
||||
|
||||
TEST_F(MapsTest, parse_offset) {
|
||||
MapsBuffer maps("a000-e000 rw-p 00000000 00:00 0 /system/lib/fake.so\n"
|
||||
"e000-f000 rw-p 00a12345 00:00 0 /system/lib/fake.so\n");
|
||||
|
||||
ASSERT_TRUE(maps.Parse());
|
||||
ASSERT_EQ(2U, maps.Total());
|
||||
auto it = maps.begin();
|
||||
ASSERT_EQ(0U, it->offset);
|
||||
ASSERT_EQ(0xa000U, it->start);
|
||||
ASSERT_EQ(0xe000U, it->end);
|
||||
ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
|
||||
ASSERT_EQ("/system/lib/fake.so", it->name);
|
||||
++it;
|
||||
ASSERT_EQ(0xa12345U, it->offset);
|
||||
ASSERT_EQ(0xe000U, it->start);
|
||||
ASSERT_EQ(0xf000U, it->end);
|
||||
ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
|
||||
ASSERT_EQ("/system/lib/fake.so", it->name);
|
||||
++it;
|
||||
ASSERT_EQ(maps.end(), it);
|
||||
}
|
||||
|
||||
TEST_F(MapsTest, file_smoke) {
|
||||
TemporaryFile tf;
|
||||
ASSERT_TRUE(tf.fd != -1);
|
||||
|
||||
ASSERT_TRUE(android::base::WriteStringToFile(
|
||||
"720b29b000-720b29e000 r-xp a0000000 00:00 0 /fake.so\n"
|
||||
"720b2b0000-720b2e0000 r-xp b0000000 00:00 0 /fake2.so\n"
|
||||
"720b2e0000-720b2f0000 r-xp c0000000 00:00 0 /fake3.so\n",
|
||||
tf.path, 0660, getuid(), getgid()));
|
||||
|
||||
MapsFile maps(tf.path);
|
||||
|
||||
ASSERT_TRUE(maps.Parse());
|
||||
ASSERT_EQ(3U, maps.Total());
|
||||
auto it = maps.begin();
|
||||
ASSERT_EQ(0x720b29b000U, it->start);
|
||||
ASSERT_EQ(0x720b29e000U, it->end);
|
||||
ASSERT_EQ(0xa0000000U, it->offset);
|
||||
ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
|
||||
ASSERT_EQ("/fake.so", it->name);
|
||||
++it;
|
||||
ASSERT_EQ(0x720b2b0000U, it->start);
|
||||
ASSERT_EQ(0x720b2e0000U, it->end);
|
||||
ASSERT_EQ(0xb0000000U, it->offset);
|
||||
ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
|
||||
ASSERT_EQ("/fake2.so", it->name);
|
||||
++it;
|
||||
ASSERT_EQ(0x720b2e0000U, it->start);
|
||||
ASSERT_EQ(0x720b2f0000U, it->end);
|
||||
ASSERT_EQ(0xc0000000U, it->offset);
|
||||
ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
|
||||
ASSERT_EQ("/fake3.so", it->name);
|
||||
++it;
|
||||
ASSERT_EQ(it, maps.end());
|
||||
}
|
||||
|
||||
TEST_F(MapsTest, find) {
|
||||
MapsBuffer maps("1000-2000 r--p 00000010 00:00 0 /system/lib/fake1.so\n"
|
||||
"3000-4000 -w-p 00000020 00:00 0 /system/lib/fake2.so\n"
|
||||
"6000-8000 --xp 00000030 00:00 0 /system/lib/fake3.so\n"
|
||||
"a000-b000 rw-p 00000040 00:00 0 /system/lib/fake4.so\n"
|
||||
"e000-f000 rwxp 00000050 00:00 0 /system/lib/fake5.so\n");
|
||||
ASSERT_TRUE(maps.Parse());
|
||||
ASSERT_EQ(5U, maps.Total());
|
||||
|
||||
ASSERT_TRUE(maps.Find(0x500) == nullptr);
|
||||
ASSERT_TRUE(maps.Find(0x2000) == nullptr);
|
||||
ASSERT_TRUE(maps.Find(0x5010) == nullptr);
|
||||
ASSERT_TRUE(maps.Find(0x9a00) == nullptr);
|
||||
ASSERT_TRUE(maps.Find(0xf000) == nullptr);
|
||||
ASSERT_TRUE(maps.Find(0xf010) == nullptr);
|
||||
|
||||
MapInfo* info = maps.Find(0x1000);
|
||||
ASSERT_TRUE(info != nullptr);
|
||||
ASSERT_EQ(0x1000U, info->start);
|
||||
ASSERT_EQ(0x2000U, info->end);
|
||||
ASSERT_EQ(0x10U, info->offset);
|
||||
ASSERT_EQ(PROT_READ, info->flags);
|
||||
ASSERT_EQ("/system/lib/fake1.so", info->name);
|
||||
|
||||
info = maps.Find(0x3020);
|
||||
ASSERT_TRUE(info != nullptr);
|
||||
ASSERT_EQ(0x3000U, info->start);
|
||||
ASSERT_EQ(0x4000U, info->end);
|
||||
ASSERT_EQ(0x20U, info->offset);
|
||||
ASSERT_EQ(PROT_WRITE, info->flags);
|
||||
ASSERT_EQ("/system/lib/fake2.so", info->name);
|
||||
|
||||
info = maps.Find(0x6020);
|
||||
ASSERT_TRUE(info != nullptr);
|
||||
ASSERT_EQ(0x6000U, info->start);
|
||||
ASSERT_EQ(0x8000U, info->end);
|
||||
ASSERT_EQ(0x30U, info->offset);
|
||||
ASSERT_EQ(PROT_EXEC, info->flags);
|
||||
ASSERT_EQ("/system/lib/fake3.so", info->name);
|
||||
|
||||
info = maps.Find(0xafff);
|
||||
ASSERT_TRUE(info != nullptr);
|
||||
ASSERT_EQ(0xa000U, info->start);
|
||||
ASSERT_EQ(0xb000U, info->end);
|
||||
ASSERT_EQ(0x40U, info->offset);
|
||||
ASSERT_EQ(PROT_READ | PROT_WRITE, info->flags);
|
||||
ASSERT_EQ("/system/lib/fake4.so", info->name);
|
||||
|
||||
info = maps.Find(0xe500);
|
||||
ASSERT_TRUE(info != nullptr);
|
||||
ASSERT_EQ(0xe000U, info->start);
|
||||
ASSERT_EQ(0xf000U, info->end);
|
||||
ASSERT_EQ(0x50U, info->offset);
|
||||
ASSERT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info->flags);
|
||||
ASSERT_EQ("/system/lib/fake5.so", info->name);
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "MemoryFake.h"
|
||||
|
||||
void MemoryFake::SetMemory(uint64_t addr, const void* memory, size_t length) {
|
||||
const uint8_t* src = reinterpret_cast<const uint8_t*>(memory);
|
||||
for (size_t i = 0; i < length; i++, addr++) {
|
||||
auto value = data_.find(addr);
|
||||
if (value != data_.end()) {
|
||||
value->second = src[i];
|
||||
} else {
|
||||
data_.insert({ addr, src[i] });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool MemoryFake::Read(uint64_t addr, void* memory, size_t size) {
|
||||
uint8_t* dst = reinterpret_cast<uint8_t*>(memory);
|
||||
for (size_t i = 0; i < size; i++, addr++) {
|
||||
auto value = data_.find(addr);
|
||||
if (value == data_.end()) {
|
||||
return false;
|
||||
}
|
||||
dst[i] = value->second;
|
||||
}
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* 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_TESTS_MEMORY_FAKE_H
|
||||
#define _LIBUNWINDSTACK_TESTS_MEMORY_FAKE_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "Memory.h"
|
||||
|
||||
class MemoryFake : public Memory {
|
||||
public:
|
||||
MemoryFake() = default;
|
||||
virtual ~MemoryFake() = default;
|
||||
|
||||
bool Read(uint64_t addr, void* buffer, size_t size) override;
|
||||
|
||||
void SetMemory(uint64_t addr, const void* memory, size_t length);
|
||||
|
||||
void SetData(uint64_t addr, uint32_t value) {
|
||||
SetMemory(addr, &value, sizeof(value));
|
||||
}
|
||||
|
||||
void SetMemory(uint64_t addr, std::vector<uint8_t> values) {
|
||||
SetMemory(addr, values.data(), values.size());
|
||||
}
|
||||
|
||||
void SetMemory(uint64_t addr, std::string string) {
|
||||
SetMemory(addr, string.c_str(), string.size() + 1);
|
||||
}
|
||||
|
||||
void Clear() { data_.clear(); }
|
||||
|
||||
private:
|
||||
std::unordered_map<uint64_t, uint8_t> data_;
|
||||
};
|
||||
|
||||
class MemoryFakeAlwaysReadZero : public Memory {
|
||||
public:
|
||||
MemoryFakeAlwaysReadZero() = default;
|
||||
virtual ~MemoryFakeAlwaysReadZero() = default;
|
||||
|
||||
bool Read(uint64_t, void* buffer, size_t size) override {
|
||||
memset(buffer, 0, size);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // _LIBUNWINDSTACK_TESTS_MEMORY_FAKE_H
|
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
* 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 <android-base/test_utils.h>
|
||||
#include <android-base/file.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "Memory.h"
|
||||
|
||||
#include "LogFake.h"
|
||||
|
||||
class MemoryFileTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
ResetLogs();
|
||||
tf_ = new TemporaryFile;
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
delete tf_;
|
||||
}
|
||||
|
||||
void WriteTestData() {
|
||||
ASSERT_TRUE(android::base::WriteStringToFd("0123456789abcdefghijklmnopqrstuvxyz", tf_->fd));
|
||||
}
|
||||
|
||||
MemoryFileAtOffset memory_;
|
||||
|
||||
TemporaryFile* tf_ = nullptr;
|
||||
};
|
||||
|
||||
TEST_F(MemoryFileTest, offset_0) {
|
||||
WriteTestData();
|
||||
|
||||
ASSERT_TRUE(memory_.Init(tf_->path, 0));
|
||||
std::vector<char> buffer(11);
|
||||
ASSERT_TRUE(memory_.Read(0, buffer.data(), 10));
|
||||
buffer[10] = '\0';
|
||||
ASSERT_STREQ("0123456789", buffer.data());
|
||||
}
|
||||
|
||||
TEST_F(MemoryFileTest, offset_non_zero) {
|
||||
WriteTestData();
|
||||
|
||||
ASSERT_TRUE(memory_.Init(tf_->path, 10));
|
||||
std::vector<char> buffer(11);
|
||||
ASSERT_TRUE(memory_.Read(0, buffer.data(), 10));
|
||||
buffer[10] = '\0';
|
||||
ASSERT_STREQ("abcdefghij", buffer.data());
|
||||
}
|
||||
|
||||
TEST_F(MemoryFileTest, offset_non_zero_larger_than_pagesize) {
|
||||
size_t pagesize = getpagesize();
|
||||
std::string large_string;
|
||||
for (size_t i = 0; i < pagesize; i++) {
|
||||
large_string += '1';
|
||||
}
|
||||
large_string += "012345678901234abcdefgh";
|
||||
ASSERT_TRUE(android::base::WriteStringToFd(large_string, tf_->fd));
|
||||
|
||||
ASSERT_TRUE(memory_.Init(tf_->path, pagesize + 15));
|
||||
std::vector<char> buffer(9);
|
||||
ASSERT_TRUE(memory_.Read(0, buffer.data(), 8));
|
||||
buffer[8] = '\0';
|
||||
ASSERT_STREQ("abcdefgh", buffer.data());
|
||||
}
|
||||
|
||||
TEST_F(MemoryFileTest, offset_pagesize_aligned) {
|
||||
size_t pagesize = getpagesize();
|
||||
std::string data;
|
||||
for (size_t i = 0; i < 2 * pagesize; i++) {
|
||||
data += static_cast<char>((i / pagesize) + '0');
|
||||
data += static_cast<char>((i % 10) + '0');
|
||||
}
|
||||
ASSERT_TRUE(android::base::WriteStringToFd(data, tf_->fd));
|
||||
ASSERT_TRUE(memory_.Init(tf_->path, 2 * pagesize));
|
||||
std::vector<char> buffer(11);
|
||||
ASSERT_TRUE(memory_.Read(0, buffer.data(), 10));
|
||||
buffer[10] = '\0';
|
||||
std::string expected_str;
|
||||
for (size_t i = 0; i < 5; i++) {
|
||||
expected_str += '1';
|
||||
expected_str += static_cast<char>(((i + pagesize) % 10) + '0');
|
||||
}
|
||||
ASSERT_STREQ(expected_str.c_str(), buffer.data());
|
||||
}
|
||||
|
||||
TEST_F(MemoryFileTest, offset_pagesize_aligned_plus_extra) {
|
||||
size_t pagesize = getpagesize();
|
||||
std::string data;
|
||||
for (size_t i = 0; i < 2 * pagesize; i++) {
|
||||
data += static_cast<char>((i / pagesize) + '0');
|
||||
data += static_cast<char>((i % 10) + '0');
|
||||
}
|
||||
ASSERT_TRUE(android::base::WriteStringToFd(data, tf_->fd));
|
||||
ASSERT_TRUE(memory_.Init(tf_->path, 2 * pagesize + 10));
|
||||
std::vector<char> buffer(11);
|
||||
ASSERT_TRUE(memory_.Read(0, buffer.data(), 10));
|
||||
buffer[10] = '\0';
|
||||
std::string expected_str;
|
||||
for (size_t i = 0; i < 5; i++) {
|
||||
expected_str += '1';
|
||||
expected_str += static_cast<char>(((i + pagesize + 5) % 10) + '0');
|
||||
}
|
||||
ASSERT_STREQ(expected_str.c_str(), buffer.data());
|
||||
}
|
||||
|
||||
TEST_F(MemoryFileTest, read_error) {
|
||||
std::string data;
|
||||
for (size_t i = 0; i < 5000; i++) {
|
||||
data += static_cast<char>((i % 10) + '0');
|
||||
}
|
||||
ASSERT_TRUE(android::base::WriteStringToFd(data, tf_->fd));
|
||||
|
||||
std::vector<char> buffer(100);
|
||||
|
||||
// Read before init.
|
||||
ASSERT_FALSE(memory_.Read(0, buffer.data(), 10));
|
||||
|
||||
ASSERT_TRUE(memory_.Init(tf_->path, 0));
|
||||
|
||||
ASSERT_FALSE(memory_.Read(10000, buffer.data(), 10));
|
||||
ASSERT_FALSE(memory_.Read(5000, buffer.data(), 10));
|
||||
ASSERT_FALSE(memory_.Read(4990, buffer.data(), 11));
|
||||
ASSERT_TRUE(memory_.Read(4990, buffer.data(), 10));
|
||||
ASSERT_FALSE(memory_.Read(4999, buffer.data(), 2));
|
||||
ASSERT_TRUE(memory_.Read(4999, buffer.data(), 1));
|
||||
}
|
||||
|
||||
TEST_F(MemoryFileTest, read_string) {
|
||||
std::string value("name_in_file");
|
||||
ASSERT_TRUE(android::base::WriteFully(tf_->fd, value.c_str(), value.size() + 1));
|
||||
|
||||
std::string name;
|
||||
ASSERT_TRUE(memory_.Init(tf_->path, 0));
|
||||
ASSERT_TRUE(memory_.ReadString(0, &name));
|
||||
ASSERT_EQ("name_in_file", name);
|
||||
ASSERT_TRUE(memory_.ReadString(5, &name));
|
||||
ASSERT_EQ("in_file", name);
|
||||
}
|
||||
|
||||
TEST_F(MemoryFileTest, read_string_error) {
|
||||
std::vector<uint8_t> buffer = { 0x23, 0x32, 0x45 };
|
||||
ASSERT_TRUE(android::base::WriteFully(tf_->fd, buffer.data(), buffer.size()));
|
||||
|
||||
std::string name;
|
||||
ASSERT_TRUE(memory_.Init(tf_->path, 0));
|
||||
|
||||
// Read from a non-existant address.
|
||||
ASSERT_FALSE(memory_.ReadString(100, &name));
|
||||
|
||||
// This should fail because there is no terminating \0
|
||||
ASSERT_FALSE(memory_.ReadString(0, &name));
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "Memory.h"
|
||||
|
||||
#include "LogFake.h"
|
||||
|
||||
class MemoryLocalTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
ResetLogs();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(MemoryLocalTest, read) {
|
||||
std::vector<uint8_t> src(1024);
|
||||
memset(src.data(), 0x4c, 1024);
|
||||
|
||||
MemoryLocal local;
|
||||
|
||||
std::vector<uint8_t> dst(1024);
|
||||
ASSERT_TRUE(local.Read(reinterpret_cast<uint64_t>(src.data()), dst.data(), 1024));
|
||||
ASSERT_EQ(0, memcmp(src.data(), dst.data(), 1024));
|
||||
for (size_t i = 0; i < 1024; i++) {
|
||||
ASSERT_EQ(0x4cU, dst[i]);
|
||||
}
|
||||
|
||||
memset(src.data(), 0x23, 512);
|
||||
ASSERT_TRUE(local.Read(reinterpret_cast<uint64_t>(src.data()), dst.data(), 1024));
|
||||
ASSERT_EQ(0, memcmp(src.data(), dst.data(), 1024));
|
||||
for (size_t i = 0; i < 512; i++) {
|
||||
ASSERT_EQ(0x23U, dst[i]);
|
||||
}
|
||||
for (size_t i = 512; i < 1024; i++) {
|
||||
ASSERT_EQ(0x4cU, dst[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(MemoryLocalTest, read_string) {
|
||||
std::string name("string_in_memory");
|
||||
|
||||
MemoryLocal local;
|
||||
|
||||
std::vector<uint8_t> dst(1024);
|
||||
std::string dst_name;
|
||||
ASSERT_TRUE(local.ReadString(reinterpret_cast<uint64_t>(name.c_str()), &dst_name));
|
||||
ASSERT_EQ("string_in_memory", dst_name);
|
||||
|
||||
ASSERT_TRUE(local.ReadString(reinterpret_cast<uint64_t>(&name[7]), &dst_name));
|
||||
ASSERT_EQ("in_memory", dst_name);
|
||||
|
||||
ASSERT_TRUE(local.ReadString(reinterpret_cast<uint64_t>(&name[7]), &dst_name, 10));
|
||||
ASSERT_EQ("in_memory", dst_name);
|
||||
|
||||
ASSERT_FALSE(local.ReadString(reinterpret_cast<uint64_t>(&name[7]), &dst_name, 9));
|
||||
}
|
||||
|
||||
TEST_F(MemoryLocalTest, read_illegal) {
|
||||
MemoryLocal local;
|
||||
|
||||
std::vector<uint8_t> dst(100);
|
||||
ASSERT_FALSE(local.Read(0, dst.data(), 1));
|
||||
ASSERT_FALSE(local.Read(0, dst.data(), 100));
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "Memory.h"
|
||||
|
||||
#include "LogFake.h"
|
||||
#include "MemoryFake.h"
|
||||
|
||||
class MemoryRangeTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
ResetLogs();
|
||||
memory_ = new MemoryFake;
|
||||
}
|
||||
|
||||
MemoryFake* memory_;
|
||||
};
|
||||
|
||||
TEST_F(MemoryRangeTest, read) {
|
||||
std::vector<uint8_t> src(1024);
|
||||
memset(src.data(), 0x4c, 1024);
|
||||
memory_->SetMemory(9001, src);
|
||||
|
||||
MemoryRange range(memory_, 9001, 9001 + src.size());
|
||||
|
||||
std::vector<uint8_t> dst(1024);
|
||||
ASSERT_TRUE(range.Read(0, dst.data(), src.size()));
|
||||
for (size_t i = 0; i < 1024; i++) {
|
||||
ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(MemoryRangeTest, read_near_limit) {
|
||||
std::vector<uint8_t> src(4096);
|
||||
memset(src.data(), 0x4c, 4096);
|
||||
memory_->SetMemory(1000, src);
|
||||
|
||||
MemoryRange range(memory_, 1000, 2024);
|
||||
|
||||
std::vector<uint8_t> dst(1024);
|
||||
ASSERT_TRUE(range.Read(1020, dst.data(), 4));
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
|
||||
}
|
||||
|
||||
// Verify that reads outside of the range will fail.
|
||||
ASSERT_FALSE(range.Read(1020, dst.data(), 5));
|
||||
ASSERT_FALSE(range.Read(1024, dst.data(), 1));
|
||||
ASSERT_FALSE(range.Read(1024, dst.data(), 1024));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRangeTest, read_string_past_end) {
|
||||
std::string name("0123456789");
|
||||
memory_->SetMemory(0, name);
|
||||
|
||||
// Verify a read past the range fails.
|
||||
MemoryRange range(memory_, 0, 5);
|
||||
std::string dst_name;
|
||||
ASSERT_FALSE(range.ReadString(0, &dst_name));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRangeTest, read_string_to_end) {
|
||||
std::string name("0123456789");
|
||||
memory_->SetMemory(30, name);
|
||||
|
||||
// Verify the range going to the end of the string works.
|
||||
MemoryRange range(memory_, 30, 30 + name.size() + 1);
|
||||
std::string dst_name;
|
||||
ASSERT_TRUE(range.ReadString(0, &dst_name));
|
||||
ASSERT_EQ("0123456789", dst_name);
|
||||
}
|
||||
|
||||
TEST_F(MemoryRangeTest, read_string_fencepost) {
|
||||
std::string name("0123456789");
|
||||
memory_->SetMemory(10, name);
|
||||
|
||||
// Verify the range set to one byte less than the end of the string fails.
|
||||
MemoryRange range(memory_, 10, 10 + name.size());
|
||||
std::string dst_name;
|
||||
ASSERT_FALSE(range.ReadString(0, &dst_name));
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* 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 <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/test_utils.h>
|
||||
#include <android-base/file.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "Memory.h"
|
||||
|
||||
#include "LogFake.h"
|
||||
|
||||
class MemoryRemoteTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
ResetLogs();
|
||||
}
|
||||
|
||||
static uint64_t NanoTime() {
|
||||
struct timespec t = { 0, 0 };
|
||||
clock_gettime(CLOCK_MONOTONIC, &t);
|
||||
return static_cast<uint64_t>(t.tv_sec * NS_PER_SEC + t.tv_nsec);
|
||||
}
|
||||
|
||||
static bool Attach(pid_t pid) {
|
||||
if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t start = NanoTime();
|
||||
siginfo_t si;
|
||||
while (TEMP_FAILURE_RETRY(ptrace(PTRACE_GETSIGINFO, pid, 0, &si)) < 0 && errno == ESRCH) {
|
||||
if ((NanoTime() - start) > 10 * NS_PER_SEC) {
|
||||
printf("%d: Failed to stop after 10 seconds.\n", pid);
|
||||
return false;
|
||||
}
|
||||
usleep(30);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool Detach(pid_t pid) {
|
||||
return ptrace(PTRACE_DETACH, pid, 0, 0) == 0;
|
||||
}
|
||||
|
||||
static constexpr size_t NS_PER_SEC = 1000000000ULL;
|
||||
};
|
||||
|
||||
TEST_F(MemoryRemoteTest, read) {
|
||||
std::vector<uint8_t> src(1024);
|
||||
memset(src.data(), 0x4c, 1024);
|
||||
|
||||
pid_t pid;
|
||||
if ((pid = fork()) == 0) {
|
||||
while (true);
|
||||
exit(1);
|
||||
}
|
||||
ASSERT_LT(0, pid);
|
||||
|
||||
ASSERT_TRUE(Attach(pid));
|
||||
|
||||
MemoryRemote remote(pid);
|
||||
|
||||
std::vector<uint8_t> dst(1024);
|
||||
ASSERT_TRUE(remote.Read(reinterpret_cast<uint64_t>(src.data()), dst.data(), 1024));
|
||||
for (size_t i = 0; i < 1024; i++) {
|
||||
ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
|
||||
}
|
||||
|
||||
ASSERT_TRUE(Detach(pid));
|
||||
|
||||
kill(pid, SIGKILL);
|
||||
}
|
||||
|
||||
TEST_F(MemoryRemoteTest, read_fail) {
|
||||
int pagesize = getpagesize();
|
||||
void* src = mmap(nullptr, pagesize * 2, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE,-1, 0);
|
||||
memset(src, 0x4c, pagesize * 2);
|
||||
ASSERT_NE(MAP_FAILED, src);
|
||||
// Put a hole right after the first page.
|
||||
ASSERT_EQ(0, munmap(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(src) + pagesize),
|
||||
pagesize));
|
||||
|
||||
pid_t pid;
|
||||
if ((pid = fork()) == 0) {
|
||||
while (true);
|
||||
exit(1);
|
||||
}
|
||||
ASSERT_LT(0, pid);
|
||||
|
||||
ASSERT_TRUE(Attach(pid));
|
||||
|
||||
MemoryRemote remote(pid);
|
||||
|
||||
std::vector<uint8_t> dst(pagesize);
|
||||
ASSERT_TRUE(remote.Read(reinterpret_cast<uint64_t>(src), dst.data(), pagesize));
|
||||
for (size_t i = 0; i < 1024; i++) {
|
||||
ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
|
||||
}
|
||||
|
||||
ASSERT_FALSE(remote.Read(reinterpret_cast<uint64_t>(src) + pagesize, dst.data(), 1));
|
||||
ASSERT_TRUE(remote.Read(reinterpret_cast<uint64_t>(src) + pagesize - 1, dst.data(), 1));
|
||||
ASSERT_FALSE(remote.Read(reinterpret_cast<uint64_t>(src) + pagesize - 4, dst.data(), 8));
|
||||
|
||||
ASSERT_EQ(0, munmap(src, pagesize));
|
||||
|
||||
ASSERT_TRUE(Detach(pid));
|
||||
|
||||
kill(pid, SIGKILL);
|
||||
}
|
||||
|
||||
TEST_F(MemoryRemoteTest, read_illegal) {
|
||||
pid_t pid;
|
||||
if ((pid = fork()) == 0) {
|
||||
while (true);
|
||||
exit(1);
|
||||
}
|
||||
ASSERT_LT(0, pid);
|
||||
|
||||
ASSERT_TRUE(Attach(pid));
|
||||
|
||||
MemoryRemote remote(pid);
|
||||
|
||||
std::vector<uint8_t> dst(100);
|
||||
ASSERT_FALSE(remote.Read(0, dst.data(), 1));
|
||||
ASSERT_FALSE(remote.Read(0, dst.data(), 100));
|
||||
|
||||
ASSERT_TRUE(Detach(pid));
|
||||
|
||||
kill(pid, SIGKILL);
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "Regs.h"
|
||||
|
||||
class RegsTest : public ::testing::Test {};
|
||||
|
||||
TEST_F(RegsTest, regs32) {
|
||||
Regs32 regs32(10, 20, 30);
|
||||
|
||||
ASSERT_EQ(10U, regs32.pc_reg());
|
||||
ASSERT_EQ(20U, regs32.sp_reg());
|
||||
ASSERT_EQ(30U, regs32.total_regs());
|
||||
|
||||
uint32_t* raw = reinterpret_cast<uint32_t*>(regs32.raw_data());
|
||||
for (size_t i = 0; i < 30; i++) {
|
||||
raw[i] = 0xf0000000 + i;
|
||||
}
|
||||
|
||||
ASSERT_EQ(0xf000000aU, regs32.pc());
|
||||
ASSERT_EQ(0xf0000014U, regs32.sp());
|
||||
|
||||
ASSERT_EQ(0xf0000001U, regs32[1]);
|
||||
regs32[1] = 10;
|
||||
ASSERT_EQ(10U, regs32[1]);
|
||||
|
||||
ASSERT_EQ(0xf000001dU, regs32[29]);
|
||||
}
|
||||
|
||||
TEST_F(RegsTest, regs64) {
|
||||
Regs64 regs64(10, 20, 30);
|
||||
|
||||
ASSERT_EQ(10U, regs64.pc_reg());
|
||||
ASSERT_EQ(20U, regs64.sp_reg());
|
||||
ASSERT_EQ(30U, regs64.total_regs());
|
||||
|
||||
uint64_t* raw = reinterpret_cast<uint64_t*>(regs64.raw_data());
|
||||
for (size_t i = 0; i < 30; i++) {
|
||||
raw[i] = 0xf123456780000000UL + i;
|
||||
}
|
||||
|
||||
ASSERT_EQ(0xf12345678000000aUL, regs64.pc());
|
||||
ASSERT_EQ(0xf123456780000014UL, regs64.sp());
|
||||
|
||||
ASSERT_EQ(0xf123456780000008U, regs64[8]);
|
||||
regs64[8] = 10;
|
||||
ASSERT_EQ(10U, regs64[8]);
|
||||
|
||||
ASSERT_EQ(0xf12345678000001dU, regs64[29]);
|
||||
}
|
Loading…
Reference in New Issue