diff options
Diffstat (limited to 'src/processor')
-rw-r--r-- | src/processor/stackwalker_arm64.cc | 73 | ||||
-rw-r--r-- | src/processor/stackwalker_arm64.h | 4 | ||||
-rw-r--r-- | src/processor/stackwalker_arm64_unittest.cc | 348 |
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()); +} |