mirror of
https://github.com/yuzu-emu/breakpad.git
synced 2024-11-24 16:45:48 +01:00
Breakpad processor: Segregate STACK WIN vs. traditional stack walking.
This patch moves the code for finding caller frames using STACK WIN data and the code to do so using the traditional frame layout (%ebp points at saved %ebp, pushed just after return address) into their own functions. In addition to making things a little clearer, this is preparation for adding support for STACK CFI records into the mix. a=jimblandy, r=mmentovai git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@512 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
parent
89e07bf2c7
commit
7f1455601d
@ -36,13 +36,14 @@
|
||||
|
||||
#include "processor/postfix_evaluator-inl.h"
|
||||
|
||||
#include "processor/stackwalker_x86.h"
|
||||
#include "google_breakpad/processor/call_stack.h"
|
||||
#include "google_breakpad/processor/code_modules.h"
|
||||
#include "google_breakpad/processor/memory_region.h"
|
||||
#include "google_breakpad/processor/stack_frame_cpu.h"
|
||||
#include "google_breakpad/processor/source_line_resolver_interface.h"
|
||||
#include "google_breakpad/processor/stack_frame_cpu.h"
|
||||
#include "processor/logging.h"
|
||||
#include "processor/scoped_ptr.h"
|
||||
#include "processor/stackwalker_x86.h"
|
||||
#include "processor/windows_frame_info.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
@ -72,7 +73,7 @@ StackFrameX86::~StackFrameX86() {
|
||||
windows_frame_info = NULL;
|
||||
}
|
||||
|
||||
StackFrame* StackwalkerX86::GetContextFrame() {
|
||||
StackFrame *StackwalkerX86::GetContextFrame() {
|
||||
if (!context_ || !memory_) {
|
||||
BPLOG(ERROR) << "Can't get context frame without context or memory";
|
||||
return NULL;
|
||||
@ -90,23 +91,23 @@ StackFrame* StackwalkerX86::GetContextFrame() {
|
||||
return frame;
|
||||
}
|
||||
|
||||
|
||||
StackFrame* StackwalkerX86::GetCallerFrame(const CallStack *stack) {
|
||||
if (!memory_ || !stack) {
|
||||
BPLOG(ERROR) << "Can't get caller frame without memory or stack";
|
||||
return NULL;
|
||||
}
|
||||
StackFrameX86 *StackwalkerX86::GetCallerByWindowsFrameInfo(
|
||||
const vector<StackFrame *> &frames,
|
||||
WindowsFrameInfo *last_frame_info) {
|
||||
StackFrameX86::FrameTrust trust = StackFrameX86::FRAME_TRUST_NONE;
|
||||
StackFrameX86 *last_frame = static_cast<StackFrameX86*>(
|
||||
stack->frames()->back());
|
||||
|
||||
WindowsFrameInfo *last_frame_info
|
||||
= resolver_->FindWindowsFrameInfo(last_frame);
|
||||
StackFrameX86 *last_frame = static_cast<StackFrameX86 *>(frames.back());
|
||||
|
||||
// Save the stack walking info we found, in case we need it later to
|
||||
// find the callee of the frame we're constructing now.
|
||||
last_frame->windows_frame_info = last_frame_info;
|
||||
|
||||
// This function only covers the full STACK WIN case. If
|
||||
// last_frame_info is VALID_PARAMETER_SIZE-only, then we should
|
||||
// assume the traditional frame format or use some other strategy.
|
||||
if (last_frame_info->valid != WindowsFrameInfo::VALID_ALL)
|
||||
return NULL;
|
||||
|
||||
// This stackwalker sets each frame's %esp to its value immediately prior
|
||||
// to the CALL into the callee. This means that %esp points to the last
|
||||
// callee argument pushed onto the stack, which may not be where %esp points
|
||||
@ -139,12 +140,11 @@ StackFrame* StackwalkerX86::GetCallerFrame(const CallStack *stack) {
|
||||
// are unknown, 0 is also used in that case. When that happens, it should
|
||||
// be possible to walk to the next frame without reference to %esp.
|
||||
|
||||
int frames_already_walked = stack->frames()->size();
|
||||
u_int32_t last_frame_callee_parameter_size = 0;
|
||||
int frames_already_walked = frames.size();
|
||||
if (frames_already_walked >= 2) {
|
||||
StackFrameX86 *last_frame_callee
|
||||
= static_cast<StackFrameX86 *>((*stack->frames())
|
||||
[frames_already_walked - 2]);
|
||||
const StackFrameX86 *last_frame_callee
|
||||
= static_cast<StackFrameX86 *>(frames[frames_already_walked - 2]);
|
||||
WindowsFrameInfo *last_frame_callee_info
|
||||
= last_frame_callee->windows_frame_info;
|
||||
if (last_frame_callee_info &&
|
||||
@ -157,46 +157,35 @@ StackFrame* StackwalkerX86::GetCallerFrame(const CallStack *stack) {
|
||||
|
||||
// Set up the dictionary for the PostfixEvaluator. %ebp and %esp are used
|
||||
// in each program string, and their previous values are known, so set them
|
||||
// here. .cbCalleeParams is a Breakpad extension that allows us to use
|
||||
// the PostfixEvaluator engine when certain types of debugging information
|
||||
// are present without having to write the constants into the program string
|
||||
// as literals.
|
||||
// here.
|
||||
PostfixEvaluator<u_int32_t>::DictionaryType dictionary;
|
||||
// Provide the current register values.
|
||||
dictionary["$ebp"] = last_frame->context.ebp;
|
||||
dictionary["$esp"] = last_frame->context.esp;
|
||||
// Provide constants from the debug info for last_frame and its callee.
|
||||
// .cbCalleeParams is a Breakpad extension that allows us to use the
|
||||
// PostfixEvaluator engine when certain types of debugging information
|
||||
// are present without having to write the constants into the program
|
||||
// string as literals.
|
||||
dictionary[".cbCalleeParams"] = last_frame_callee_parameter_size;
|
||||
|
||||
if (last_frame_info && last_frame_info->valid == WindowsFrameInfo::VALID_ALL) {
|
||||
// FPO debugging data is available. Initialize constants.
|
||||
dictionary[".cbSavedRegs"] = last_frame_info->saved_register_size;
|
||||
dictionary[".cbLocals"] = last_frame_info->local_size;
|
||||
dictionary[".raSearchStart"] = last_frame->context.esp +
|
||||
last_frame_callee_parameter_size +
|
||||
last_frame_info->local_size +
|
||||
last_frame_info->saved_register_size;
|
||||
}
|
||||
if (last_frame_info &&
|
||||
last_frame_info->valid & WindowsFrameInfo::VALID_PARAMETER_SIZE) {
|
||||
// This is treated separately because it can either come from FPO data or
|
||||
// from other debugging data.
|
||||
dictionary[".cbParams"] = last_frame_info->parameter_size;
|
||||
}
|
||||
|
||||
// Decide what type of program string to use. The program string is in
|
||||
// postfix notation and will be passed to PostfixEvaluator::Evaluate.
|
||||
// Given the dictionary and the program string, it is possible to compute
|
||||
// the return address and the values of other registers in the calling
|
||||
// function. When encountering a nontraditional frame (one which takes
|
||||
// advantage of FPO), the stack may need to be scanned for these values.
|
||||
// For traditional frames, simple deterministic dereferencing suffices
|
||||
// without any need for scanning. The results of program string evaluation
|
||||
// function. Because of bugs described below, the stack may need to be
|
||||
// scanned for these values. The results of program string evaluation
|
||||
// will be used to determine whether to scan for better values.
|
||||
string program_string;
|
||||
bool traditional_frame = true;
|
||||
bool recover_ebp = true;
|
||||
if (last_frame_info && last_frame_info->valid == WindowsFrameInfo::VALID_ALL) {
|
||||
// FPO data available.
|
||||
traditional_frame = false;
|
||||
|
||||
trust = StackFrameX86::FRAME_TRUST_CFI;
|
||||
if (!last_frame_info->program_string.empty()) {
|
||||
// The FPO data has its own program string, which will tell us how to
|
||||
@ -268,38 +257,6 @@ StackFrame* StackwalkerX86::GetCallerFrame(const CallStack *stack) {
|
||||
"$esp .raSearchStart 4 + =";
|
||||
recover_ebp = false;
|
||||
}
|
||||
} else {
|
||||
// No FPO information is available for the last frame. Assume that the
|
||||
// standard %ebp-using x86 calling convention is in use.
|
||||
//
|
||||
// The typical x86 calling convention, when frame pointers are present,
|
||||
// is for the calling procedure to use CALL, which pushes the return
|
||||
// address onto the stack and sets the instruction pointer (%eip) to
|
||||
// the entry point of the called routine. The called routine then
|
||||
// PUSHes the calling routine's frame pointer (%ebp) onto the stack
|
||||
// before copying the stack pointer (%esp) to the frame pointer (%ebp).
|
||||
// Therefore, the calling procedure's frame pointer is always available
|
||||
// by dereferencing the called procedure's frame pointer, and the return
|
||||
// address is always available at the memory location immediately above
|
||||
// the address pointed to by the called procedure's frame pointer. The
|
||||
// calling procedure's stack pointer (%esp) is 8 higher than the value
|
||||
// of the called procedure's frame pointer at the time the calling
|
||||
// procedure made the CALL: 4 bytes for the return address pushed by the
|
||||
// CALL itself, and 4 bytes for the callee's PUSH of the caller's frame
|
||||
// pointer.
|
||||
//
|
||||
// Instruction and frame pointer recovery for these traditional frames is
|
||||
// entirely deterministic, and the stack will not be scanned after
|
||||
// recovering these values.
|
||||
//
|
||||
// %eip_new = *(%ebp_old + 4)
|
||||
// %esp_new = %ebp_old + 8
|
||||
// %ebp_new = *(%ebp_old)
|
||||
trust = StackFrameX86::FRAME_TRUST_FP;
|
||||
program_string = "$eip $ebp 4 + ^ = "
|
||||
"$esp $ebp 8 + = "
|
||||
"$ebp $ebp ^ =";
|
||||
}
|
||||
|
||||
// Now crank it out, making sure that the program string set at least the
|
||||
// two required variables.
|
||||
@ -331,15 +288,14 @@ StackFrame* StackwalkerX86::GetCallerFrame(const CallStack *stack) {
|
||||
trust = StackFrameX86::FRAME_TRUST_SCAN;
|
||||
}
|
||||
|
||||
// If this stack frame did not use %ebp in a traditional way, locating the
|
||||
// return address isn't entirely deterministic. In that case, the stack
|
||||
// can be scanned to locate the return address.
|
||||
// Since this stack frame did not use %ebp in a traditional way,
|
||||
// locating the return address isn't entirely deterministic. In that
|
||||
// case, the stack can be scanned to locate the return address.
|
||||
//
|
||||
// Even in nontraditional frames, if program string evaluation resulted in
|
||||
// both %eip and %ebp values of 0, trust that the end of the stack has been
|
||||
// However, if program string evaluation resulted in both %eip and
|
||||
// %ebp values of 0, trust that the end of the stack has been
|
||||
// reached and don't scan for anything else.
|
||||
if (!traditional_frame &&
|
||||
(dictionary["$eip"] != 0 || dictionary["$ebp"] != 0)) {
|
||||
if (dictionary["$eip"] != 0 || dictionary["$ebp"] != 0) {
|
||||
int offset = 0;
|
||||
|
||||
// This scan can only be done if a CodeModules object is available, to
|
||||
@ -401,13 +357,6 @@ StackFrame* StackwalkerX86::GetCallerFrame(const CallStack *stack) {
|
||||
}
|
||||
}
|
||||
|
||||
// Treat an instruction address of 0 as end-of-stack. Treat incorrect stack
|
||||
// direction as end-of-stack to enforce progress and avoid infinite loops.
|
||||
if (dictionary["$eip"] == 0 ||
|
||||
dictionary["$esp"] <= last_frame->context.esp) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Create a new stack frame (ownership will be transferred to the caller)
|
||||
// and fill it in.
|
||||
StackFrameX86 *frame = new StackFrameX86();
|
||||
@ -436,19 +385,129 @@ StackFrame* StackwalkerX86::GetCallerFrame(const CallStack *stack) {
|
||||
frame->context_validity |= StackFrameX86::CONTEXT_VALID_EDI;
|
||||
}
|
||||
|
||||
// frame->context.eip is the return address, which is one instruction
|
||||
// past the CALL that caused us to arrive at the callee. Set
|
||||
// frame->instruction to one less than that. This won't reference the
|
||||
// beginning of the CALL instruction, but it's guaranteed to be within the
|
||||
// CALL, which is sufficient to get the source line information to match up
|
||||
// with the line that contains a function call. Callers that require the
|
||||
// exact return address value may access the context.eip field of
|
||||
// StackFrameX86.
|
||||
frame->instruction = frame->context.eip - 1;
|
||||
return frame;
|
||||
}
|
||||
|
||||
StackFrameX86 *StackwalkerX86::GetCallerByEBPAtBase(
|
||||
const vector<StackFrame *> &frames) {
|
||||
StackFrameX86::FrameTrust trust;
|
||||
StackFrameX86 *last_frame = static_cast<StackFrameX86 *>(frames.back());
|
||||
u_int32_t last_esp = last_frame->context.esp;
|
||||
u_int32_t last_ebp = last_frame->context.ebp;
|
||||
|
||||
// Assume that the standard %ebp-using x86 calling convention is in
|
||||
// use.
|
||||
//
|
||||
// The typical x86 calling convention, when frame pointers are present,
|
||||
// is for the calling procedure to use CALL, which pushes the return
|
||||
// address onto the stack and sets the instruction pointer (%eip) to
|
||||
// the entry point of the called routine. The called routine then
|
||||
// PUSHes the calling routine's frame pointer (%ebp) onto the stack
|
||||
// before copying the stack pointer (%esp) to the frame pointer (%ebp).
|
||||
// Therefore, the calling procedure's frame pointer is always available
|
||||
// by dereferencing the called procedure's frame pointer, and the return
|
||||
// address is always available at the memory location immediately above
|
||||
// the address pointed to by the called procedure's frame pointer. The
|
||||
// calling procedure's stack pointer (%esp) is 8 higher than the value
|
||||
// of the called procedure's frame pointer at the time the calling
|
||||
// procedure made the CALL: 4 bytes for the return address pushed by the
|
||||
// CALL itself, and 4 bytes for the callee's PUSH of the caller's frame
|
||||
// pointer.
|
||||
//
|
||||
// %eip_new = *(%ebp_old + 4)
|
||||
// %esp_new = %ebp_old + 8
|
||||
// %ebp_new = *(%ebp_old)
|
||||
|
||||
u_int32_t caller_eip, caller_esp, caller_ebp;
|
||||
|
||||
if (memory_->GetMemoryAtAddress(last_ebp + 4, &caller_eip) &&
|
||||
memory_->GetMemoryAtAddress(last_ebp, &caller_ebp)) {
|
||||
caller_esp = last_ebp + 8;
|
||||
trust = StackFrameX86::FRAME_TRUST_FP;
|
||||
} else {
|
||||
// We couldn't read the memory %ebp refers to. It may be that %ebp
|
||||
// is pointing to non-stack memory. We'll scan the stack for a
|
||||
// return address. This can happen if last_frame is executing code
|
||||
// for a module for which we don't have symbols, and that module
|
||||
// is compiled without a frame pointer.
|
||||
if (!ScanForReturnAddress(last_esp, &caller_esp, &caller_eip)) {
|
||||
// if we can't find an instruction pointer even with stack scanning,
|
||||
// give up.
|
||||
return false;
|
||||
}
|
||||
|
||||
// ScanForReturnAddress found a reasonable return address. Advance
|
||||
// %esp to the location above the one where the return address was
|
||||
// found. Assume that %ebp is unchanged.
|
||||
caller_esp += 4;
|
||||
caller_ebp = last_ebp;
|
||||
|
||||
trust = StackFrameX86::FRAME_TRUST_SCAN;
|
||||
}
|
||||
|
||||
// Create a new stack frame (ownership will be transferred to the caller)
|
||||
// and fill it in.
|
||||
StackFrameX86 *frame = new StackFrameX86();
|
||||
|
||||
frame->trust = trust;
|
||||
frame->context = last_frame->context;
|
||||
frame->context.eip = caller_eip;
|
||||
frame->context.esp = caller_esp;
|
||||
frame->context.ebp = caller_ebp;
|
||||
frame->context_validity = StackFrameX86::CONTEXT_VALID_EIP |
|
||||
StackFrameX86::CONTEXT_VALID_ESP |
|
||||
StackFrameX86::CONTEXT_VALID_EBP;
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
StackFrame *StackwalkerX86::GetCallerFrame(const CallStack *stack) {
|
||||
if (!memory_ || !stack) {
|
||||
BPLOG(ERROR) << "Can't get caller frame without memory or stack";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const vector<StackFrame *> &frames = *stack->frames();
|
||||
StackFrameX86 *last_frame = static_cast<StackFrameX86 *>(frames.back());
|
||||
scoped_ptr<StackFrameX86> new_frame;
|
||||
|
||||
// If we have Windows stack walking information, use that.
|
||||
WindowsFrameInfo *windows_frame_info
|
||||
= resolver_->FindWindowsFrameInfo(last_frame);
|
||||
if (windows_frame_info)
|
||||
new_frame.reset(GetCallerByWindowsFrameInfo(frames, windows_frame_info));
|
||||
|
||||
// Otherwise, hope that we're using a traditional frame structure.
|
||||
if (!new_frame.get())
|
||||
new_frame.reset(GetCallerByEBPAtBase(frames));
|
||||
|
||||
// If nothing worked, tell the caller.
|
||||
if (!new_frame.get())
|
||||
return NULL;
|
||||
|
||||
// Treat an instruction address of 0 as end-of-stack.
|
||||
if (new_frame->context.eip == 0)
|
||||
return NULL;
|
||||
|
||||
// If the new stack pointer is at a lower address than the old, then
|
||||
// that's clearly incorrect. Treat this as end-of-stack to enforce
|
||||
// progress and avoid infinite loops.
|
||||
if (new_frame->context.esp <= last_frame->context.esp)
|
||||
return NULL;
|
||||
|
||||
// new_frame->context.eip is the return address, which is one instruction
|
||||
// past the CALL that caused us to arrive at the callee. Set
|
||||
// new_frame->instruction to one less than that. This won't reference the
|
||||
// beginning of the CALL instruction, but it's guaranteed to be within
|
||||
// the CALL, which is sufficient to get the source line information to
|
||||
// match up with the line that contains a function call. Callers that
|
||||
// require the exact return address value may access the context.eip
|
||||
// field of StackFrameX86.
|
||||
new_frame->instruction = new_frame->context.eip - 1;
|
||||
|
||||
return new_frame.release();
|
||||
}
|
||||
|
||||
bool StackwalkerX86::ScanForReturnAddress(u_int32_t location_start,
|
||||
u_int32_t *location_found,
|
||||
u_int32_t *eip_found) {
|
||||
|
@ -1,3 +1,5 @@
|
||||
// -*- mode: c++ -*-
|
||||
|
||||
// Copyright (c) 2006, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
@ -42,6 +44,7 @@
|
||||
#include "google_breakpad/common/breakpad_types.h"
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
#include "google_breakpad/processor/stackwalker.h"
|
||||
#include "google_breakpad/processor/stack_frame_cpu.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
@ -66,8 +69,22 @@ class StackwalkerX86 : public Stackwalker {
|
||||
// stack conventions (saved %ebp at [%ebp], saved %eip at 4[%ebp], or
|
||||
// alternate conventions as guided by any WindowsFrameInfo available for the
|
||||
// code in question.).
|
||||
virtual StackFrame* GetContextFrame();
|
||||
virtual StackFrame* GetCallerFrame(const CallStack *stack);
|
||||
virtual StackFrame *GetContextFrame();
|
||||
virtual StackFrame *GetCallerFrame(const CallStack *stack);
|
||||
|
||||
// Use windows_frame_info (derived from STACK WIN and FUNC records)
|
||||
// to construct the frame that called frames.back(). The caller
|
||||
// takes ownership of the returned frame. Return NULL on failure.
|
||||
StackFrameX86 *GetCallerByWindowsFrameInfo(
|
||||
const vector<StackFrame*> &frames,
|
||||
WindowsFrameInfo *windows_frame_info);
|
||||
|
||||
// Assuming a traditional frame layout --- where the caller's %ebp
|
||||
// has been pushed just after the return address and the callee's
|
||||
// %ebp points to the saved %ebp --- construct the frame that called
|
||||
// frames.back(). The caller takes ownership of the returned frame.
|
||||
// Return NULL on failure.
|
||||
StackFrameX86 *GetCallerByEBPAtBase(const vector<StackFrame*> &frames);
|
||||
|
||||
// Scan the stack starting at location_start, looking for an address
|
||||
// that looks like a valid instruction pointer. Addresses must
|
||||
|
Loading…
Reference in New Issue
Block a user