#include "base/profiler/chrome_unwinder_android_32.h"
#include <algorithm>
#include "base/compiler_specific.h"
#include "base/memory/aligned_memory.h"
#include "base/profiler/chrome_unwind_info_android_32.h"
#include "base/profiler/register_context_registers.h"
#include "base/profiler/stack_sampling_profiler_test_util.h"
#include "base/test/gtest_util.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
TEST(ChromeAndroid32UnwindInstructionTest,
TestSmallStackPointerIncrementMinValue) {
RegisterContext thread_context = {};
const uint8_t instruction = 0b00000000;
const uint8_t* current_instruction = &instruction;
thread_context.arm_sp = 0x10000000;
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::kInstructionPending);
EXPECT_FALSE(pc_was_updated);
UNSAFE_TODO(ASSERT_EQ(current_instruction, &instruction + 1));
EXPECT_EQ(0x10000004ul, thread_context.arm_sp);
}
TEST(ChromeAndroid32UnwindInstructionTest,
TestSmallStackPointerIncrementMidValue) {
RegisterContext thread_context = {};
const uint8_t instruction = 0b00000100;
const uint8_t* current_instruction = &instruction;
thread_context.arm_sp = 0x10000000;
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::kInstructionPending);
EXPECT_FALSE(pc_was_updated);
UNSAFE_TODO(ASSERT_EQ(current_instruction, &instruction + 1));
EXPECT_EQ(0x10000014ul, thread_context.arm_sp);
}
TEST(ChromeAndroid32UnwindInstructionTest,
TestSmallStackPointerIncrementMaxValue) {
RegisterContext thread_context = {};
const uint8_t instruction = 0b00111111;
const uint8_t* current_instruction = &instruction;
thread_context.arm_sp = 0x10000000;
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::kInstructionPending);
EXPECT_FALSE(pc_was_updated);
UNSAFE_TODO(ASSERT_EQ(current_instruction, &instruction + 1));
EXPECT_EQ(0x10000100ul, thread_context.arm_sp);
}
TEST(ChromeAndroid32UnwindInstructionTest,
TestSmallStackPointerIncrementOverflow) {
RegisterContext thread_context = {};
const uint8_t instruction = 0b00111111;
const uint8_t* current_instruction = &instruction;
thread_context.arm_sp = 0xffffffff;
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::kAborted);
UNSAFE_TODO(ASSERT_EQ(current_instruction, &instruction + 1));
EXPECT_EQ(0xffffffff, thread_context.arm_sp);
}
TEST(ChromeAndroid32UnwindInstructionTest,
TestSmallStackPointerDecrementMinValue) {
RegisterContext thread_context = {};
const uint8_t instruction = 0b01000000;
const uint8_t* current_instruction = &instruction;
thread_context.arm_sp = 0x10000000;
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::kInstructionPending);
EXPECT_FALSE(pc_was_updated);
UNSAFE_TODO(ASSERT_EQ(current_instruction, &instruction + 1));
EXPECT_EQ(0x0ffffffcul, thread_context.arm_sp);
}
TEST(ChromeAndroid32UnwindInstructionTest,
TestSmallStackPointerDecrementMidValue) {
RegisterContext thread_context = {};
const uint8_t instruction = 0b01000100;
const uint8_t* current_instruction = &instruction;
thread_context.arm_sp = 0x10000000;
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::kInstructionPending);
EXPECT_FALSE(pc_was_updated);
UNSAFE_TODO(ASSERT_EQ(current_instruction, &instruction + 1));
EXPECT_EQ(0x0fffffecul, thread_context.arm_sp);
}
TEST(ChromeAndroid32UnwindInstructionTest,
TestSmallStackPointerDecrementMaxValue) {
RegisterContext thread_context = {};
const uint8_t instruction = 0b01111111;
const uint8_t* current_instruction = &instruction;
thread_context.arm_sp = 0x10000000;
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::kInstructionPending);
EXPECT_FALSE(pc_was_updated);
UNSAFE_TODO(ASSERT_EQ(current_instruction, &instruction + 1));
EXPECT_EQ(0x0fffff00ul, thread_context.arm_sp);
}
TEST(ChromeAndroid32UnwindInstructionTest,
TestSmallStackPointerDecrementUnderflow) {
RegisterContext thread_context = {};
const uint8_t instruction = 0b01111111;
const uint8_t* current_instruction = &instruction;
thread_context.arm_sp = 0x00000000;
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::kAborted);
EXPECT_FALSE(pc_was_updated);
UNSAFE_TODO(ASSERT_EQ(current_instruction, &instruction + 1));
EXPECT_EQ(0x0ul, thread_context.arm_sp);
}
using ChromeAndroidUnwindSetStackPointerFromRegisterValueTest =
::testing::TestWithParam<uint8_t>;
INSTANTIATE_TEST_SUITE_P(
All,
ChromeAndroidUnwindSetStackPointerFromRegisterValueTest,
::testing::Values(4, 5, 6, 7, 8, 9, 10, 11, 12, 14));
TEST_P(ChromeAndroidUnwindSetStackPointerFromRegisterValueTest,
TestSetStackPointerFromRegisterValue) {
const uint8_t register_index = GetParam();
RegisterContext thread_context = {};
thread_context.arm_r0 = 100;
thread_context.arm_r1 = 101;
thread_context.arm_r2 = 102;
thread_context.arm_r3 = 103;
thread_context.arm_r4 = 104;
thread_context.arm_r5 = 105;
thread_context.arm_r6 = 106;
thread_context.arm_r7 = 107;
thread_context.arm_r8 = 108;
thread_context.arm_r9 = 109;
thread_context.arm_r10 = 110;
thread_context.arm_fp = 111;
thread_context.arm_ip = 112;
thread_context.arm_lr = 114;
const uint8_t instruction = 0b10010000 + register_index;
const uint8_t* current_instruction = &instruction;
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::kInstructionPending);
EXPECT_FALSE(pc_was_updated);
UNSAFE_TODO(ASSERT_EQ(current_instruction, &instruction + 1));
EXPECT_EQ(100ul + register_index, thread_context.arm_sp);
}
TEST(ChromeAndroid32UnwindInstructionTest, TestCompleteWithNoPriorPCUpdate) {
RegisterContext thread_context = {};
thread_context.arm_lr = 114;
thread_context.arm_pc = 115;
const uint8_t instruction = 0b10110000;
const uint8_t* current_instruction = &instruction;
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::kCompleted);
UNSAFE_TODO(ASSERT_EQ(current_instruction, &instruction + 1));
EXPECT_EQ(114ul, thread_context.arm_pc);
}
TEST(ChromeAndroid32UnwindInstructionTest, TestCompleteWithPriorPCUpdate) {
RegisterContext thread_context = {};
thread_context.arm_lr = 114;
thread_context.arm_pc = 115;
const uint8_t instruction = 0b10110000;
const uint8_t* current_instruction = &instruction;
bool pc_was_updated = true;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::kCompleted);
UNSAFE_TODO(ASSERT_EQ(current_instruction, &instruction + 1));
EXPECT_EQ(115ul, thread_context.arm_pc);
}
TEST(ChromeAndroid32UnwindInstructionTest,
TestPopDiscontinuousRegistersIncludingPC) {
RegisterContext thread_context = {};
thread_context.arm_r0 = 100;
thread_context.arm_r1 = 101;
thread_context.arm_r2 = 102;
thread_context.arm_r3 = 103;
thread_context.arm_r4 = 104;
thread_context.arm_r5 = 105;
thread_context.arm_r6 = 106;
thread_context.arm_r7 = 107;
thread_context.arm_r8 = 108;
thread_context.arm_r9 = 109;
thread_context.arm_r10 = 110;
thread_context.arm_fp = 111;
thread_context.arm_ip = 112;
thread_context.arm_lr = 113;
thread_context.arm_pc = 114;
const uintptr_t stack[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
thread_context.arm_sp = reinterpret_cast<uintptr_t>(&stack[0]);
const uint8_t instruction[] = {0b10001001, 0b00010001};
const uint8_t* current_instruction = instruction;
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::kInstructionPending);
EXPECT_TRUE(pc_was_updated);
UNSAFE_TODO(ASSERT_EQ(current_instruction, instruction + 2));
UNSAFE_TODO(EXPECT_EQ(reinterpret_cast<uintptr_t>(&stack[0] + 4),
thread_context.arm_sp));
EXPECT_EQ(100ul, thread_context.arm_r0);
EXPECT_EQ(101ul, thread_context.arm_r1);
EXPECT_EQ(102ul, thread_context.arm_r2);
EXPECT_EQ(103ul, thread_context.arm_r3);
EXPECT_EQ(1ul, thread_context.arm_r4);
EXPECT_EQ(105ul, thread_context.arm_r5);
EXPECT_EQ(106ul, thread_context.arm_r6);
EXPECT_EQ(107ul, thread_context.arm_r7);
EXPECT_EQ(2ul, thread_context.arm_r8);
EXPECT_EQ(109ul, thread_context.arm_r9);
EXPECT_EQ(110ul, thread_context.arm_r10);
EXPECT_EQ(111ul, thread_context.arm_fp);
EXPECT_EQ(3ul, thread_context.arm_ip);
EXPECT_EQ(113ul, thread_context.arm_lr);
EXPECT_EQ(4ul, thread_context.arm_pc);
}
TEST(ChromeAndroid32UnwindInstructionTest, TestPopDiscontinuousRegisters) {
RegisterContext thread_context = {};
thread_context.arm_r0 = 100;
thread_context.arm_r1 = 101;
thread_context.arm_r2 = 102;
thread_context.arm_r3 = 103;
thread_context.arm_r4 = 104;
thread_context.arm_r5 = 105;
thread_context.arm_r6 = 106;
thread_context.arm_r7 = 107;
thread_context.arm_r8 = 108;
thread_context.arm_r9 = 109;
thread_context.arm_r10 = 110;
thread_context.arm_fp = 111;
thread_context.arm_ip = 112;
thread_context.arm_lr = 113;
thread_context.arm_pc = 114;
const uintptr_t stack[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
thread_context.arm_sp = reinterpret_cast<uintptr_t>(&stack[0]);
const uint8_t instruction[] = {0b10000001, 0b00010001};
const uint8_t* current_instruction = instruction;
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::kInstructionPending);
EXPECT_FALSE(pc_was_updated);
UNSAFE_TODO(ASSERT_EQ(current_instruction, instruction + 2));
UNSAFE_TODO(EXPECT_EQ(reinterpret_cast<uintptr_t>(&stack[0] + 3),
thread_context.arm_sp));
EXPECT_EQ(100ul, thread_context.arm_r0);
EXPECT_EQ(101ul, thread_context.arm_r1);
EXPECT_EQ(102ul, thread_context.arm_r2);
EXPECT_EQ(103ul, thread_context.arm_r3);
EXPECT_EQ(1ul, thread_context.arm_r4);
EXPECT_EQ(105ul, thread_context.arm_r5);
EXPECT_EQ(106ul, thread_context.arm_r6);
EXPECT_EQ(107ul, thread_context.arm_r7);
EXPECT_EQ(2ul, thread_context.arm_r8);
EXPECT_EQ(109ul, thread_context.arm_r9);
EXPECT_EQ(110ul, thread_context.arm_r10);
EXPECT_EQ(111ul, thread_context.arm_fp);
EXPECT_EQ(3ul, thread_context.arm_ip);
EXPECT_EQ(113ul, thread_context.arm_lr);
EXPECT_EQ(114ul, thread_context.arm_pc);
}
TEST(ChromeAndroid32UnwindInstructionTest,
TestPopDiscontinuousRegistersOverflow) {
RegisterContext thread_context = {};
thread_context.arm_r0 = 100;
thread_context.arm_r1 = 101;
thread_context.arm_r2 = 102;
thread_context.arm_r3 = 103;
thread_context.arm_r4 = 104;
thread_context.arm_r5 = 105;
thread_context.arm_r6 = 106;
thread_context.arm_r7 = 107;
thread_context.arm_r8 = 108;
thread_context.arm_r9 = 109;
thread_context.arm_r10 = 110;
thread_context.arm_fp = 111;
thread_context.arm_ip = 112;
thread_context.arm_lr = 113;
thread_context.arm_pc = 114;
thread_context.arm_sp = 0xffffffff;
const uint8_t instruction[] = {0b10001001, 0b00010001};
const uint8_t* current_instruction = instruction;
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::kAborted);
EXPECT_FALSE(pc_was_updated);
UNSAFE_TODO(ASSERT_EQ(current_instruction, instruction + 2));
EXPECT_EQ(0xffffffff, thread_context.arm_sp);
EXPECT_EQ(100ul, thread_context.arm_r0);
EXPECT_EQ(101ul, thread_context.arm_r1);
EXPECT_EQ(102ul, thread_context.arm_r2);
EXPECT_EQ(103ul, thread_context.arm_r3);
EXPECT_EQ(104ul, thread_context.arm_r4);
EXPECT_EQ(105ul, thread_context.arm_r5);
EXPECT_EQ(106ul, thread_context.arm_r6);
EXPECT_EQ(107ul, thread_context.arm_r7);
EXPECT_EQ(108ul, thread_context.arm_r8);
EXPECT_EQ(109ul, thread_context.arm_r9);
EXPECT_EQ(110ul, thread_context.arm_r10);
EXPECT_EQ(111ul, thread_context.arm_fp);
EXPECT_EQ(112ul, thread_context.arm_ip);
EXPECT_EQ(113ul, thread_context.arm_lr);
EXPECT_EQ(114ul, thread_context.arm_pc);
}
TEST(ChromeAndroid32UnwindInstructionTest, TestRefuseToUnwind) {
RegisterContext thread_context = {};
const uint8_t instruction[] = {0b10000000, 0b0};
const uint8_t* current_instruction = instruction;
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::kAborted);
EXPECT_FALSE(pc_was_updated);
UNSAFE_TODO(ASSERT_EQ(current_instruction, instruction + 2));
}
TEST(ChromeAndroid32UnwindInstructionTest,
TestPopRegistersIncludingR14MinRegisters) {
RegisterContext thread_context = {};
thread_context.arm_r0 = 100;
thread_context.arm_r1 = 101;
thread_context.arm_r2 = 102;
thread_context.arm_r3 = 103;
thread_context.arm_r4 = 104;
thread_context.arm_r5 = 105;
thread_context.arm_r6 = 106;
thread_context.arm_r7 = 107;
thread_context.arm_r8 = 108;
thread_context.arm_r9 = 109;
thread_context.arm_r10 = 110;
thread_context.arm_fp = 111;
thread_context.arm_ip = 112;
thread_context.arm_lr = 113;
const uintptr_t stack[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
thread_context.arm_sp = reinterpret_cast<uintptr_t>(&stack[0]);
const uint8_t instruction = 0b10101000;
const uint8_t* current_instruction = &instruction;
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::kInstructionPending);
EXPECT_FALSE(pc_was_updated);
UNSAFE_TODO(ASSERT_EQ(current_instruction, &instruction + 1));
UNSAFE_TODO(EXPECT_EQ(reinterpret_cast<uintptr_t>(&stack[0] + 2),
thread_context.arm_sp));
EXPECT_EQ(100ul, thread_context.arm_r0);
EXPECT_EQ(101ul, thread_context.arm_r1);
EXPECT_EQ(102ul, thread_context.arm_r2);
EXPECT_EQ(103ul, thread_context.arm_r3);
EXPECT_EQ(1ul, thread_context.arm_r4);
EXPECT_EQ(105ul, thread_context.arm_r5);
EXPECT_EQ(106ul, thread_context.arm_r6);
EXPECT_EQ(107ul, thread_context.arm_r7);
EXPECT_EQ(108ul, thread_context.arm_r8);
EXPECT_EQ(109ul, thread_context.arm_r9);
EXPECT_EQ(110ul, thread_context.arm_r10);
EXPECT_EQ(111ul, thread_context.arm_fp);
EXPECT_EQ(112ul, thread_context.arm_ip);
EXPECT_EQ(2ul, thread_context.arm_lr);
}
TEST(ChromeAndroid32UnwindInstructionTest,
TestPopRegistersIncludingR14MidRegisters) {
RegisterContext thread_context = {};
thread_context.arm_r0 = 100;
thread_context.arm_r1 = 101;
thread_context.arm_r2 = 102;
thread_context.arm_r3 = 103;
thread_context.arm_r4 = 104;
thread_context.arm_r5 = 105;
thread_context.arm_r6 = 106;
thread_context.arm_r7 = 107;
thread_context.arm_r8 = 108;
thread_context.arm_r9 = 109;
thread_context.arm_r10 = 110;
thread_context.arm_fp = 111;
thread_context.arm_ip = 112;
thread_context.arm_lr = 113;
const uintptr_t stack[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
thread_context.arm_sp = reinterpret_cast<uintptr_t>(&stack[0]);
const uint8_t instruction = 0b10101100;
const uint8_t* current_instruction = &instruction;
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::kInstructionPending);
EXPECT_FALSE(pc_was_updated);
UNSAFE_TODO(ASSERT_EQ(current_instruction, &instruction + 1));
UNSAFE_TODO(EXPECT_EQ(reinterpret_cast<uintptr_t>(&stack[0] + 6),
thread_context.arm_sp));
EXPECT_EQ(100ul, thread_context.arm_r0);
EXPECT_EQ(101ul, thread_context.arm_r1);
EXPECT_EQ(102ul, thread_context.arm_r2);
EXPECT_EQ(103ul, thread_context.arm_r3);
EXPECT_EQ(1ul, thread_context.arm_r4);
EXPECT_EQ(2ul, thread_context.arm_r5);
EXPECT_EQ(3ul, thread_context.arm_r6);
EXPECT_EQ(4ul, thread_context.arm_r7);
EXPECT_EQ(5ul, thread_context.arm_r8);
EXPECT_EQ(109ul, thread_context.arm_r9);
EXPECT_EQ(110ul, thread_context.arm_r10);
EXPECT_EQ(111ul, thread_context.arm_fp);
EXPECT_EQ(112ul, thread_context.arm_ip);
EXPECT_EQ(6ul, thread_context.arm_lr);
}
TEST(ChromeAndroid32UnwindInstructionTest,
TestPopRegistersIncludingR14MaxRegisters) {
RegisterContext thread_context = {};
thread_context.arm_r0 = 100;
thread_context.arm_r1 = 101;
thread_context.arm_r2 = 102;
thread_context.arm_r3 = 103;
thread_context.arm_r4 = 104;
thread_context.arm_r5 = 105;
thread_context.arm_r6 = 106;
thread_context.arm_r7 = 107;
thread_context.arm_r8 = 108;
thread_context.arm_r9 = 109;
thread_context.arm_r10 = 110;
thread_context.arm_fp = 111;
thread_context.arm_ip = 112;
thread_context.arm_lr = 113;
const uintptr_t stack[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
thread_context.arm_sp = reinterpret_cast<uintptr_t>(&stack[0]);
const uint8_t instruction = 0b10101111;
const uint8_t* current_instruction = &instruction;
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::kInstructionPending);
EXPECT_FALSE(pc_was_updated);
UNSAFE_TODO(ASSERT_EQ(current_instruction, &instruction + 1));
UNSAFE_TODO(EXPECT_EQ(reinterpret_cast<uintptr_t>(&stack[0] + 9),
thread_context.arm_sp));
EXPECT_EQ(100ul, thread_context.arm_r0);
EXPECT_EQ(101ul, thread_context.arm_r1);
EXPECT_EQ(102ul, thread_context.arm_r2);
EXPECT_EQ(103ul, thread_context.arm_r3);
EXPECT_EQ(1ul, thread_context.arm_r4);
EXPECT_EQ(2ul, thread_context.arm_r5);
EXPECT_EQ(3ul, thread_context.arm_r6);
EXPECT_EQ(4ul, thread_context.arm_r7);
EXPECT_EQ(5ul, thread_context.arm_r8);
EXPECT_EQ(6ul, thread_context.arm_r9);
EXPECT_EQ(7ul, thread_context.arm_r10);
EXPECT_EQ(8ul, thread_context.arm_fp);
EXPECT_EQ(112ul, thread_context.arm_ip);
EXPECT_EQ(9ul, thread_context.arm_lr);
}
TEST(ChromeAndroid32UnwindInstructionTest,
TestPopRegistersIncludingR14Overflow) {
RegisterContext thread_context = {};
thread_context.arm_r0 = 100;
thread_context.arm_r1 = 101;
thread_context.arm_r2 = 102;
thread_context.arm_r3 = 103;
thread_context.arm_r4 = 104;
thread_context.arm_r5 = 105;
thread_context.arm_r6 = 106;
thread_context.arm_r7 = 107;
thread_context.arm_r8 = 108;
thread_context.arm_r9 = 109;
thread_context.arm_r10 = 110;
thread_context.arm_fp = 111;
thread_context.arm_ip = 112;
thread_context.arm_lr = 113;
thread_context.arm_sp = 0xffffffff;
const uint8_t instruction = 0b10101111;
const uint8_t* current_instruction = &instruction;
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::kAborted);
EXPECT_FALSE(pc_was_updated);
UNSAFE_TODO(ASSERT_EQ(current_instruction, &instruction + 1));
EXPECT_EQ(0xffffffff, thread_context.arm_sp);
EXPECT_EQ(100ul, thread_context.arm_r0);
EXPECT_EQ(101ul, thread_context.arm_r1);
EXPECT_EQ(102ul, thread_context.arm_r2);
EXPECT_EQ(103ul, thread_context.arm_r3);
EXPECT_EQ(104ul, thread_context.arm_r4);
EXPECT_EQ(105ul, thread_context.arm_r5);
EXPECT_EQ(106ul, thread_context.arm_r6);
EXPECT_EQ(107ul, thread_context.arm_r7);
EXPECT_EQ(108ul, thread_context.arm_r8);
EXPECT_EQ(109ul, thread_context.arm_r9);
EXPECT_EQ(110ul, thread_context.arm_r10);
EXPECT_EQ(111ul, thread_context.arm_fp);
EXPECT_EQ(112ul, thread_context.arm_ip);
EXPECT_EQ(113ul, thread_context.arm_lr);
}
TEST(ChromeAndroid32UnwindInstructionTest,
TestBigStackPointerIncrementMinValue) {
RegisterContext thread_context = {};
thread_context.arm_sp = 0x10000000;
const uint8_t increment_0[] = {
0b10110010,
0b00000000,
};
const uint8_t* current_instruction = &increment_0[0];
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::kInstructionPending);
EXPECT_FALSE(pc_was_updated);
UNSAFE_TODO(
ASSERT_EQ(current_instruction, increment_0 + sizeof(increment_0)));
EXPECT_EQ(0x10000204ul, thread_context.arm_sp);
}
TEST(ChromeAndroid32UnwindInstructionTest,
TestBigStackPointerIncrementMidValue) {
RegisterContext thread_context = {};
thread_context.arm_sp = 0x10000000;
const uint8_t increment_4[] = {
0b10110010,
0b00000100,
};
const uint8_t* current_instruction = &increment_4[0];
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::kInstructionPending);
EXPECT_FALSE(pc_was_updated);
UNSAFE_TODO(
ASSERT_EQ(current_instruction, increment_4 + sizeof(increment_4)));
EXPECT_EQ(0x10000214ul, thread_context.arm_sp);
}
TEST(ChromeAndroid32UnwindInstructionTest,
TestBigStackPointerIncrementLargeValue) {
RegisterContext thread_context = {};
thread_context.arm_sp = 0x10000000;
const uint8_t increment_128[] = {
0b10110010,
0b10000000,
0b00000001,
};
const uint8_t* current_instruction = &increment_128[0];
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::kInstructionPending);
EXPECT_FALSE(pc_was_updated);
UNSAFE_TODO(
ASSERT_EQ(current_instruction, increment_128 + sizeof(increment_128)));
EXPECT_EQ(0x10000404ul, thread_context.arm_sp);
}
TEST(ChromeAndroid32UnwindInstructionTest,
TestBigStackPointerIncrementOverflow) {
RegisterContext thread_context = {};
thread_context.arm_sp = 0xffffffff;
const uint8_t increment_overflow[] = {
0b10110010,
0b10000000,
0b00000001,
};
const uint8_t* current_instruction = &increment_overflow[0];
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::kAborted);
EXPECT_FALSE(pc_was_updated);
UNSAFE_TODO(ASSERT_EQ(current_instruction,
increment_overflow + sizeof(increment_overflow)));
EXPECT_EQ(0xfffffffful, thread_context.arm_sp);
}
TEST(ChromeUnwinderAndroid32Test,
TestFunctionOffsetTableLookupExactMatchingOffset) {
const uint8_t function_offset_table[] = {
0b10000010,
0b00000001,
0b00000010,
0b10000000,
0b00000001,
0b00000011,
0b00000000,
0b00000100,
};
EXPECT_EQ(3ul, GetFirstUnwindInstructionIndexFromFunctionOffsetTableEntry(
&function_offset_table[0],
128));
}
TEST(ChromeUnwinderAndroid32Test,
TestFunctionOffsetTableLookupNonExactMatchingOffset) {
const uint8_t function_offset_table[] = {
0b10000010,
0b00000001,
0b00000010,
0b10000000,
0b00000001,
0b00000011,
0b00000000,
0b00000100,
};
EXPECT_EQ(3ul, GetFirstUnwindInstructionIndexFromFunctionOffsetTableEntry(
&function_offset_table[0],
129));
}
TEST(ChromeUnwinderAndroid32Test, TestFunctionOffsetTableLookupZeroOffset) {
const uint8_t function_offset_table[] = {
0b10000010,
0b00000001,
0b00000010,
0b10000000,
0b00000001,
0b00000011,
0b00000000,
0b00000100,
};
EXPECT_EQ(4ul, GetFirstUnwindInstructionIndexFromFunctionOffsetTableEntry(
&function_offset_table[0],
0));
}
TEST(ChromeUnwinderAndroid32Test, TestAddressTableLookupEntryInPage) {
const uint32_t page_start_instructions[] = {0, 2};
const FunctionTableEntry function_offset_table_indices[] = {
{
0,
20,
},
{
4,
40,
},
{
6,
70,
},
};
{
const uint32_t page_number = 0;
const uint32_t page_instruction_offset = 4;
const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
page_start_instructions, function_offset_table_indices,
(page_instruction_offset << 1) +
(page_number << 17));
ASSERT_NE(std::nullopt, entry_found);
EXPECT_EQ(0, entry_found->instruction_offset_from_function_start);
EXPECT_EQ(40ul, entry_found->function_offset_table_byte_index);
}
{
const uint32_t page_number = 0;
const uint32_t page_instruction_offset = 50;
const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
page_start_instructions, function_offset_table_indices,
(page_instruction_offset << 1) +
(page_number << 17));
ASSERT_NE(std::nullopt, entry_found);
EXPECT_EQ(46, entry_found->instruction_offset_from_function_start);
EXPECT_EQ(40ul, entry_found->function_offset_table_byte_index);
}
{
const uint32_t page_number = 1;
const uint32_t page_instruction_offset = 0xffff;
const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
page_start_instructions, function_offset_table_indices,
(page_instruction_offset << 1) +
(page_number << 17));
ASSERT_NE(std::nullopt, entry_found);
EXPECT_EQ(0xfff9, entry_found->instruction_offset_from_function_start);
EXPECT_EQ(70ul, entry_found->function_offset_table_byte_index);
}
}
TEST(ChromeUnwinderAndroid32Test, TestAddressTableLookupEmptyPage) {
const uint32_t page_start_instructions[] = {0, 1, 1};
const FunctionTableEntry function_offset_table_indices[] = {
{
0,
20,
},
{
6,
70,
},
};
const uint32_t page_number = 1;
const uint32_t page_instruction_offset = 4;
const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
page_start_instructions, function_offset_table_indices,
(page_instruction_offset << 1) +
(page_number << 17));
ASSERT_NE(std::nullopt, entry_found);
EXPECT_EQ(0x10004, entry_found->instruction_offset_from_function_start);
EXPECT_EQ(20ul, entry_found->function_offset_table_byte_index);
}
TEST(ChromeUnwinderAndroid32Test,
TestAddressTableLookupInvalidIntructionOffset) {
const uint32_t page_start_instructions[] = {0, 1};
const FunctionTableEntry function_offset_table_indices[] = {
{
0,
20,
},
{
6,
70,
},
};
{
const uint32_t page_number = 50;
const uint32_t page_instruction_offset = 6;
const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
page_start_instructions, function_offset_table_indices,
(page_instruction_offset << 1) +
(page_number << 17));
ASSERT_EQ(std::nullopt, entry_found);
}
{
const uint32_t page_number = 2;
const uint32_t page_instruction_offset = 0;
const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
page_start_instructions, function_offset_table_indices,
(page_instruction_offset << 1) +
(page_number << 17));
ASSERT_EQ(std::nullopt, entry_found);
}
}
TEST(ChromeUnwinderAndroid32Test,
TestAddressTableLookupOnSecondPageOfFunctionSpanningPageBoundary) {
const uint32_t page_start_instructions[] = {0, 1, 2};
const FunctionTableEntry function_offset_table_indices[] = {
{
0,
20,
},
{
6,
70,
},
{
10,
80,
}};
const uint32_t page_number = 1;
const uint32_t page_instruction_offset = 4;
const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
page_start_instructions, function_offset_table_indices,
(page_instruction_offset << 1) +
(page_number << 17));
ASSERT_NE(std::nullopt, entry_found);
EXPECT_EQ(0x10004, entry_found->instruction_offset_from_function_start);
EXPECT_EQ(20ul, entry_found->function_offset_table_byte_index);
}
TEST(ChromeUnwinderAndroid32Test,
TestAddressTableLookupWithinFunctionSpanningMultiplePages) {
const uint32_t page_start_instructions[] = {0, 1, 1, 1};
const FunctionTableEntry function_offset_table_indices[] = {
{
0,
20,
},
{
6,
70,
},
};
{
const uint32_t page_number = 0;
const uint32_t page_instruction_offset = 4;
const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
page_start_instructions, function_offset_table_indices,
(page_instruction_offset << 1) +
(page_number << 17));
ASSERT_NE(std::nullopt, entry_found);
EXPECT_EQ(0x4, entry_found->instruction_offset_from_function_start);
EXPECT_EQ(20ul, entry_found->function_offset_table_byte_index);
}
{
const uint32_t page_number = 1;
const uint32_t page_instruction_offset = 4;
const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
page_start_instructions, function_offset_table_indices,
(page_instruction_offset << 1) +
(page_number << 17));
ASSERT_NE(std::nullopt, entry_found);
EXPECT_EQ(0x10004, entry_found->instruction_offset_from_function_start);
EXPECT_EQ(20ul, entry_found->function_offset_table_byte_index);
}
{
const uint32_t page_number = 2;
const uint32_t page_instruction_offset = 4;
const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
page_start_instructions, function_offset_table_indices,
(page_instruction_offset << 1) +
(page_number << 17));
ASSERT_NE(std::nullopt, entry_found);
EXPECT_EQ(0x20004, entry_found->instruction_offset_from_function_start);
EXPECT_EQ(20ul, entry_found->function_offset_table_byte_index);
}
{
const uint32_t page_number = 3;
const uint32_t page_instruction_offset = 4;
const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
page_start_instructions, function_offset_table_indices,
(page_instruction_offset << 1) +
(page_number << 17));
ASSERT_NE(std::nullopt, entry_found);
EXPECT_EQ(0x30004, entry_found->instruction_offset_from_function_start);
EXPECT_EQ(20ul, entry_found->function_offset_table_byte_index);
}
}
const ModuleCache::Module* AddNativeModule(
ModuleCache* cache,
std::unique_ptr<const ModuleCache::Module> module) {
const ModuleCache::Module* module_ptr = module.get();
cache->AddCustomNativeModule(std::move(module));
return module_ptr;
}
TEST(ChromeUnwinderAndroid32Test, CanUnwindFrom) {
const uint32_t page_table[] = {0};
const FunctionTableEntry function_table[] = {{0, 0}};
const uint8_t function_offset_table[] = {0};
const uint8_t unwind_instruction_table[] = {0};
auto dummy_unwind_info = ChromeUnwindInfoAndroid32{
unwind_instruction_table,
function_offset_table,
function_table,
page_table,
};
auto chrome_module = std::make_unique<TestModule>(0x1000, 0x500);
auto non_chrome_module = std::make_unique<TestModule>(0x2000, 0x500);
ModuleCache module_cache;
ChromeUnwinderAndroid32 unwinder(dummy_unwind_info,
chrome_module->GetBaseAddress(),
chrome_module->GetBaseAddress() + 4);
unwinder.Initialize(&module_cache);
EXPECT_TRUE(unwinder.CanUnwindFrom({0x1100, chrome_module.get()}));
EXPECT_TRUE(unwinder.CanUnwindFrom({0x1000, chrome_module.get()}));
EXPECT_FALSE(unwinder.CanUnwindFrom({0x2100, non_chrome_module.get()}));
EXPECT_FALSE(unwinder.CanUnwindFrom({0x400, nullptr}));
}
namespace {
void ExpectFramesEq(const std::vector<Frame>& expected,
const std::vector<Frame>& actual) {
EXPECT_EQ(actual.size(), expected.size());
if (actual.size() != expected.size()) {
return;
}
for (size_t i = 0; i < actual.size(); i++) {
EXPECT_EQ(expected[i].module, actual[i].module);
EXPECT_EQ(expected[i].instruction_pointer, actual[i].instruction_pointer);
}
}
class AlignedStackMemory {
public:
AlignedStackMemory(std::initializer_list<uintptr_t> values)
: size_(values.size()),
stack_memory_(static_cast<uintptr_t*>(
AlignedAlloc(size_ * sizeof(uintptr_t), 2 * sizeof(uintptr_t)))) {
DCHECK_EQ(size_ % 2, 0u);
std::ranges::copy(values, stack_memory_.get());
}
uintptr_t stack_start_address() const {
return reinterpret_cast<uintptr_t>(stack_memory_.get());
}
uintptr_t stack_end_address() const {
return reinterpret_cast<uintptr_t>(
UNSAFE_TODO(stack_memory_.get() + size_));
}
private:
const uintptr_t size_;
const std::unique_ptr<uintptr_t, AlignedFreeDeleter> stack_memory_;
};
}
TEST(ChromeUnwinderAndroid32Test, TryUnwind) {
const uint32_t page_table[] = {0, 2};
const size_t number_of_pages = std::size(page_table);
const size_t page_size = 1 << 17;
const FunctionTableEntry function_table[] = {
{0, 0},
{0x10, 4},
{0x5, 8},
{0x20, 12},
};
const uint8_t function_offset_table[] = {
0x2,
0,
0x0,
1,
0x7f,
0,
0x0,
1,
0x78,
0,
0x0,
1,
0x2,
0,
0x0,
1,
};
const uint8_t unwind_instruction_table[] = {
0b10101000,
0b10110000,
};
auto unwind_info = ChromeUnwindInfoAndroid32{
unwind_instruction_table,
function_offset_table,
function_table,
page_table,
};
ModuleCache module_cache;
const ModuleCache::Module* chrome_module = AddNativeModule(
&module_cache, std::make_unique<TestModule>(
0x1000, number_of_pages * page_size, "ChromeModule"));
uintptr_t text_section_start_address = 0x1100;
ChromeUnwinderAndroid32 unwinder(unwind_info, chrome_module->GetBaseAddress(),
text_section_start_address);
unwinder.Initialize(&module_cache);
uintptr_t first_pc = text_section_start_address + 0x20;
uintptr_t second_pc = text_section_start_address + page_size + 0x4;
uintptr_t third_pc = text_section_start_address + 3 * page_size;
AlignedStackMemory stack_memory = {
0x0,
third_pc,
0xFFFF,
0xFFFF,
};
std::vector<Frame> unwound_frames = {{first_pc, chrome_module}};
RegisterContext context;
RegisterContextInstructionPointer(&context) = first_pc;
RegisterContextStackPointer(&context) = stack_memory.stack_start_address();
context.arm_lr = second_pc;
EXPECT_EQ(
UnwindResult::kUnrecognizedFrame,
unwinder.TryUnwind(nullptr, &context,
stack_memory.stack_end_address(), &unwound_frames));
ExpectFramesEq(std::vector<Frame>({{first_pc, chrome_module},
{second_pc, chrome_module},
{third_pc, nullptr}}),
unwound_frames);
}
TEST(ChromeUnwinderAndroid32Test, TryUnwindInfiniteLoopSingleFrame) {
const uint32_t page_table[] = {0, 2};
const size_t number_of_pages = std::size(page_table);
const size_t page_size = 1 << 17;
const FunctionTableEntry function_table[] = {
{0x0, 0},
{0x10, 2},
{0x5, 0},
};
const uint8_t function_offset_table[] = {
0x0,
0,
0x0,
2,
};
const uint8_t unwind_instruction_table[] = {
0b10000000,
0b00000000,
0b10110000,
};
auto unwind_info = ChromeUnwindInfoAndroid32{
unwind_instruction_table,
function_offset_table,
function_table,
page_table,
};
ModuleCache module_cache;
const ModuleCache::Module* chrome_module = AddNativeModule(
&module_cache, std::make_unique<TestModule>(
0x1000, number_of_pages * page_size, "ChromeModule"));
uintptr_t text_section_start_address = 0x1100;
ChromeUnwinderAndroid32 unwinder(unwind_info, chrome_module->GetBaseAddress(),
text_section_start_address);
unwinder.Initialize(&module_cache);
uintptr_t pc = text_section_start_address + 0x20;
AlignedStackMemory stack_memory = {
0xFFFF,
0xFFFF,
};
std::vector<Frame> unwound_frames = {{pc, chrome_module}};
RegisterContext context;
RegisterContextInstructionPointer(&context) = pc;
RegisterContextStackPointer(&context) = stack_memory.stack_start_address();
context.arm_lr = pc;
EXPECT_EQ(
UnwindResult::kAborted,
unwinder.TryUnwind(nullptr, &context,
stack_memory.stack_end_address(), &unwound_frames));
ExpectFramesEq(std::vector<Frame>({{pc, chrome_module}}), unwound_frames);
}
TEST(ChromeUnwinderAndroid32Test, TryUnwindInfiniteLoopMultipleFrames) {
const uint32_t page_table[] = {0, 3};
const size_t number_of_pages = std::size(page_table);
const size_t page_size = 1 << 17;
const FunctionTableEntry function_table[] = {
{0x0, 0},
{0x10, 2},
{0x100, 2},
{0x5, 0},
};
const uint8_t function_offset_table[] = {
0x0,
0,
0x0,
2,
0x2,
3,
0x1,
5,
0x0,
6,
};
const uint8_t unwind_instruction_table[] = {
0b10000000,
0b00000000,
0b10110000,
0b10001100,
0b00000000,
0b10010100,
0b10110000,
};
auto unwind_info = ChromeUnwindInfoAndroid32{
unwind_instruction_table,
function_offset_table,
function_table,
page_table,
};
ModuleCache module_cache;
const ModuleCache::Module* chrome_module = AddNativeModule(
&module_cache, std::make_unique<TestModule>(
0x1000, number_of_pages * page_size, "ChromeModule"));
uintptr_t text_section_start_address = 0x1100;
ChromeUnwinderAndroid32 unwinder(unwind_info, chrome_module->GetBaseAddress(),
text_section_start_address);
unwinder.Initialize(&module_cache);
uintptr_t first_pc = text_section_start_address + 0x20;
uintptr_t second_pc = text_section_start_address + 0x110;
AlignedStackMemory stack_memory = {
second_pc,
first_pc,
0xFFFF,
0xFFFF,
};
std::vector<Frame> unwound_frames = {{first_pc, chrome_module}};
RegisterContext context;
RegisterContextInstructionPointer(&context) = first_pc;
RegisterContextStackPointer(&context) = stack_memory.stack_start_address();
context.arm_lr = second_pc;
context.arm_r4 = stack_memory.stack_start_address();
EXPECT_EQ(
UnwindResult::kAborted,
unwinder.TryUnwind(nullptr, &context,
stack_memory.stack_end_address(), &unwound_frames));
ExpectFramesEq(std::vector<Frame>(
{{first_pc, chrome_module}, {second_pc, chrome_module}}),
unwound_frames);
}
TEST(ChromeUnwinderAndroid32Test, TryUnwindUnalignedSPFrameUnwind) {
const uint32_t page_table[] = {0, 2};
const size_t number_of_pages = std::size(page_table);
const size_t page_size = 1 << 17;
const FunctionTableEntry function_table[] = {
{0x0, 0},
{0x10, 2},
{0x5, 0},
};
const uint8_t function_offset_table[] = {
0x0,
0,
0x0,
2,
};
const uint8_t unwind_instruction_table[] = {
0b10000000,
0b00000000,
0b10110000,
};
auto unwind_info = ChromeUnwindInfoAndroid32{
unwind_instruction_table,
function_offset_table,
function_table,
page_table,
};
ModuleCache module_cache;
const ModuleCache::Module* chrome_module = AddNativeModule(
&module_cache, std::make_unique<TestModule>(
0x1000, number_of_pages * page_size, "ChromeModule"));
uintptr_t text_section_start_address = 0x1100;
ChromeUnwinderAndroid32 unwinder(unwind_info, chrome_module->GetBaseAddress(),
text_section_start_address);
unwinder.Initialize(&module_cache);
uintptr_t pc = text_section_start_address + 0x20;
AlignedStackMemory stack_memory = {
0xFFFF,
0xFFFF,
};
std::vector<Frame> unwound_frames = {{pc, chrome_module}};
RegisterContext context;
RegisterContextInstructionPointer(&context) = pc;
RegisterContextStackPointer(&context) =
stack_memory.stack_start_address() + sizeof(uintptr_t);
context.arm_lr =
text_section_start_address + (number_of_pages + 1) * page_size;
EXPECT_EQ(
UnwindResult::kAborted,
unwinder.TryUnwind(nullptr, &context,
stack_memory.stack_end_address(), &unwound_frames));
ExpectFramesEq(std::vector<Frame>({{pc, chrome_module}}), unwound_frames);
}
TEST(ChromeUnwinderAndroid32Test, TryUnwindUnalignedSPInstructionUnwind) {
const uint32_t page_table[] = {0, 2};
const size_t number_of_pages = std::size(page_table);
const size_t page_size = 1 << 17;
const FunctionTableEntry function_table[] = {
{0x0, 0},
{0x10, 2},
{0x5, 0},
};
const uint8_t function_offset_table[] = {
0x0,
0,
0x0,
2,
};
const uint8_t unwind_instruction_table[] = {
0b10000000, 0b00000000,
0b10010100,
0b10110000,
};
auto unwind_info = ChromeUnwindInfoAndroid32{
unwind_instruction_table,
function_offset_table,
function_table,
page_table,
};
ModuleCache module_cache;
const ModuleCache::Module* chrome_module = AddNativeModule(
&module_cache, std::make_unique<TestModule>(
0x1000, number_of_pages * page_size, "ChromeModule"));
uintptr_t text_section_start_address = 0x1100;
ChromeUnwinderAndroid32 unwinder(unwind_info, chrome_module->GetBaseAddress(),
text_section_start_address);
unwinder.Initialize(&module_cache);
uintptr_t pc = text_section_start_address + 0x20;
AlignedStackMemory stack_memory = {
0xFFFF,
0xFFFF,
};
std::vector<Frame> unwound_frames = {{pc, chrome_module}};
RegisterContext context;
RegisterContextInstructionPointer(&context) = pc;
RegisterContextStackPointer(&context) = stack_memory.stack_start_address();
context.arm_lr =
text_section_start_address + (number_of_pages + 1) * page_size;
context.arm_r4 = stack_memory.stack_start_address() + sizeof(uintptr_t) / 2;
EXPECT_EQ(
UnwindResult::kAborted,
unwinder.TryUnwind(nullptr, &context,
stack_memory.stack_end_address(), &unwound_frames));
ExpectFramesEq(std::vector<Frame>({{pc, chrome_module}}), unwound_frames);
}
TEST(ChromeUnwinderAndroid32Test, TryUnwindSPOverflow) {
const uint32_t page_table[] = {0, 2};
const size_t number_of_pages = std::size(page_table);
const size_t page_size = 1 << 17;
const FunctionTableEntry function_table[] = {
{0x0, 0},
{0x10, 2},
{0x5, 0},
};
const uint8_t function_offset_table[] = {
0x0,
0,
0x0,
2,
};
const uint8_t unwind_instruction_table[] = {
0b10000000, 0b00000000,
0b10010100,
0b10101000,
0b10110000,
};
auto unwind_info = ChromeUnwindInfoAndroid32{
unwind_instruction_table,
function_offset_table,
function_table,
page_table,
};
ModuleCache module_cache;
const ModuleCache::Module* chrome_module = AddNativeModule(
&module_cache, std::make_unique<TestModule>(
0x1000, number_of_pages * page_size, "ChromeModule"));
uintptr_t text_section_start_address = 0x1100;
ChromeUnwinderAndroid32 unwinder(unwind_info, chrome_module->GetBaseAddress(),
text_section_start_address);
unwinder.Initialize(&module_cache);
uintptr_t pc = text_section_start_address + 0x20;
AlignedStackMemory stack_memory = {
0xFFFF,
0xFFFF,
};
std::vector<Frame> unwound_frames = {{pc, chrome_module}};
RegisterContext context;
RegisterContextInstructionPointer(&context) = pc;
RegisterContextStackPointer(&context) = stack_memory.stack_start_address();
context.arm_r4 = 0xffffffff;
context.arm_lr =
text_section_start_address + (number_of_pages + 1) * page_size;
EXPECT_EQ(
UnwindResult::kAborted,
unwinder.TryUnwind(nullptr, &context,
stack_memory.stack_end_address(), &unwound_frames));
ExpectFramesEq(std::vector<Frame>({{pc, chrome_module}}), unwound_frames);
}
TEST(ChromeUnwinderAndroid32Test, TryUnwindNullSP) {
const uint32_t page_table[] = {0, 2};
const size_t number_of_pages = std::size(page_table);
const size_t page_size = 1 << 17;
const FunctionTableEntry function_table[] = {
{0x0, 0},
{0x10, 2},
{0x5, 0},
};
const uint8_t function_offset_table[] = {
0x0,
0,
0x0,
2,
};
const uint8_t unwind_instruction_table[] = {
0b10000000, 0b00000000,
0b10010100,
0b10101000,
0b10110000,
};
auto unwind_info = ChromeUnwindInfoAndroid32{
unwind_instruction_table,
function_offset_table,
function_table,
page_table,
};
ModuleCache module_cache;
const ModuleCache::Module* chrome_module = AddNativeModule(
&module_cache, std::make_unique<TestModule>(
0x1000, number_of_pages * page_size, "ChromeModule"));
uintptr_t text_section_start_address = 0x1100;
ChromeUnwinderAndroid32 unwinder(unwind_info, chrome_module->GetBaseAddress(),
text_section_start_address);
unwinder.Initialize(&module_cache);
uintptr_t pc = text_section_start_address + 0x20;
AlignedStackMemory stack_memory = {
0xFFFF,
0xFFFF,
};
std::vector<Frame> unwound_frames = {{pc, chrome_module}};
RegisterContext context;
RegisterContextInstructionPointer(&context) = pc;
RegisterContextStackPointer(&context) = stack_memory.stack_start_address();
context.arm_r4 = 0x0;
context.arm_lr =
text_section_start_address + (number_of_pages + 1) * page_size;
EXPECT_EQ(
UnwindResult::kAborted,
unwinder.TryUnwind(nullptr, &context,
stack_memory.stack_end_address(), &unwound_frames));
ExpectFramesEq(std::vector<Frame>({{pc, chrome_module}}), unwound_frames);
}
TEST(ChromeUnwinderAndroid32Test, TryUnwindInvalidSPOperation) {
const uint32_t page_table[] = {0, 2};
const size_t number_of_pages = std::size(page_table);
const size_t page_size = 1 << 17;
const FunctionTableEntry function_table[] = {
{0x0, 0},
{0x10, 2},
{0x5, 0},
};
const uint8_t function_offset_table[] = {
0x0,
0,
0x0,
2,
};
const uint8_t unwind_instruction_table[] = {
0b10000000, 0b00000000,
0b10010100,
0b10010101,
0b10110000,
};
auto unwind_info = ChromeUnwindInfoAndroid32{
unwind_instruction_table,
function_offset_table,
function_table,
page_table,
};
ModuleCache module_cache;
const ModuleCache::Module* chrome_module = AddNativeModule(
&module_cache, std::make_unique<TestModule>(
0x1000, number_of_pages * page_size, "ChromeModule"));
uintptr_t text_section_start_address = 0x1100;
ChromeUnwinderAndroid32 unwinder(unwind_info, chrome_module->GetBaseAddress(),
text_section_start_address);
unwinder.Initialize(&module_cache);
uintptr_t pc = text_section_start_address + 0x20;
AlignedStackMemory stack_memory = {
0xFFFF,
0xFFFF,
};
std::vector<Frame> unwound_frames = {{pc, chrome_module}};
RegisterContext context;
RegisterContextInstructionPointer(&context) = pc;
RegisterContextStackPointer(&context) = stack_memory.stack_start_address();
context.arm_r4 = stack_memory.stack_start_address() - 2 * sizeof(uintptr_t);
context.arm_r5 = stack_memory.stack_start_address() + 2 * sizeof(uintptr_t);
context.arm_lr =
text_section_start_address + (number_of_pages + 1) * page_size;
EXPECT_EQ(
UnwindResult::kAborted,
unwinder.TryUnwind(nullptr, &context,
stack_memory.stack_end_address(), &unwound_frames));
ExpectFramesEq(std::vector<Frame>({{pc, chrome_module}}), unwound_frames);
}
}