Merge "New version of unwinder."

This commit is contained in:
Christopher Ferris 2017-01-30 21:01:56 +00:00 committed by Gerrit Code Review
commit e69b1b9831
21 changed files with 3861 additions and 0 deletions

129
libunwindstack/Android.bp Normal file
View File

@ -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",
],
}

680
libunwindstack/ArmExidx.cpp Normal file
View File

@ -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;
}

103
libunwindstack/ArmExidx.h Normal file
View File

@ -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

53
libunwindstack/Log.cpp Normal file
View File

@ -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);
}

25
libunwindstack/Log.h Normal file
View File

@ -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

135
libunwindstack/Machine.h Normal file
View File

@ -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

180
libunwindstack/Memory.cpp Normal file
View File

@ -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;
}

121
libunwindstack/Memory.h Normal file
View File

@ -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

77
libunwindstack/Regs.h Normal file
View File

@ -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

View File

@ -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"));

View File

@ -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());
}

View File

@ -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*) {
}

View File

@ -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

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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

View File

@ -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));
}

View File

@ -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));
}

View File

@ -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));
}

View File

@ -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);
}

View File

@ -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]);
}