aboutsummaryrefslogtreecommitdiff
path: root/src/processor
diff options
context:
space:
mode:
authorrsesek@chromium.org <rsesek@chromium.org@4c0a9323-5329-0410-9bdc-e9ce6186880e>2014-07-18 00:27:49 +0000
committerrsesek@chromium.org <rsesek@chromium.org@4c0a9323-5329-0410-9bdc-e9ce6186880e>2014-07-18 00:27:49 +0000
commit6c57bc19a58a9fae96b0ce1dc7e969a65f2d37b8 (patch)
tree58ed13ec1e8e997ab5de6e5c770c7244a10f7b0d /src/processor
parentChange some ELF utils to return the length as a size_t. (diff)
downloadbreakpad-6c57bc19a58a9fae96b0ce1dc7e969a65f2d37b8.tar.xz
Add frame pointer recovery to the AMD64 Stackwalker.
BUG=https://code.google.com/p/chromium/issues/detail?id=393594 R=mark@chromium.org Review URL: https://breakpad.appspot.com/10664002 git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@1350 4c0a9323-5329-0410-9bdc-e9ce6186880e
Diffstat (limited to 'src/processor')
-rw-r--r--src/processor/stackwalker_amd64.cc54
-rw-r--r--src/processor/stackwalker_amd64.h8
-rw-r--r--src/processor/stackwalker_amd64_unittest.cc2
3 files changed, 61 insertions, 3 deletions
diff --git a/src/processor/stackwalker_amd64.cc b/src/processor/stackwalker_amd64.cc
index b2ffdb89..f252a33b 100644
--- a/src/processor/stackwalker_amd64.cc
+++ b/src/processor/stackwalker_amd64.cc
@@ -147,6 +147,52 @@ StackFrameAMD64* StackwalkerAMD64::GetCallerByCFIFrameInfo(
return frame.release();
}
+StackFrameAMD64* StackwalkerAMD64::GetCallerByFramePointerRecovery(
+ const vector<StackFrame*>& frames) {
+ StackFrameAMD64* last_frame = static_cast<StackFrameAMD64*>(frames.back());
+ uint64_t last_rsp = last_frame->context.rsp;
+ uint64_t last_rbp = last_frame->context.rbp;
+
+ // Assume the presence of a frame pointer. This is not mandated by the
+ // AMD64 ABI, c.f. section 3.2.2 footnote 7, though it is typical for
+ // compilers to still preserve the frame pointer and not treat %rbp as a
+ // general purpose register.
+ //
+ // With this assumption, the CALL instruction pushes the return address
+ // onto the stack and sets %rip to the procedure to enter. The procedure
+ // then establishes the stack frame with a prologue that PUSHes the current
+ // %rbp onto the stack, MOVes the current %rsp to %rbp, and then allocates
+ // space for any local variables. Using this procedure linking information,
+ // it is possible to locate frame information for the callee:
+ //
+ // %caller_rsp = *(%callee_rbp + 16)
+ // %caller_rip = *(%callee_rbp + 8)
+ // %caller_rbp = *(%callee_rbp)
+
+ uint64_t caller_rip, caller_rbp;
+ if (memory_->GetMemoryAtAddress(last_rbp + 8, &caller_rip) &&
+ memory_->GetMemoryAtAddress(last_rbp, &caller_rbp)) {
+ uint64_t caller_rsp = last_rbp + 16;
+
+ // Simple sanity check that the stack is growing downwards as expected.
+ if (caller_rbp < last_rbp || caller_rsp < last_rsp)
+ return NULL;
+
+ StackFrameAMD64* frame = new StackFrameAMD64();
+ frame->trust = StackFrame::FRAME_TRUST_FP;
+ frame->context = last_frame->context;
+ frame->context.rip = caller_rip;
+ frame->context.rsp = caller_rsp;
+ frame->context.rbp = caller_rbp;
+ frame->context_validity = StackFrameAMD64::CONTEXT_VALID_RIP |
+ StackFrameAMD64::CONTEXT_VALID_RSP |
+ StackFrameAMD64::CONTEXT_VALID_RBP;
+ return frame;
+ }
+
+ return NULL;
+}
+
StackFrameAMD64* StackwalkerAMD64::GetCallerByStackScan(
const vector<StackFrame*> &frames) {
StackFrameAMD64* last_frame = static_cast<StackFrameAMD64*>(frames.back());
@@ -214,8 +260,12 @@ StackFrame* StackwalkerAMD64::GetCallerFrame(const CallStack* stack,
if (cfi_frame_info.get())
new_frame.reset(GetCallerByCFIFrameInfo(frames, cfi_frame_info.get()));
- // If CFI failed, or there wasn't CFI available, fall back
- // to stack scanning.
+ // If CFI was not available or failed, try using frame pointer recovery.
+ if (!new_frame.get()) {
+ new_frame.reset(GetCallerByFramePointerRecovery(frames));
+ }
+
+ // If all else fails, fall back to stack scanning.
if (stack_scan_allowed && !new_frame.get()) {
new_frame.reset(GetCallerByStackScan(frames));
}
diff --git a/src/processor/stackwalker_amd64.h b/src/processor/stackwalker_amd64.h
index acdd2c2f..8f3dbd52 100644
--- a/src/processor/stackwalker_amd64.h
+++ b/src/processor/stackwalker_amd64.h
@@ -78,6 +78,14 @@ class StackwalkerAMD64 : public Stackwalker {
StackFrameAMD64* GetCallerByCFIFrameInfo(const vector<StackFrame*> &frames,
CFIFrameInfo* cfi_frame_info);
+ // Assumes a traditional frame layout where the frame pointer has not been
+ // omitted. The expectation is that caller's %rbp is pushed to the stack
+ // after the return address of the callee, and that the callee's %rsp can
+ // be used to find the pushed %rbp.
+ // Caller owns the returned frame object. Returns NULL on failure.
+ StackFrameAMD64* GetCallerByFramePointerRecovery(
+ const vector<StackFrame*>& frames);
+
// Scan the stack for plausible return addresses. The caller takes ownership
// of the returned frame. Return NULL on failure.
StackFrameAMD64* GetCallerByStackScan(const vector<StackFrame*> &frames);
diff --git a/src/processor/stackwalker_amd64_unittest.cc b/src/processor/stackwalker_amd64_unittest.cc
index 31489106..a54198bf 100644
--- a/src/processor/stackwalker_amd64_unittest.cc
+++ b/src/processor/stackwalker_amd64_unittest.cc
@@ -491,7 +491,7 @@ TEST_F(GetCallerFrame, CallerPushedRBP) {
EXPECT_EQ(0x40000000c0000100ULL, frame0->function_base);
StackFrameAMD64 *frame1 = static_cast<StackFrameAMD64 *>(frames->at(1));
- EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
+ EXPECT_EQ(StackFrame::FRAME_TRUST_FP, frame1->trust);
ASSERT_EQ((StackFrameAMD64::CONTEXT_VALID_RIP |
StackFrameAMD64::CONTEXT_VALID_RSP |
StackFrameAMD64::CONTEXT_VALID_RBP),