Use frame pointer to walk ARM stack on iOS.
Review URL: http://breakpad.appspot.com/314001 git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@869 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
parent
375928a0a6
commit
01596d3bc1
5 changed files with 280 additions and 30 deletions
|
@ -120,6 +120,7 @@ typedef struct {
|
||||||
* purpose.
|
* purpose.
|
||||||
*/
|
*/
|
||||||
enum MDARMRegisterNumbers {
|
enum MDARMRegisterNumbers {
|
||||||
|
MD_CONTEXT_ARM_REG_IOS_FP = 7,
|
||||||
MD_CONTEXT_ARM_REG_FP = 11,
|
MD_CONTEXT_ARM_REG_FP = 11,
|
||||||
MD_CONTEXT_ARM_REG_SP = 13,
|
MD_CONTEXT_ARM_REG_SP = 13,
|
||||||
MD_CONTEXT_ARM_REG_LR = 14,
|
MD_CONTEXT_ARM_REG_LR = 14,
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
#include "google_breakpad/processor/source_line_resolver_interface.h"
|
#include "google_breakpad/processor/source_line_resolver_interface.h"
|
||||||
#include "google_breakpad/processor/stack_frame.h"
|
#include "google_breakpad/processor/stack_frame.h"
|
||||||
#include "google_breakpad/processor/symbol_supplier.h"
|
#include "google_breakpad/processor/symbol_supplier.h"
|
||||||
|
#include "google_breakpad/processor/system_info.h"
|
||||||
#include "processor/linked_ptr.h"
|
#include "processor/linked_ptr.h"
|
||||||
#include "processor/logging.h"
|
#include "processor/logging.h"
|
||||||
#include "processor/scoped_ptr.h"
|
#include "processor/scoped_ptr.h"
|
||||||
|
@ -187,10 +188,13 @@ Stackwalker* Stackwalker::StackwalkerForCPU(
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MD_CONTEXT_ARM:
|
case MD_CONTEXT_ARM:
|
||||||
|
int fp_register = -1;
|
||||||
|
if (system_info->os_short == "ios")
|
||||||
|
fp_register = MD_CONTEXT_ARM_REG_IOS_FP;
|
||||||
cpu_stackwalker = new StackwalkerARM(system_info,
|
cpu_stackwalker = new StackwalkerARM(system_info,
|
||||||
context->GetContextARM(),
|
context->GetContextARM(),
|
||||||
memory, modules, supplier,
|
fp_register, memory, modules,
|
||||||
resolver);
|
supplier, resolver);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,6 @@
|
||||||
//
|
//
|
||||||
// Author: Mark Mentovai, Ted Mielczarek, Jim Blandy
|
// Author: Mark Mentovai, Ted Mielczarek, Jim Blandy
|
||||||
|
|
||||||
|
|
||||||
#include "google_breakpad/processor/call_stack.h"
|
#include "google_breakpad/processor/call_stack.h"
|
||||||
#include "google_breakpad/processor/memory_region.h"
|
#include "google_breakpad/processor/memory_region.h"
|
||||||
#include "google_breakpad/processor/source_line_resolver_interface.h"
|
#include "google_breakpad/processor/source_line_resolver_interface.h"
|
||||||
|
@ -48,12 +47,13 @@ namespace google_breakpad {
|
||||||
|
|
||||||
StackwalkerARM::StackwalkerARM(const SystemInfo *system_info,
|
StackwalkerARM::StackwalkerARM(const SystemInfo *system_info,
|
||||||
const MDRawContextARM *context,
|
const MDRawContextARM *context,
|
||||||
|
int fp_register,
|
||||||
MemoryRegion *memory,
|
MemoryRegion *memory,
|
||||||
const CodeModules *modules,
|
const CodeModules *modules,
|
||||||
SymbolSupplier *supplier,
|
SymbolSupplier *supplier,
|
||||||
SourceLineResolverInterface *resolver)
|
SourceLineResolverInterface *resolver)
|
||||||
: Stackwalker(system_info, memory, modules, supplier, resolver),
|
: Stackwalker(system_info, memory, modules, supplier, resolver),
|
||||||
context_(context),
|
context_(context), fp_register_(fp_register),
|
||||||
context_frame_validity_(StackFrameARM::CONTEXT_VALID_ALL) { }
|
context_frame_validity_(StackFrameARM::CONTEXT_VALID_ALL) { }
|
||||||
|
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ StackFrame* StackwalkerARM::GetContextFrame() {
|
||||||
frame->context = *context_;
|
frame->context = *context_;
|
||||||
frame->context_validity = context_frame_validity_;
|
frame->context_validity = context_frame_validity_;
|
||||||
frame->trust = StackFrame::FRAME_TRUST_CONTEXT;
|
frame->trust = StackFrame::FRAME_TRUST_CONTEXT;
|
||||||
frame->instruction = frame->context.iregs[15];
|
frame->instruction = frame->context.iregs[MD_CONTEXT_ARM_REG_PC];
|
||||||
|
|
||||||
return frame;
|
return frame;
|
||||||
}
|
}
|
||||||
|
@ -125,8 +125,18 @@ StackFrameARM *StackwalkerARM::GetCallerByCFIFrameInfo(
|
||||||
CFIFrameInfo::RegisterValueMap<u_int32_t>::iterator entry =
|
CFIFrameInfo::RegisterValueMap<u_int32_t>::iterator entry =
|
||||||
caller_registers.find(".ra");
|
caller_registers.find(".ra");
|
||||||
if (entry != caller_registers.end()) {
|
if (entry != caller_registers.end()) {
|
||||||
|
if (fp_register_ == -1) {
|
||||||
frame->context_validity |= StackFrameARM::CONTEXT_VALID_PC;
|
frame->context_validity |= StackFrameARM::CONTEXT_VALID_PC;
|
||||||
frame->context.iregs[MD_CONTEXT_ARM_REG_PC] = entry->second;
|
frame->context.iregs[MD_CONTEXT_ARM_REG_PC] = entry->second;
|
||||||
|
} else {
|
||||||
|
// The CFI updated the link register and not the program counter.
|
||||||
|
// Handle getting the program counter from the link register.
|
||||||
|
frame->context_validity |= StackFrameARM::CONTEXT_VALID_PC;
|
||||||
|
frame->context_validity |= StackFrameARM::CONTEXT_VALID_LR;
|
||||||
|
frame->context.iregs[MD_CONTEXT_ARM_REG_LR] = entry->second;
|
||||||
|
frame->context.iregs[MD_CONTEXT_ARM_REG_PC] =
|
||||||
|
last_frame->context.iregs[MD_CONTEXT_ARM_REG_LR];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If the CFI doesn't recover the SP explicitly, then use .cfa.
|
// If the CFI doesn't recover the SP explicitly, then use .cfa.
|
||||||
|
@ -179,6 +189,52 @@ StackFrameARM *StackwalkerARM::GetCallerByStackScan(
|
||||||
return frame;
|
return frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StackFrameARM *StackwalkerARM::GetCallerByFramePointer(
|
||||||
|
const vector<StackFrame *> &frames) {
|
||||||
|
StackFrameARM *last_frame = static_cast<StackFrameARM *>(frames.back());
|
||||||
|
|
||||||
|
if (!(last_frame->context_validity &
|
||||||
|
StackFrameARM::RegisterValidFlag(fp_register_))) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
u_int32_t last_fp = last_frame->context.iregs[fp_register_];
|
||||||
|
|
||||||
|
u_int32_t caller_fp = 0;
|
||||||
|
if (last_fp && !memory_->GetMemoryAtAddress(last_fp, &caller_fp)) {
|
||||||
|
BPLOG(ERROR) << "Unable to read caller_fp from last_fp: 0x"
|
||||||
|
<< std::hex << last_fp;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
u_int32_t caller_lr = 0;
|
||||||
|
if (last_fp && !memory_->GetMemoryAtAddress(last_fp + 4, &caller_lr)) {
|
||||||
|
BPLOG(ERROR) << "Unable to read caller_lr from last_fp + 4: 0x"
|
||||||
|
<< std::hex << (last_fp + 4);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
u_int32_t caller_sp = last_fp ? last_fp + 8 :
|
||||||
|
last_frame->context.iregs[MD_CONTEXT_ARM_REG_SP];
|
||||||
|
|
||||||
|
// Create a new stack frame (ownership will be transferred to the caller)
|
||||||
|
// and fill it in.
|
||||||
|
StackFrameARM *frame = new StackFrameARM();
|
||||||
|
|
||||||
|
frame->trust = StackFrame::FRAME_TRUST_FP;
|
||||||
|
frame->context = last_frame->context;
|
||||||
|
frame->context.iregs[fp_register_] = caller_fp;
|
||||||
|
frame->context.iregs[MD_CONTEXT_ARM_REG_SP] = caller_sp;
|
||||||
|
frame->context.iregs[MD_CONTEXT_ARM_REG_PC] =
|
||||||
|
last_frame->context.iregs[MD_CONTEXT_ARM_REG_LR];
|
||||||
|
frame->context.iregs[MD_CONTEXT_ARM_REG_LR] = caller_lr;
|
||||||
|
frame->context_validity = StackFrameARM::CONTEXT_VALID_PC |
|
||||||
|
StackFrameARM::CONTEXT_VALID_LR |
|
||||||
|
StackFrameARM::RegisterValidFlag(fp_register_) |
|
||||||
|
StackFrameARM::CONTEXT_VALID_SP;
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
|
||||||
StackFrame* StackwalkerARM::GetCallerFrame(const CallStack *stack) {
|
StackFrame* StackwalkerARM::GetCallerFrame(const CallStack *stack) {
|
||||||
if (!memory_ || !stack) {
|
if (!memory_ || !stack) {
|
||||||
BPLOG(ERROR) << "Can't get caller frame without memory or stack";
|
BPLOG(ERROR) << "Can't get caller frame without memory or stack";
|
||||||
|
@ -196,10 +252,13 @@ StackFrame* StackwalkerARM::GetCallerFrame(const CallStack *stack) {
|
||||||
frame.reset(GetCallerByCFIFrameInfo(frames, cfi_frame_info.get()));
|
frame.reset(GetCallerByCFIFrameInfo(frames, cfi_frame_info.get()));
|
||||||
|
|
||||||
// If CFI failed, or there wasn't CFI available, fall back
|
// If CFI failed, or there wasn't CFI available, fall back
|
||||||
// to stack scanning.
|
// to frame pointer, if this is configured.
|
||||||
if (!frame.get()) {
|
if (fp_register_ >= 0 && !frame.get())
|
||||||
|
frame.reset(GetCallerByFramePointer(frames));
|
||||||
|
|
||||||
|
// If everuthing failed, fall back to stack scanning.
|
||||||
|
if (!frame.get())
|
||||||
frame.reset(GetCallerByStackScan(frames));
|
frame.reset(GetCallerByStackScan(frames));
|
||||||
}
|
|
||||||
|
|
||||||
// If nothing worked, tell the caller.
|
// If nothing worked, tell the caller.
|
||||||
if (!frame.get())
|
if (!frame.get())
|
||||||
|
@ -225,7 +284,7 @@ StackFrame* StackwalkerARM::GetCallerFrame(const CallStack *stack) {
|
||||||
// match up with the line that contains the function call. Callers that
|
// match up with the line that contains the function call. Callers that
|
||||||
// require the exact return address value may access
|
// require the exact return address value may access
|
||||||
// frame->context.iregs[MD_CONTEXT_ARM_REG_PC].
|
// frame->context.iregs[MD_CONTEXT_ARM_REG_PC].
|
||||||
frame->instruction = frame->context.iregs[MD_CONTEXT_ARM_REG_PC] - 1;
|
frame->instruction = frame->context.iregs[MD_CONTEXT_ARM_REG_PC] - 2;
|
||||||
|
|
||||||
return frame.release();
|
return frame.release();
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,6 @@
|
||||||
#ifndef PROCESSOR_STACKWALKER_ARM_H__
|
#ifndef PROCESSOR_STACKWALKER_ARM_H__
|
||||||
#define PROCESSOR_STACKWALKER_ARM_H__
|
#define PROCESSOR_STACKWALKER_ARM_H__
|
||||||
|
|
||||||
|
|
||||||
#include "google_breakpad/common/breakpad_types.h"
|
#include "google_breakpad/common/breakpad_types.h"
|
||||||
#include "google_breakpad/common/minidump_format.h"
|
#include "google_breakpad/common/minidump_format.h"
|
||||||
#include "google_breakpad/processor/stackwalker.h"
|
#include "google_breakpad/processor/stackwalker.h"
|
||||||
|
@ -57,6 +56,7 @@ class StackwalkerARM : public Stackwalker {
|
||||||
// to the base Stackwalker constructor.
|
// to the base Stackwalker constructor.
|
||||||
StackwalkerARM(const SystemInfo *system_info,
|
StackwalkerARM(const SystemInfo *system_info,
|
||||||
const MDRawContextARM *context,
|
const MDRawContextARM *context,
|
||||||
|
int fp_register,
|
||||||
MemoryRegion *memory,
|
MemoryRegion *memory,
|
||||||
const CodeModules *modules,
|
const CodeModules *modules,
|
||||||
SymbolSupplier *supplier,
|
SymbolSupplier *supplier,
|
||||||
|
@ -78,6 +78,10 @@ class StackwalkerARM : public Stackwalker {
|
||||||
StackFrameARM *GetCallerByCFIFrameInfo(const vector<StackFrame *> &frames,
|
StackFrameARM *GetCallerByCFIFrameInfo(const vector<StackFrame *> &frames,
|
||||||
CFIFrameInfo *cfi_frame_info);
|
CFIFrameInfo *cfi_frame_info);
|
||||||
|
|
||||||
|
// Use the frame pointer. The caller takes ownership of the returned frame.
|
||||||
|
// Return NULL on failure.
|
||||||
|
StackFrameARM *GetCallerByFramePointer(const vector<StackFrame *> &frames);
|
||||||
|
|
||||||
// Scan the stack for plausible return addresses. The caller takes ownership
|
// Scan the stack for plausible return addresses. The caller takes ownership
|
||||||
// of the returned frame. Return NULL on failure.
|
// of the returned frame. Return NULL on failure.
|
||||||
StackFrameARM *GetCallerByStackScan(const vector<StackFrame *> &frames);
|
StackFrameARM *GetCallerByStackScan(const vector<StackFrame *> &frames);
|
||||||
|
@ -90,6 +94,10 @@ class StackwalkerARM : public Stackwalker {
|
||||||
// CONTEXT_VALID_ALL in real use; it is only changeable for the sake of
|
// CONTEXT_VALID_ALL in real use; it is only changeable for the sake of
|
||||||
// unit tests.
|
// unit tests.
|
||||||
int context_frame_validity_;
|
int context_frame_validity_;
|
||||||
|
|
||||||
|
// The register to use a as frame pointer. The value is -1 if frame pointer
|
||||||
|
// cannot be used.
|
||||||
|
int fp_register_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -136,7 +136,7 @@ TEST_F(SanityCheck, NoResolver) {
|
||||||
// Since we have no call frame information, and all unwinding
|
// Since we have no call frame information, and all unwinding
|
||||||
// requires call frame information, the stack walk will end after
|
// requires call frame information, the stack walk will end after
|
||||||
// the first frame.
|
// the first frame.
|
||||||
StackwalkerARM walker(&system_info, &raw_context, &stack_region, &modules,
|
StackwalkerARM walker(&system_info, &raw_context, -1, &stack_region, &modules,
|
||||||
NULL, NULL);
|
NULL, NULL);
|
||||||
// This should succeed even without a resolver or supplier.
|
// This should succeed even without a resolver or supplier.
|
||||||
ASSERT_TRUE(walker.Walk(&call_stack));
|
ASSERT_TRUE(walker.Walk(&call_stack));
|
||||||
|
@ -154,7 +154,7 @@ TEST_F(GetContextFrame, Simple) {
|
||||||
// Since we have no call frame information, and all unwinding
|
// Since we have no call frame information, and all unwinding
|
||||||
// requires call frame information, the stack walk will end after
|
// requires call frame information, the stack walk will end after
|
||||||
// the first frame.
|
// the first frame.
|
||||||
StackwalkerARM walker(&system_info, &raw_context, &stack_region, &modules,
|
StackwalkerARM walker(&system_info, &raw_context, -1, &stack_region, &modules,
|
||||||
&supplier, &resolver);
|
&supplier, &resolver);
|
||||||
ASSERT_TRUE(walker.Walk(&call_stack));
|
ASSERT_TRUE(walker.Walk(&call_stack));
|
||||||
frames = call_stack.frames();
|
frames = call_stack.frames();
|
||||||
|
@ -201,7 +201,7 @@ TEST_F(GetCallerFrame, ScanWithoutSymbols) {
|
||||||
raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40005510;
|
raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40005510;
|
||||||
raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = stack_section.start().Value();
|
raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = stack_section.start().Value();
|
||||||
|
|
||||||
StackwalkerARM walker(&system_info, &raw_context, &stack_region, &modules,
|
StackwalkerARM walker(&system_info, &raw_context, -1, &stack_region, &modules,
|
||||||
&supplier, &resolver);
|
&supplier, &resolver);
|
||||||
ASSERT_TRUE(walker.Walk(&call_stack));
|
ASSERT_TRUE(walker.Walk(&call_stack));
|
||||||
frames = call_stack.frames();
|
frames = call_stack.frames();
|
||||||
|
@ -264,7 +264,7 @@ TEST_F(GetCallerFrame, ScanWithFunctionSymbols) {
|
||||||
// The calling frame's function.
|
// The calling frame's function.
|
||||||
"FUNC 100 400 10 marsupial\n");
|
"FUNC 100 400 10 marsupial\n");
|
||||||
|
|
||||||
StackwalkerARM walker(&system_info, &raw_context, &stack_region, &modules,
|
StackwalkerARM walker(&system_info, &raw_context, -1, &stack_region, &modules,
|
||||||
&supplier, &resolver);
|
&supplier, &resolver);
|
||||||
ASSERT_TRUE(walker.Walk(&call_stack));
|
ASSERT_TRUE(walker.Walk(&call_stack));
|
||||||
frames = call_stack.frames();
|
frames = call_stack.frames();
|
||||||
|
@ -374,8 +374,8 @@ struct CFIFixture: public StackwalkerARMFixture {
|
||||||
RegionFromSection();
|
RegionFromSection();
|
||||||
raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = stack_section.start().Value();
|
raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = stack_section.start().Value();
|
||||||
|
|
||||||
StackwalkerARM walker(&system_info, &raw_context, &stack_region, &modules,
|
StackwalkerARM walker(&system_info, &raw_context, -1, &stack_region,
|
||||||
&supplier, &resolver);
|
&modules, &supplier, &resolver);
|
||||||
walker.SetContextFrameValidity(context_frame_validity);
|
walker.SetContextFrameValidity(context_frame_validity);
|
||||||
ASSERT_TRUE(walker.Walk(&call_stack));
|
ASSERT_TRUE(walker.Walk(&call_stack));
|
||||||
frames = call_stack.frames();
|
frames = call_stack.frames();
|
||||||
|
@ -417,7 +417,7 @@ struct CFIFixture: public StackwalkerARMFixture {
|
||||||
EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM_REG_PC],
|
EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM_REG_PC],
|
||||||
frame1->context.iregs[MD_CONTEXT_ARM_REG_PC]);
|
frame1->context.iregs[MD_CONTEXT_ARM_REG_PC]);
|
||||||
EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM_REG_PC],
|
EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM_REG_PC],
|
||||||
frame1->instruction + 1);
|
frame1->instruction + 2);
|
||||||
EXPECT_EQ("epictetus", frame1->function_name);
|
EXPECT_EQ("epictetus", frame1->function_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -566,7 +566,7 @@ TEST_F(CFI, RejectBackwards) {
|
||||||
raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40006000;
|
raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40006000;
|
||||||
raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = 0x80000000;
|
raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = 0x80000000;
|
||||||
raw_context.iregs[MD_CONTEXT_ARM_REG_LR] = 0x40005510;
|
raw_context.iregs[MD_CONTEXT_ARM_REG_LR] = 0x40005510;
|
||||||
StackwalkerARM walker(&system_info, &raw_context, &stack_region, &modules,
|
StackwalkerARM walker(&system_info, &raw_context, -1, &stack_region, &modules,
|
||||||
&supplier, &resolver);
|
&supplier, &resolver);
|
||||||
ASSERT_TRUE(walker.Walk(&call_stack));
|
ASSERT_TRUE(walker.Walk(&call_stack));
|
||||||
frames = call_stack.frames();
|
frames = call_stack.frames();
|
||||||
|
@ -577,10 +577,188 @@ TEST_F(CFI, RejectBackwards) {
|
||||||
TEST_F(CFI, RejectBadExpressions) {
|
TEST_F(CFI, RejectBadExpressions) {
|
||||||
raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40007000;
|
raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40007000;
|
||||||
raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = 0x80000000;
|
raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = 0x80000000;
|
||||||
StackwalkerARM walker(&system_info, &raw_context, &stack_region, &modules,
|
StackwalkerARM walker(&system_info, &raw_context, -1, &stack_region, &modules,
|
||||||
&supplier, &resolver);
|
&supplier, &resolver);
|
||||||
ASSERT_TRUE(walker.Walk(&call_stack));
|
ASSERT_TRUE(walker.Walk(&call_stack));
|
||||||
frames = call_stack.frames();
|
frames = call_stack.frames();
|
||||||
ASSERT_EQ(1U, frames->size());
|
ASSERT_EQ(1U, frames->size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class StackwalkerARMFixtureIOS : public StackwalkerARMFixture {
|
||||||
|
public:
|
||||||
|
StackwalkerARMFixtureIOS() {
|
||||||
|
system_info.os = "iOS";
|
||||||
|
system_info.os_short = "ios";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class GetFramesByFramePointer: public StackwalkerARMFixtureIOS, public Test { };
|
||||||
|
|
||||||
|
TEST_F(GetFramesByFramePointer, OnlyFramePointer) {
|
||||||
|
stack_section.start() = 0x80000000;
|
||||||
|
u_int32_t return_address1 = 0x50000100;
|
||||||
|
u_int32_t return_address2 = 0x50000900;
|
||||||
|
Label frame1_sp, frame2_sp;
|
||||||
|
Label frame1_fp, frame2_fp;
|
||||||
|
stack_section
|
||||||
|
// frame 0
|
||||||
|
.Append(32, 0) // Whatever values on the stack.
|
||||||
|
.D32(0x0000000D) // junk that's not
|
||||||
|
.D32(0xF0000000) // a return address.
|
||||||
|
|
||||||
|
.Mark(&frame1_fp) // Next fp will point to the next value.
|
||||||
|
.D32(frame2_fp) // Save current frame pointer.
|
||||||
|
.D32(return_address2) // Save current link register.
|
||||||
|
.Mark(&frame1_sp)
|
||||||
|
|
||||||
|
// frame 1
|
||||||
|
.Append(32, 0) // Whatever values on the stack.
|
||||||
|
.D32(0x0000000D) // junk that's not
|
||||||
|
.D32(0xF0000000) // a return address.
|
||||||
|
|
||||||
|
.Mark(&frame2_fp)
|
||||||
|
.D32(0)
|
||||||
|
.D32(0)
|
||||||
|
.Mark(&frame2_sp)
|
||||||
|
|
||||||
|
// frame 2
|
||||||
|
.Append(32, 0) // Whatever values on the stack.
|
||||||
|
.D32(0x0000000D) // junk that's not
|
||||||
|
.D32(0xF0000000); // a return address.
|
||||||
|
RegionFromSection();
|
||||||
|
|
||||||
|
|
||||||
|
raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40005510;
|
||||||
|
raw_context.iregs[MD_CONTEXT_ARM_REG_LR] = return_address1;
|
||||||
|
raw_context.iregs[MD_CONTEXT_ARM_REG_IOS_FP] = frame1_fp.Value();
|
||||||
|
raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = stack_section.start().Value();
|
||||||
|
|
||||||
|
StackwalkerARM walker(&system_info, &raw_context, MD_CONTEXT_ARM_REG_IOS_FP,
|
||||||
|
&stack_region, &modules, &supplier, &resolver);
|
||||||
|
|
||||||
|
ASSERT_TRUE(walker.Walk(&call_stack));
|
||||||
|
frames = call_stack.frames();
|
||||||
|
ASSERT_EQ(3U, frames->size());
|
||||||
|
|
||||||
|
StackFrameARM *frame0 = static_cast<StackFrameARM *>(frames->at(0));
|
||||||
|
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
|
||||||
|
ASSERT_EQ(StackFrameARM::CONTEXT_VALID_ALL, frame0->context_validity);
|
||||||
|
EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context)));
|
||||||
|
|
||||||
|
StackFrameARM *frame1 = static_cast<StackFrameARM *>(frames->at(1));
|
||||||
|
EXPECT_EQ(StackFrame::FRAME_TRUST_FP, frame1->trust);
|
||||||
|
ASSERT_EQ((StackFrameARM::CONTEXT_VALID_PC |
|
||||||
|
StackFrameARM::CONTEXT_VALID_LR |
|
||||||
|
StackFrameARM::RegisterValidFlag(MD_CONTEXT_ARM_REG_IOS_FP) |
|
||||||
|
StackFrameARM::CONTEXT_VALID_SP),
|
||||||
|
frame1->context_validity);
|
||||||
|
EXPECT_EQ(return_address1, frame1->context.iregs[MD_CONTEXT_ARM_REG_PC]);
|
||||||
|
EXPECT_EQ(return_address2, frame1->context.iregs[MD_CONTEXT_ARM_REG_LR]);
|
||||||
|
EXPECT_EQ(frame1_sp.Value(), frame1->context.iregs[MD_CONTEXT_ARM_REG_SP]);
|
||||||
|
EXPECT_EQ(frame2_fp.Value(),
|
||||||
|
frame1->context.iregs[MD_CONTEXT_ARM_REG_IOS_FP]);
|
||||||
|
|
||||||
|
StackFrameARM *frame2 = static_cast<StackFrameARM *>(frames->at(2));
|
||||||
|
EXPECT_EQ(StackFrame::FRAME_TRUST_FP, frame2->trust);
|
||||||
|
ASSERT_EQ((StackFrameARM::CONTEXT_VALID_PC |
|
||||||
|
StackFrameARM::CONTEXT_VALID_LR |
|
||||||
|
StackFrameARM::RegisterValidFlag(MD_CONTEXT_ARM_REG_IOS_FP) |
|
||||||
|
StackFrameARM::CONTEXT_VALID_SP),
|
||||||
|
frame2->context_validity);
|
||||||
|
EXPECT_EQ(return_address2, frame2->context.iregs[MD_CONTEXT_ARM_REG_PC]);
|
||||||
|
EXPECT_EQ(0, frame2->context.iregs[MD_CONTEXT_ARM_REG_LR]);
|
||||||
|
EXPECT_EQ(frame2_sp.Value(), frame2->context.iregs[MD_CONTEXT_ARM_REG_SP]);
|
||||||
|
EXPECT_EQ(0, frame2->context.iregs[MD_CONTEXT_ARM_REG_IOS_FP]);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(GetFramesByFramePointer, FramePointerAndCFI) {
|
||||||
|
// Provide the standatd STACK CFI records that is obtained when exmining an
|
||||||
|
// executable produced by XCode.
|
||||||
|
SetModuleSymbols(&module1,
|
||||||
|
// Adding a function in CFI.
|
||||||
|
"FUNC 4000 1000 10 enchiridion\n"
|
||||||
|
|
||||||
|
"STACK CFI INIT 4000 100 .cfa: sp 0 + .ra: lr\n"
|
||||||
|
"STACK CFI 4001 .cfa: sp 8 + .ra: .cfa -4 + ^"
|
||||||
|
" r7: .cfa -8 + ^\n"
|
||||||
|
"STACK CFI 4002 .cfa: r7 8 +\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
stack_section.start() = 0x80000000;
|
||||||
|
u_int32_t return_address1 = 0x40004010;
|
||||||
|
u_int32_t return_address2 = 0x50000900;
|
||||||
|
Label frame1_sp, frame2_sp;
|
||||||
|
Label frame1_fp, frame2_fp;
|
||||||
|
stack_section
|
||||||
|
// frame 0
|
||||||
|
.Append(32, 0) // Whatever values on the stack.
|
||||||
|
.D32(0x0000000D) // junk that's not
|
||||||
|
.D32(0xF0000000) // a return address.
|
||||||
|
|
||||||
|
.Mark(&frame1_fp) // Next fp will point to the next value.
|
||||||
|
.D32(frame2_fp) // Save current frame pointer.
|
||||||
|
.D32(return_address2) // Save current link register.
|
||||||
|
.Mark(&frame1_sp)
|
||||||
|
|
||||||
|
// frame 1
|
||||||
|
.Append(32, 0) // Whatever values on the stack.
|
||||||
|
.D32(0x0000000D) // junk that's not
|
||||||
|
.D32(0xF0000000) // a return address.
|
||||||
|
|
||||||
|
.Mark(&frame2_fp)
|
||||||
|
.D32(0)
|
||||||
|
.D32(0)
|
||||||
|
.Mark(&frame2_sp)
|
||||||
|
|
||||||
|
// frame 2
|
||||||
|
.Append(32, 0) // Whatever values on the stack.
|
||||||
|
.D32(0x0000000D) // junk that's not
|
||||||
|
.D32(0xF0000000); // a return address.
|
||||||
|
RegionFromSection();
|
||||||
|
|
||||||
|
|
||||||
|
raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x50000400;
|
||||||
|
raw_context.iregs[MD_CONTEXT_ARM_REG_LR] = return_address1;
|
||||||
|
raw_context.iregs[MD_CONTEXT_ARM_REG_IOS_FP] = frame1_fp.Value();
|
||||||
|
raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = stack_section.start().Value();
|
||||||
|
|
||||||
|
StackwalkerARM walker(&system_info, &raw_context, MD_CONTEXT_ARM_REG_IOS_FP,
|
||||||
|
&stack_region, &modules, &supplier, &resolver);
|
||||||
|
|
||||||
|
ASSERT_TRUE(walker.Walk(&call_stack));
|
||||||
|
frames = call_stack.frames();
|
||||||
|
ASSERT_EQ(3U, frames->size());
|
||||||
|
|
||||||
|
StackFrameARM *frame0 = static_cast<StackFrameARM *>(frames->at(0));
|
||||||
|
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
|
||||||
|
ASSERT_EQ(StackFrameARM::CONTEXT_VALID_ALL, frame0->context_validity);
|
||||||
|
EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context)));
|
||||||
|
|
||||||
|
StackFrameARM *frame1 = static_cast<StackFrameARM *>(frames->at(1));
|
||||||
|
EXPECT_EQ(StackFrame::FRAME_TRUST_FP, frame1->trust);
|
||||||
|
ASSERT_EQ((StackFrameARM::CONTEXT_VALID_PC |
|
||||||
|
StackFrameARM::CONTEXT_VALID_LR |
|
||||||
|
StackFrameARM::RegisterValidFlag(MD_CONTEXT_ARM_REG_IOS_FP) |
|
||||||
|
StackFrameARM::CONTEXT_VALID_SP),
|
||||||
|
frame1->context_validity);
|
||||||
|
EXPECT_EQ(return_address1, frame1->context.iregs[MD_CONTEXT_ARM_REG_PC]);
|
||||||
|
EXPECT_EQ(return_address2, frame1->context.iregs[MD_CONTEXT_ARM_REG_LR]);
|
||||||
|
EXPECT_EQ(frame1_sp.Value(), frame1->context.iregs[MD_CONTEXT_ARM_REG_SP]);
|
||||||
|
EXPECT_EQ(frame2_fp.Value(),
|
||||||
|
frame1->context.iregs[MD_CONTEXT_ARM_REG_IOS_FP]);
|
||||||
|
EXPECT_EQ("enchiridion", frame1->function_name);
|
||||||
|
EXPECT_EQ(0x40004000U, frame1->function_base);
|
||||||
|
|
||||||
|
|
||||||
|
StackFrameARM *frame2 = static_cast<StackFrameARM *>(frames->at(2));
|
||||||
|
EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame2->trust);
|
||||||
|
ASSERT_EQ((StackFrameARM::CONTEXT_VALID_PC |
|
||||||
|
StackFrameARM::CONTEXT_VALID_LR |
|
||||||
|
StackFrameARM::RegisterValidFlag(MD_CONTEXT_ARM_REG_IOS_FP) |
|
||||||
|
StackFrameARM::CONTEXT_VALID_SP),
|
||||||
|
frame2->context_validity);
|
||||||
|
EXPECT_EQ(return_address2, frame2->context.iregs[MD_CONTEXT_ARM_REG_PC]);
|
||||||
|
EXPECT_EQ(0, frame2->context.iregs[MD_CONTEXT_ARM_REG_LR]);
|
||||||
|
EXPECT_EQ(frame2_sp.Value(), frame2->context.iregs[MD_CONTEXT_ARM_REG_SP]);
|
||||||
|
EXPECT_EQ(0, frame2->context.iregs[MD_CONTEXT_ARM_REG_IOS_FP]);
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue