aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/processor/stackwalker_arm64.cc73
-rw-r--r--src/processor/stackwalker_arm64.h4
-rw-r--r--src/processor/stackwalker_arm64_unittest.cc348
3 files changed, 420 insertions, 5 deletions
diff --git a/src/processor/stackwalker_arm64.cc b/src/processor/stackwalker_arm64.cc
index f82c9cbf..31119a97 100644
--- a/src/processor/stackwalker_arm64.cc
+++ b/src/processor/stackwalker_arm64.cc
@@ -78,8 +78,77 @@ StackFrame* StackwalkerARM64::GetContextFrame() {
StackFrameARM64* StackwalkerARM64::GetCallerByCFIFrameInfo(
const vector<StackFrame*> &frames,
CFIFrameInfo* cfi_frame_info) {
- // Obtaining the stack frame from CFI info is not yet supported for ARM64.
- return NULL;
+ StackFrameARM64* last_frame = static_cast<StackFrameARM64*>(frames.back());
+
+ static const char* register_names[] = {
+ "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
+ "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",
+ "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23",
+ "x24", "x25", "x26", "x27", "x28", "x29", "x30", "sp",
+ "pc", NULL
+ };
+
+ // Populate a dictionary with the valid register values in last_frame.
+ CFIFrameInfo::RegisterValueMap<uint64_t> callee_registers;
+ for (int i = 0; register_names[i]; i++) {
+ if (last_frame->context_validity & StackFrameARM64::RegisterValidFlag(i))
+ callee_registers[register_names[i]] = last_frame->context.iregs[i];
+ }
+
+ // Use the STACK CFI data to recover the caller's register values.
+ CFIFrameInfo::RegisterValueMap<uint64_t> caller_registers;
+ if (!cfi_frame_info->FindCallerRegs(callee_registers, *memory_,
+ &caller_registers)) {
+ return NULL;
+ }
+ // Construct a new stack frame given the values the CFI recovered.
+ scoped_ptr<StackFrameARM64> frame(new StackFrameARM64());
+ for (int i = 0; register_names[i]; i++) {
+ CFIFrameInfo::RegisterValueMap<uint64_t>::iterator entry =
+ caller_registers.find(register_names[i]);
+ if (entry != caller_registers.end()) {
+ // We recovered the value of this register; fill the context with the
+ // value from caller_registers.
+ frame->context_validity |= StackFrameARM64::RegisterValidFlag(i);
+ frame->context.iregs[i] = entry->second;
+ } else if (19 <= i && i <= 29 && (last_frame->context_validity &
+ StackFrameARM64::RegisterValidFlag(i))) {
+ // If the STACK CFI data doesn't mention some callee-saves register, and
+ // it is valid in the callee, assume the callee has not yet changed it.
+ // Registers r19 through r29 are callee-saves, according to the Procedure
+ // Call Standard for the ARM AARCH64 Architecture, which the Linux ABI
+ // follows.
+ frame->context_validity |= StackFrameARM64::RegisterValidFlag(i);
+ frame->context.iregs[i] = last_frame->context.iregs[i];
+ }
+ }
+ // If the CFI doesn't recover the PC explicitly, then use .ra.
+ if (!(frame->context_validity & StackFrameARM64::CONTEXT_VALID_PC)) {
+ CFIFrameInfo::RegisterValueMap<uint64_t>::iterator entry =
+ caller_registers.find(".ra");
+ if (entry != caller_registers.end()) {
+ frame->context_validity |= StackFrameARM64::CONTEXT_VALID_PC;
+ frame->context.iregs[MD_CONTEXT_ARM64_REG_PC] = entry->second;
+ }
+ }
+ // If the CFI doesn't recover the SP explicitly, then use .cfa.
+ if (!(frame->context_validity & StackFrameARM64::CONTEXT_VALID_SP)) {
+ CFIFrameInfo::RegisterValueMap<uint64_t>::iterator entry =
+ caller_registers.find(".cfa");
+ if (entry != caller_registers.end()) {
+ frame->context_validity |= StackFrameARM64::CONTEXT_VALID_SP;
+ frame->context.iregs[MD_CONTEXT_ARM64_REG_SP] = entry->second;
+ }
+ }
+
+ // If we didn't recover the PC and the SP, then the frame isn't very useful.
+ static const uint64_t essentials = (StackFrameARM64::CONTEXT_VALID_SP
+ | StackFrameARM64::CONTEXT_VALID_PC);
+ if ((frame->context_validity & essentials) != essentials)
+ return NULL;
+
+ frame->trust = StackFrame::FRAME_TRUST_CFI;
+ return frame.release();
}
StackFrameARM64* StackwalkerARM64::GetCallerByStackScan(
diff --git a/src/processor/stackwalker_arm64.h b/src/processor/stackwalker_arm64.h
index fc7f776b..121e8246 100644
--- a/src/processor/stackwalker_arm64.h
+++ b/src/processor/stackwalker_arm64.h
@@ -63,7 +63,9 @@ class StackwalkerARM64 : public Stackwalker {
// Change the context validity mask of the frame returned by
// GetContextFrame to VALID. This is only for use by unit tests; the
// default behavior is correct for all application code.
- void SetContextFrameValidity(int valid) { context_frame_validity_ = valid; }
+ void SetContextFrameValidity(uint64_t valid) {
+ context_frame_validity_ = valid;
+ }
private:
// Implementation of Stackwalker, using arm64 context and stack conventions.
diff --git a/src/processor/stackwalker_arm64_unittest.cc b/src/processor/stackwalker_arm64_unittest.cc
index 1502c8f3..dd617f69 100644
--- a/src/processor/stackwalker_arm64_unittest.cc
+++ b/src/processor/stackwalker_arm64_unittest.cc
@@ -76,8 +76,7 @@ class StackwalkerARM64Fixture {
// for tests to play with.
module1(0x40000000, 0x10000, "module1", "version1"),
module2(0x50000000, 0x10000, "module2", "version2") {
- // Identify the system as an iOS system, since that is the only platform
- // for which ARM64 support is currently enabled.
+ // Identify the system as an iOS system.
system_info.os = "iOS";
system_info.os_short = "ios";
system_info.cpu = "arm64";
@@ -534,3 +533,348 @@ TEST_F(GetFramesByFramePointer, OnlyFramePointer) {
EXPECT_EQ(frame2_sp.Value(), frame2->context.iregs[MD_CONTEXT_ARM64_REG_SP]);
EXPECT_EQ(0U, frame2->context.iregs[MD_CONTEXT_ARM64_REG_FP]);
}
+
+struct CFIFixture: public StackwalkerARM64Fixture {
+ CFIFixture() {
+ // Provide a bunch of STACK CFI records; we'll walk to the caller
+ // from every point in this series, expecting to find the same set
+ // of register values.
+ SetModuleSymbols(&module1,
+ // The youngest frame's function.
+ "FUNC 4000 1000 10 enchiridion\n"
+ // Initially, nothing has been pushed on the stack,
+ // and the return address is still in the link
+ // register (x30).
+ "STACK CFI INIT 4000 100 .cfa: sp 0 + .ra: x30\n"
+ // Push x19, x20, the frame pointer and the link register.
+ "STACK CFI 4001 .cfa: sp 32 + .ra: .cfa -8 + ^"
+ " x19: .cfa -32 + ^ x20: .cfa -24 + ^ "
+ " x29: .cfa -16 + ^\n"
+ // Save x19..x22 in x0..x3: verify that we populate
+ // the youngest frame with all the values we have.
+ "STACK CFI 4002 x19: x0 x20: x1 x21: x2 x22: x3\n"
+ // Restore x19..x22. Save the non-callee-saves register x1.
+ "STACK CFI 4003 .cfa: sp 40 + x1: .cfa 40 - ^"
+ " x19: x19 x20: x20 x21: x21 x22: x22\n"
+ // Move the .cfa back eight bytes, to point at the return
+ // address, and restore the sp explicitly.
+ "STACK CFI 4005 .cfa: sp 32 + x1: .cfa 32 - ^"
+ " x29: .cfa 8 - ^ .ra: .cfa ^ sp: .cfa 8 +\n"
+ // Recover the PC explicitly from a new stack slot;
+ // provide garbage for the .ra.
+ "STACK CFI 4006 .cfa: sp 40 + pc: .cfa 40 - ^\n"
+
+ // The calling function.
+ "FUNC 5000 1000 10 epictetus\n"
+ // Mark it as end of stack.
+ "STACK CFI INIT 5000 1000 .cfa: 0 .ra: 0\n"
+
+ // A function whose CFI makes the stack pointer
+ // go backwards.
+ "FUNC 6000 1000 20 palinal\n"
+ "STACK CFI INIT 6000 1000 .cfa: sp 8 - .ra: x30\n"
+
+ // A function with CFI expressions that can't be
+ // evaluated.
+ "FUNC 7000 1000 20 rhetorical\n"
+ "STACK CFI INIT 7000 1000 .cfa: moot .ra: ambiguous\n");
+
+ // Provide some distinctive values for the caller's registers.
+ expected.iregs[MD_CONTEXT_ARM64_REG_PC] = 0x0000000040005510L;
+ expected.iregs[MD_CONTEXT_ARM64_REG_SP] = 0x0000000080000000L;
+ expected.iregs[19] = 0x5e68b5d5b5d55e68L;
+ expected.iregs[20] = 0x34f3ebd1ebd134f3L;
+ expected.iregs[21] = 0x74bca31ea31e74bcL;
+ expected.iregs[22] = 0x16b32dcb2dcb16b3L;
+ expected.iregs[23] = 0x21372ada2ada2137L;
+ expected.iregs[24] = 0x557dbbbbbbbb557dL;
+ expected.iregs[25] = 0x8ca748bf48bf8ca7L;
+ expected.iregs[26] = 0x21f0ab46ab4621f0L;
+ expected.iregs[27] = 0x146732b732b71467L;
+ expected.iregs[28] = 0xa673645fa673645fL;
+ expected.iregs[MD_CONTEXT_ARM64_REG_FP] = 0xe11081128112e110L;
+
+ // Expect CFI to recover all callee-saves registers. Since CFI is the
+ // only stack frame construction technique we have, aside from the
+ // context frame itself, there's no way for us to have a set of valid
+ // registers smaller than this.
+ expected_validity = (StackFrameARM64::CONTEXT_VALID_PC |
+ StackFrameARM64::CONTEXT_VALID_SP |
+ StackFrameARM64::CONTEXT_VALID_X19 |
+ StackFrameARM64::CONTEXT_VALID_X20 |
+ StackFrameARM64::CONTEXT_VALID_X21 |
+ StackFrameARM64::CONTEXT_VALID_X22 |
+ StackFrameARM64::CONTEXT_VALID_X23 |
+ StackFrameARM64::CONTEXT_VALID_X24 |
+ StackFrameARM64::CONTEXT_VALID_X25 |
+ StackFrameARM64::CONTEXT_VALID_X26 |
+ StackFrameARM64::CONTEXT_VALID_X27 |
+ StackFrameARM64::CONTEXT_VALID_X28 |
+ StackFrameARM64::CONTEXT_VALID_FP);
+
+ // By default, context frames provide all registers, as normal.
+ context_frame_validity = StackFrameARM64::CONTEXT_VALID_ALL;
+
+ // By default, registers are unchanged.
+ raw_context = expected;
+ }
+
+ // Walk the stack, using stack_section as the contents of the stack
+ // and raw_context as the current register values. (Set the stack
+ // pointer to the stack's starting address.) Expect two stack
+ // frames; in the older frame, expect the callee-saves registers to
+ // have values matching those in 'expected'.
+ void CheckWalk() {
+ RegionFromSection();
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_SP] = stack_section.start().Value();
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerARM64 walker(&system_info, &raw_context, &stack_region,
+ &modules, &frame_symbolizer);
+ walker.SetContextFrameValidity(context_frame_validity);
+ vector<const CodeModule*> modules_without_symbols;
+ vector<const CodeModule*> modules_with_corrupt_symbols;
+ ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
+ &modules_with_corrupt_symbols));
+ ASSERT_EQ(0U, modules_without_symbols.size());
+ ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
+ frames = call_stack.frames();
+ ASSERT_EQ(2U, frames->size());
+
+ StackFrameARM64 *frame0 = static_cast<StackFrameARM64 *>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
+ ASSERT_EQ(context_frame_validity, frame0->context_validity);
+ EXPECT_EQ("enchiridion", frame0->function_name);
+ EXPECT_EQ(0x0000000040004000UL, frame0->function_base);
+
+ StackFrameARM64 *frame1 = static_cast<StackFrameARM64 *>(frames->at(1));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust);
+ ASSERT_EQ(expected_validity, frame1->context_validity);
+ if (expected_validity & StackFrameARM64::CONTEXT_VALID_X1)
+ EXPECT_EQ(expected.iregs[1], frame1->context.iregs[1]);
+ if (expected_validity & StackFrameARM64::CONTEXT_VALID_X19)
+ EXPECT_EQ(expected.iregs[19], frame1->context.iregs[19]);
+ if (expected_validity & StackFrameARM64::CONTEXT_VALID_X20)
+ EXPECT_EQ(expected.iregs[20], frame1->context.iregs[20]);
+ if (expected_validity & StackFrameARM64::CONTEXT_VALID_X21)
+ EXPECT_EQ(expected.iregs[21], frame1->context.iregs[21]);
+ if (expected_validity & StackFrameARM64::CONTEXT_VALID_X22)
+ EXPECT_EQ(expected.iregs[22], frame1->context.iregs[22]);
+ if (expected_validity & StackFrameARM64::CONTEXT_VALID_X23)
+ EXPECT_EQ(expected.iregs[23], frame1->context.iregs[23]);
+ if (expected_validity & StackFrameARM64::CONTEXT_VALID_X24)
+ EXPECT_EQ(expected.iregs[24], frame1->context.iregs[24]);
+ if (expected_validity & StackFrameARM64::CONTEXT_VALID_X25)
+ EXPECT_EQ(expected.iregs[25], frame1->context.iregs[25]);
+ if (expected_validity & StackFrameARM64::CONTEXT_VALID_X26)
+ EXPECT_EQ(expected.iregs[26], frame1->context.iregs[26]);
+ if (expected_validity & StackFrameARM64::CONTEXT_VALID_X27)
+ EXPECT_EQ(expected.iregs[27], frame1->context.iregs[27]);
+ if (expected_validity & StackFrameARM64::CONTEXT_VALID_X28)
+ EXPECT_EQ(expected.iregs[28], frame1->context.iregs[28]);
+ if (expected_validity & StackFrameARM64::CONTEXT_VALID_FP)
+ EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM64_REG_FP],
+ frame1->context.iregs[MD_CONTEXT_ARM64_REG_FP]);
+
+ // We would never have gotten a frame in the first place if the SP
+ // and PC weren't valid or ->instruction weren't set.
+ EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM64_REG_SP],
+ frame1->context.iregs[MD_CONTEXT_ARM64_REG_SP]);
+ EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM64_REG_PC],
+ frame1->context.iregs[MD_CONTEXT_ARM64_REG_PC]);
+ EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM64_REG_PC],
+ frame1->instruction + 4);
+ EXPECT_EQ("epictetus", frame1->function_name);
+ }
+
+ // The values we expect to find for the caller's registers.
+ MDRawContextARM64 expected;
+
+ // The validity mask for expected.
+ uint64_t expected_validity;
+
+ // The validity mask to impose on the context frame.
+ uint64_t context_frame_validity;
+};
+
+class CFI: public CFIFixture, public Test { };
+
+TEST_F(CFI, At4000) {
+ stack_section.start() = expected.iregs[MD_CONTEXT_ARM64_REG_SP];
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_PC] = 0x0000000040004000L;
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_LR] = 0x0000000040005510L;
+ CheckWalk();
+}
+
+TEST_F(CFI, At4001) {
+ Label frame1_sp = expected.iregs[MD_CONTEXT_ARM64_REG_SP];
+ stack_section
+ .D64(0x5e68b5d5b5d55e68L) // saved x19
+ .D64(0x34f3ebd1ebd134f3L) // saved x20
+ .D64(0xe11081128112e110L) // saved fp
+ .D64(0x0000000040005510L) // return address
+ .Mark(&frame1_sp); // This effectively sets stack_section.start().
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_PC] = 0x0000000040004001L;
+ // distinct callee x19, x20 and fp
+ raw_context.iregs[19] = 0xadc9f635a635adc9L;
+ raw_context.iregs[20] = 0x623135ac35ac6231L;
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_FP] = 0x5fc4be14be145fc4L;
+ CheckWalk();
+}
+
+// As above, but unwind from a context that has only the PC and SP.
+TEST_F(CFI, At4001LimitedValidity) {
+ Label frame1_sp = expected.iregs[MD_CONTEXT_ARM64_REG_SP];
+ stack_section
+ .D64(0x5e68b5d5b5d55e68L) // saved x19
+ .D64(0x34f3ebd1ebd134f3L) // saved x20
+ .D64(0xe11081128112e110L) // saved fp
+ .D64(0x0000000040005510L) // return address
+ .Mark(&frame1_sp); // This effectively sets stack_section.start().
+ context_frame_validity =
+ StackFrameARM64::CONTEXT_VALID_PC | StackFrameARM64::CONTEXT_VALID_SP;
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_PC] = 0x0000000040004001L;
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_FP] = 0x5fc4be14be145fc4L;
+
+ expected_validity = (StackFrameARM64::CONTEXT_VALID_PC
+ | StackFrameARM64::CONTEXT_VALID_SP
+ | StackFrameARM64::CONTEXT_VALID_FP
+ | StackFrameARM64::CONTEXT_VALID_X19
+ | StackFrameARM64::CONTEXT_VALID_X20);
+ CheckWalk();
+}
+
+TEST_F(CFI, At4002) {
+ Label frame1_sp = expected.iregs[MD_CONTEXT_ARM64_REG_SP];
+ stack_section
+ .D64(0xff3dfb81fb81ff3dL) // no longer saved x19
+ .D64(0x34f3ebd1ebd134f3L) // no longer saved x20
+ .D64(0xe11081128112e110L) // saved fp
+ .D64(0x0000000040005510L) // return address
+ .Mark(&frame1_sp); // This effectively sets stack_section.start().
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_PC] = 0x0000000040004002L;
+ raw_context.iregs[0] = 0x5e68b5d5b5d55e68L; // saved x19
+ raw_context.iregs[1] = 0x34f3ebd1ebd134f3L; // saved x20
+ raw_context.iregs[2] = 0x74bca31ea31e74bcL; // saved x21
+ raw_context.iregs[3] = 0x16b32dcb2dcb16b3L; // saved x22
+ raw_context.iregs[19] = 0xadc9f635a635adc9L; // distinct callee x19
+ raw_context.iregs[20] = 0x623135ac35ac6231L; // distinct callee x20
+ raw_context.iregs[21] = 0xac4543564356ac45L; // distinct callee x21
+ raw_context.iregs[22] = 0x2561562f562f2561L; // distinct callee x22
+ // distinct callee fp
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_FP] = 0x5fc4be14be145fc4L;
+ CheckWalk();
+}
+
+TEST_F(CFI, At4003) {
+ Label frame1_sp = expected.iregs[MD_CONTEXT_ARM64_REG_SP];
+ stack_section
+ .D64(0xdd5a48c848c8dd5aL) // saved x1 (even though it's not callee-saves)
+ .D64(0xff3dfb81fb81ff3dL) // no longer saved x19
+ .D64(0x34f3ebd1ebd134f3L) // no longer saved x20
+ .D64(0xe11081128112e110L) // saved fp
+ .D64(0x0000000040005510L) // return address
+ .Mark(&frame1_sp); // This effectively sets stack_section.start().
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_PC] = 0x0000000040004003L;
+ // distinct callee x1 and fp
+ raw_context.iregs[1] = 0xfb756319fb756319L;
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_FP] = 0x5fc4be14be145fc4L;
+ // caller's x1
+ expected.iregs[1] = 0xdd5a48c848c8dd5aL;
+ expected_validity |= StackFrameARM64::CONTEXT_VALID_X1;
+ CheckWalk();
+}
+
+// We have no new rule at module offset 0x4004, so the results here should
+// be the same as those at module offset 0x4003.
+TEST_F(CFI, At4004) {
+ Label frame1_sp = expected.iregs[MD_CONTEXT_ARM64_REG_SP];
+ stack_section
+ .D64(0xdd5a48c848c8dd5aL) // saved x1 (even though it's not callee-saves)
+ .D64(0xff3dfb81fb81ff3dL) // no longer saved x19
+ .D64(0x34f3ebd1ebd134f3L) // no longer saved x20
+ .D64(0xe11081128112e110L) // saved fp
+ .D64(0x0000000040005510L) // return address
+ .Mark(&frame1_sp); // This effectively sets stack_section.start().
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_PC] = 0x0000000040004004L;
+ // distinct callee x1 and fp
+ raw_context.iregs[1] = 0xfb756319fb756319L;
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_FP] = 0x5fc4be14be145fc4L;
+ // caller's x1
+ expected.iregs[1] = 0xdd5a48c848c8dd5aL;
+ expected_validity |= StackFrameARM64::CONTEXT_VALID_X1;
+ CheckWalk();
+}
+
+// Here we move the .cfa, but provide an explicit rule to recover the SP,
+// so again there should be no change in the registers recovered.
+TEST_F(CFI, At4005) {
+ Label frame1_sp = expected.iregs[MD_CONTEXT_ARM64_REG_SP];
+ stack_section
+ .D64(0xdd5a48c848c8dd5aL) // saved x1 (even though it's not callee-saves)
+ .D64(0xff3dfb81fb81ff3dL) // no longer saved x19
+ .D64(0x34f3ebd1ebd134f3L) // no longer saved x20
+ .D64(0xe11081128112e110L) // saved fp
+ .D64(0x0000000040005510L) // return address
+ .Mark(&frame1_sp); // This effectively sets stack_section.start().
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_PC] = 0x0000000040004005L;
+ raw_context.iregs[1] = 0xfb756319fb756319L; // distinct callee x1
+ expected.iregs[1] = 0xdd5a48c848c8dd5aL; // caller's x1
+ expected_validity |= StackFrameARM64::CONTEXT_VALID_X1;
+ CheckWalk();
+}
+
+// Here we provide an explicit rule for the PC, and have the saved .ra be
+// bogus.
+TEST_F(CFI, At4006) {
+ Label frame1_sp = expected.iregs[MD_CONTEXT_ARM64_REG_SP];
+ stack_section
+ .D64(0x0000000040005510L) // saved pc
+ .D64(0xdd5a48c848c8dd5aL) // saved x1 (even though it's not callee-saves)
+ .D64(0xff3dfb81fb81ff3dL) // no longer saved x19
+ .D64(0x34f3ebd1ebd134f3L) // no longer saved x20
+ .D64(0xe11081128112e110L) // saved fp
+ .D64(0xf8d157835783f8d1L) // .ra rule recovers this, which is garbage
+ .Mark(&frame1_sp); // This effectively sets stack_section.start().
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_PC] = 0x0000000040004006L;
+ raw_context.iregs[1] = 0xfb756319fb756319L; // distinct callee x1
+ expected.iregs[1] = 0xdd5a48c848c8dd5aL; // caller's x1
+ expected_validity |= StackFrameARM64::CONTEXT_VALID_X1;
+ CheckWalk();
+}
+
+// Check that we reject rules that would cause the stack pointer to
+// move in the wrong direction.
+TEST_F(CFI, RejectBackwards) {
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_PC] = 0x0000000040006000L;
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_SP] = 0x0000000080000000L;
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_LR] = 0x0000000040005510L;
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerARM64 walker(&system_info, &raw_context, &stack_region, &modules,
+ &frame_symbolizer);
+ vector<const CodeModule*> modules_without_symbols;
+ vector<const CodeModule*> modules_with_corrupt_symbols;
+ ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
+ &modules_with_corrupt_symbols));
+ ASSERT_EQ(0U, modules_without_symbols.size());
+ ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
+ frames = call_stack.frames();
+ ASSERT_EQ(1U, frames->size());
+}
+
+// Check that we reject rules whose expressions' evaluation fails.
+TEST_F(CFI, RejectBadExpressions) {
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_PC] = 0x0000000040007000L;
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_SP] = 0x0000000080000000L;
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerARM64 walker(&system_info, &raw_context, &stack_region, &modules,
+ &frame_symbolizer);
+ vector<const CodeModule*> modules_without_symbols;
+ vector<const CodeModule*> modules_with_corrupt_symbols;
+ ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
+ &modules_with_corrupt_symbols));
+ ASSERT_EQ(0U, modules_without_symbols.size());
+ ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
+ frames = call_stack.frames();
+ ASSERT_EQ(1U, frames->size());
+}