1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-08-16 02:54:33 +02:00

* Updated breakpad to latest version.

This commit is contained in:
Christian Muehlhaeuser
2012-06-24 18:25:34 +02:00
parent ffd2cee2ff
commit 6aae2dd96f
186 changed files with 9184 additions and 5835 deletions

View File

@@ -200,6 +200,7 @@ TEST_F(TestBasicSourceLineResolver, TestLoadAndResolve)
ASSERT_EQ(frame.source_line_base, 0x1000);
windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame));
ASSERT_TRUE(windows_frame_info.get());
ASSERT_EQ(windows_frame_info->type_, WindowsFrameInfo::STACK_INFO_FRAME_DATA);
ASSERT_FALSE(windows_frame_info->allocates_base_pointer);
ASSERT_EQ(windows_frame_info->program_string,
"$eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ =");
@@ -219,6 +220,7 @@ TEST_F(TestBasicSourceLineResolver, TestLoadAndResolve)
ASSERT_EQ(frame.source_line, 0);
windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame));
ASSERT_TRUE(windows_frame_info.get());
ASSERT_EQ(windows_frame_info->type_, WindowsFrameInfo::STACK_INFO_UNKNOWN);
ASSERT_FALSE(windows_frame_info->allocates_base_pointer);
ASSERT_TRUE(windows_frame_info->program_string.empty());
@@ -228,6 +230,7 @@ TEST_F(TestBasicSourceLineResolver, TestLoadAndResolve)
ASSERT_TRUE(frame.source_file_name.empty());
ASSERT_EQ(frame.source_line, 0);
windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame));
ASSERT_EQ(windows_frame_info->type_, WindowsFrameInfo::STACK_INFO_FRAME_DATA);
ASSERT_TRUE(windows_frame_info.get());
ASSERT_FALSE(windows_frame_info->allocates_base_pointer);
ASSERT_FALSE(windows_frame_info->program_string.empty());
@@ -351,6 +354,7 @@ TEST_F(TestBasicSourceLineResolver, TestLoadAndResolve)
ASSERT_EQ(frame.source_line_base, 0x2180);
windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame));
ASSERT_TRUE(windows_frame_info.get());
ASSERT_EQ(windows_frame_info->type_, WindowsFrameInfo::STACK_INFO_FRAME_DATA);
ASSERT_EQ(windows_frame_info->prolog_size, 1);
frame.instruction = 0x216f;

View File

@@ -77,6 +77,9 @@ class binarystream {
void rewind() {
stream_.seekg (0, ios::beg);
stream_.seekp (0, ios::beg);
// This is to clear all the error flags, since only the EOF flag is cleared
// with seekg().
stream_.clear();
}
private:

View File

@@ -48,6 +48,9 @@ DisassemblerX86::DisassemblerX86(const u_int8_t *bytecode,
}
DisassemblerX86::~DisassemblerX86() {
if (instr_valid_)
libdis::x86_oplist_free(&current_instr_);
libdis::x86_cleanup();
}

View File

@@ -75,6 +75,7 @@ Exploitability *Exploitability::ExploitabilityForPlatform(
break;
}
case MD_OS_MAC_OS_X:
case MD_OS_IOS:
case MD_OS_LINUX:
case MD_OS_UNIX:
case MD_OS_SOLARIS:

View File

@@ -107,10 +107,15 @@ void FastSourceLineResolver::Module::LookupAddress(StackFrame *frame) const {
// WFI: WindowsFrameInfo.
// Returns a WFI object reading from a raw memory chunk of data
WindowsFrameInfo FastSourceLineResolver::CopyWFI(const char *raw) {
// The first 4Bytes of int data are unused.
// They corresponds to "int valid;" data member of WFI.
const WindowsFrameInfo::StackInfoTypes type =
static_cast<const WindowsFrameInfo::StackInfoTypes>(
*reinterpret_cast<const int32_t*>(raw));
// The first 8 bytes of int data are unused.
// They correspond to "StackInfoTypes type_;" and "int valid;"
// data member of WFI.
const u_int32_t *para_uint32 = reinterpret_cast<const u_int32_t*>(
raw + sizeof(int32_t));
raw + 2 * sizeof(int32_t));
u_int32_t prolog_size = para_uint32[0];;
u_int32_t epilog_size = para_uint32[1];
@@ -122,7 +127,8 @@ WindowsFrameInfo FastSourceLineResolver::CopyWFI(const char *raw) {
bool allocates_base_pointer = (*boolean != 0);
std::string program_string = boolean + 1;
return WindowsFrameInfo(prolog_size,
return WindowsFrameInfo(type,
prolog_size,
epilog_size,
parameter_size,
saved_register_size,

View File

@@ -248,6 +248,7 @@ TEST_F(TestFastSourceLineResolver, TestLoadAndResolve) {
ASSERT_EQ(frame.source_line, 0);
windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame));
ASSERT_TRUE(windows_frame_info.get());
ASSERT_EQ(windows_frame_info->type_, WindowsFrameInfo::STACK_INFO_UNKNOWN);
ASSERT_FALSE(windows_frame_info->allocates_base_pointer);
ASSERT_TRUE(windows_frame_info->program_string.empty());
@@ -258,6 +259,7 @@ TEST_F(TestFastSourceLineResolver, TestLoadAndResolve) {
ASSERT_EQ(frame.source_line, 0);
windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame));
ASSERT_TRUE(windows_frame_info.get());
ASSERT_EQ(windows_frame_info->type_, WindowsFrameInfo::STACK_INFO_FRAME_DATA);
ASSERT_FALSE(windows_frame_info->allocates_base_pointer);
ASSERT_FALSE(windows_frame_info->program_string.empty());
@@ -380,6 +382,7 @@ TEST_F(TestFastSourceLineResolver, TestLoadAndResolve) {
ASSERT_EQ(frame.source_line_base, 0x2180);
windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame));
ASSERT_TRUE(windows_frame_info.get());
ASSERT_EQ(windows_frame_info->type_, WindowsFrameInfo::STACK_INFO_FRAME_DATA);
ASSERT_EQ(windows_frame_info->prolog_size, 1);
frame.instruction = 0x216f;

View File

@@ -1758,6 +1758,7 @@ string MinidumpModule::code_identifier() const {
}
case MD_OS_MAC_OS_X:
case MD_OS_IOS:
case MD_OS_SOLARIS:
case MD_OS_LINUX: {
// TODO(mmentovai): support uuid extension if present, otherwise fall
@@ -2482,7 +2483,7 @@ const MinidumpModule* MinidumpModuleList::GetMainModule() const {
// The main code module is the first one present in a minidump file's
// MDRawModuleList.
return GetModuleAtSequence(0);
return GetModuleAtIndex(0);
}
@@ -3095,6 +3096,10 @@ string MinidumpSystemInfo::GetOS() {
os = "mac";
break;
case MD_OS_IOS:
os = "ios";
break;
case MD_OS_LINUX:
os = "linux";
break;

View File

@@ -386,6 +386,11 @@ bool MinidumpProcessor::GetOSInfo(Minidump *dump, SystemInfo *info) {
break;
}
case MD_OS_IOS: {
info->os = "iOS";
break;
}
case MD_OS_LINUX: {
info->os = "Linux";
break;
@@ -451,7 +456,8 @@ string MinidumpProcessor::GetCrashReason(Minidump *dump, u_int64_t *address) {
return reason;
switch (raw_system_info->platform_id) {
case MD_OS_MAC_OS_X: {
case MD_OS_MAC_OS_X:
case MD_OS_IOS: {
char flags_string[11];
snprintf(flags_string, sizeof(flags_string), "0x%08x", exception_flags);
switch (exception_code) {
@@ -637,6 +643,12 @@ string MinidumpProcessor::GetCrashReason(Minidump *dump, u_int64_t *address) {
case MD_EXCEPTION_MAC_SOFTWARE:
reason = "EXC_SOFTWARE / ";
switch (exception_flags) {
case MD_EXCEPTION_CODE_MAC_ABORT:
reason.append("SIGABRT");
break;
case MD_EXCEPTION_CODE_MAC_NS_EXCEPTION:
reason.append("UNCAUGHT_NS_EXCEPTION");
break;
// These are ppc only but shouldn't be a problem as they're
// unused on x86
case MD_EXCEPTION_CODE_MAC_PPC_TRAP:

View File

@@ -240,6 +240,7 @@ bool ModuleComparer::ComparePubSymbol(const BasicPubSymbol* basic_ps,
bool ModuleComparer::CompareWFI(const WindowsFrameInfo& wfi1,
const WindowsFrameInfo& wfi2) const {
ASSERT_TRUE(wfi1.type_ == wfi2.type_);
ASSERT_TRUE(wfi1.valid == wfi2.valid);
ASSERT_TRUE(wfi1.prolog_size == wfi2.prolog_size);
ASSERT_TRUE(wfi1.epilog_size == wfi2.epilog_size);

View File

@@ -66,6 +66,141 @@ class AutoStackClearer {
};
template<typename ValueType>
bool PostfixEvaluator<ValueType>::EvaluateToken(
const string &token,
const string &expression,
DictionaryValidityType *assigned) {
// There are enough binary operations that do exactly the same thing
// (other than the specific operation, of course) that it makes sense
// to share as much code as possible.
enum BinaryOperation {
BINARY_OP_NONE = 0,
BINARY_OP_ADD,
BINARY_OP_SUBTRACT,
BINARY_OP_MULTIPLY,
BINARY_OP_DIVIDE_QUOTIENT,
BINARY_OP_DIVIDE_MODULUS,
BINARY_OP_ALIGN
};
BinaryOperation operation = BINARY_OP_NONE;
if (token == "+")
operation = BINARY_OP_ADD;
else if (token == "-")
operation = BINARY_OP_SUBTRACT;
else if (token == "*")
operation = BINARY_OP_MULTIPLY;
else if (token == "/")
operation = BINARY_OP_DIVIDE_QUOTIENT;
else if (token == "%")
operation = BINARY_OP_DIVIDE_MODULUS;
else if (token == "@")
operation = BINARY_OP_ALIGN;
if (operation != BINARY_OP_NONE) {
// Get the operands.
ValueType operand1 = ValueType();
ValueType operand2 = ValueType();
if (!PopValues(&operand1, &operand2)) {
BPLOG(ERROR) << "Could not PopValues to get two values for binary "
"operation " << token << ": " << expression;
return false;
}
// Perform the operation.
ValueType result;
switch (operation) {
case BINARY_OP_ADD:
result = operand1 + operand2;
break;
case BINARY_OP_SUBTRACT:
result = operand1 - operand2;
break;
case BINARY_OP_MULTIPLY:
result = operand1 * operand2;
break;
case BINARY_OP_DIVIDE_QUOTIENT:
result = operand1 / operand2;
break;
case BINARY_OP_DIVIDE_MODULUS:
result = operand1 % operand2;
break;
case BINARY_OP_ALIGN:
result =
operand1 & (static_cast<ValueType>(-1) ^ (operand2 - 1));
break;
case BINARY_OP_NONE:
// This will not happen, but compilers will want a default or
// BINARY_OP_NONE case.
BPLOG(ERROR) << "Not reached!";
return false;
break;
}
// Save the result.
PushValue(result);
} else if (token == "^") {
// ^ for unary dereference. Can't dereference without memory.
if (!memory_) {
BPLOG(ERROR) << "Attempt to dereference without memory: " <<
expression;
return false;
}
ValueType address;
if (!PopValue(&address)) {
BPLOG(ERROR) << "Could not PopValue to get value to derefence: " <<
expression;
return false;
}
ValueType value;
if (!memory_->GetMemoryAtAddress(address, &value)) {
BPLOG(ERROR) << "Could not dereference memory at address " <<
HexString(address) << ": " << expression;
return false;
}
PushValue(value);
} else if (token == "=") {
// = for assignment.
ValueType value;
if (!PopValue(&value)) {
BPLOG(INFO) << "Could not PopValue to get value to assign: " <<
expression;
return false;
}
// Assignment is only meaningful when assigning into an identifier.
// The identifier must name a variable, not a constant. Variables
// begin with '$'.
string identifier;
if (PopValueOrIdentifier(NULL, &identifier) != POP_RESULT_IDENTIFIER) {
BPLOG(ERROR) << "PopValueOrIdentifier returned a value, but an "
"identifier is needed to assign " <<
HexString(value) << ": " << expression;
return false;
}
if (identifier.empty() || identifier[0] != '$') {
BPLOG(ERROR) << "Can't assign " << HexString(value) << " to " <<
identifier << ": " << expression;
return false;
}
(*dictionary_)[identifier] = value;
if (assigned)
(*assigned)[identifier] = true;
} else {
// The token is not an operator, it's a literal value or an identifier.
// Push it onto the stack as-is. Use push_back instead of PushValue
// because PushValue pushes ValueType as a string, but token is already
// a string.
stack_.push_back(token);
}
return true;
}
template<typename ValueType>
bool PostfixEvaluator<ValueType>::EvaluateInternal(
const string &expression,
@@ -74,125 +209,21 @@ bool PostfixEvaluator<ValueType>::EvaluateInternal(
istringstream stream(expression);
string token;
while (stream >> token) {
// There are enough binary operations that do exactly the same thing
// (other than the specific operation, of course) that it makes sense
// to share as much code as possible.
enum BinaryOperation {
BINARY_OP_NONE = 0,
BINARY_OP_ADD,
BINARY_OP_SUBTRACT,
BINARY_OP_MULTIPLY,
BINARY_OP_DIVIDE_QUOTIENT,
BINARY_OP_DIVIDE_MODULUS
};
BinaryOperation operation = BINARY_OP_NONE;
if (token == "+")
operation = BINARY_OP_ADD;
else if (token == "-")
operation = BINARY_OP_SUBTRACT;
else if (token == "*")
operation = BINARY_OP_MULTIPLY;
else if (token == "/")
operation = BINARY_OP_DIVIDE_QUOTIENT;
else if (token == "%")
operation = BINARY_OP_DIVIDE_MODULUS;
if (operation != BINARY_OP_NONE) {
// Get the operands.
ValueType operand1 = ValueType();
ValueType operand2 = ValueType();
if (!PopValues(&operand1, &operand2)) {
BPLOG(ERROR) << "Could not PopValues to get two values for binary "
"operation " << token << ": " << expression;
// Normally, tokens are whitespace-separated, but occasionally, the
// assignment operator is smashed up against the next token, i.e.
// $T0 $ebp 128 + =$eip $T0 4 + ^ =$ebp $T0 ^ =
// This has been observed in program strings produced by MSVS 2010 in LTO
// mode.
if (token.size() > 1 && token[0] == '=') {
if (!EvaluateToken("=", expression, assigned)) {
return false;
}
// Perform the operation.
ValueType result;
switch (operation) {
case BINARY_OP_ADD:
result = operand1 + operand2;
break;
case BINARY_OP_SUBTRACT:
result = operand1 - operand2;
break;
case BINARY_OP_MULTIPLY:
result = operand1 * operand2;
break;
case BINARY_OP_DIVIDE_QUOTIENT:
result = operand1 / operand2;
break;
case BINARY_OP_DIVIDE_MODULUS:
result = operand1 % operand2;
break;
case BINARY_OP_NONE:
// This will not happen, but compilers will want a default or
// BINARY_OP_NONE case.
BPLOG(ERROR) << "Not reached!";
return false;
break;
}
// Save the result.
PushValue(result);
} else if (token == "^") {
// ^ for unary dereference. Can't dereference without memory.
if (!memory_) {
BPLOG(ERROR) << "Attempt to dereference without memory: " <<
expression;
if (!EvaluateToken(token.substr(1), expression, assigned)) {
return false;
}
ValueType address;
if (!PopValue(&address)) {
BPLOG(ERROR) << "Could not PopValue to get value to derefence: " <<
expression;
return false;
}
ValueType value;
if (!memory_->GetMemoryAtAddress(address, &value)) {
BPLOG(ERROR) << "Could not dereference memory at address " <<
HexString(address) << ": " << expression;
return false;
}
PushValue(value);
} else if (token == "=") {
// = for assignment.
ValueType value;
if (!PopValue(&value)) {
BPLOG(INFO) << "Could not PopValue to get value to assign: " <<
expression;
return false;
}
// Assignment is only meaningful when assigning into an identifier.
// The identifier must name a variable, not a constant. Variables
// begin with '$'.
string identifier;
if (PopValueOrIdentifier(NULL, &identifier) != POP_RESULT_IDENTIFIER) {
BPLOG(ERROR) << "PopValueOrIdentifier returned a value, but an "
"identifier is needed to assign " <<
HexString(value) << ": " << expression;
return false;
}
if (identifier.empty() || identifier[0] != '$') {
BPLOG(ERROR) << "Can't assign " << HexString(value) << " to " <<
identifier << ": " << expression;
return false;
}
(*dictionary_)[identifier] = value;
if (assigned)
(*assigned)[identifier] = true;
} else {
// The token is not an operator, it's a literal value or an identifier.
// Push it onto the stack as-is. Use push_back instead of PushValue
// because PushValue pushes ValueType as a string, but token is already
// a string.
stack_.push_back(token);
} else if (!EvaluateToken(token, expression, assigned)) {
return false;
}
}

View File

@@ -44,10 +44,12 @@
// either literal values suitable for ValueType, or constants or variables,
// which reference the dictionary. The supported binary operators are +
// (addition), - (subtraction), * (multiplication), / (quotient of division),
// and % (modulus of division). The unary ^ (dereference) operator is also
// provided. These operators allow any operand to be either a literal
// value, constant, or variable. Assignment (=) of any type of operand into
// a variable is also supported.
// % (modulus of division), and @ (data alignment). The alignment operator (@)
// accepts a value and an alignment size, and produces a result that is a
// multiple of the alignment size by truncating the input value.
// The unary ^ (dereference) operator is also provided. These operators
// allow any operand to be either a literal value, constant, or variable.
// Assignment (=) of any type of operand into a variable is also supported.
//
// The dictionary is provided as a map with string keys. Keys beginning
// with the '$' character are treated as variables. All other keys are
@@ -151,6 +153,10 @@ class PostfixEvaluator {
bool EvaluateInternal(const string &expression,
DictionaryValidityType *assigned);
bool EvaluateToken(const string &token,
const string &expression,
DictionaryValidityType *assigned);
// The dictionary mapping constant and variable identifiers (strings) to
// values. Keys beginning with '$' are treated as variable names, and
// PostfixEvaluator is free to create and modify these keys. Weak pointer.

View File

@@ -148,7 +148,9 @@ static bool RunTests() {
{ "$rSub 9 6 - =", true }, // $rSub = 9 - 6 = 3
{ "$rDivQ 9 6 / =", true }, // $rDivQ = 9 / 6 = 1
{ "$rDivM 9 6 % =", true }, // $rDivM = 9 % 6 = 3
{ "$rDeref 9 ^ =", true } // $rDeref = ^9 = 10 (FakeMemoryRegion)
{ "$rDeref 9 ^ =", true }, // $rDeref = ^9 = 10 (FakeMemoryRegion)
{ "$rAlign 36 8 @ =", true }, // $rAlign = 36 @ 8
{ "$rAdd3 2 2 + =$rMul2 9 6 * =", true } // smashed-equals tokenization
};
map<string, unsigned int> validate_data_0;
validate_data_0["$rAdd"] = 8;
@@ -158,6 +160,9 @@ static bool RunTests() {
validate_data_0["$rDivQ"] = 1;
validate_data_0["$rDivM"] = 3;
validate_data_0["$rDeref"] = 10;
validate_data_0["$rAlign"] = 32;
validate_data_0["$rAdd3"] = 4;
validate_data_0["$rMul2"] = 54;
// The second test set simulates a couple of MSVC program strings.
// The data is fudged a little bit because the tests use FakeMemoryRegion

View File

@@ -132,6 +132,7 @@ class SimpleSerializer<WindowsFrameInfo> {
public:
static size_t SizeOf(const WindowsFrameInfo &wfi) {
unsigned int size = 0;
size += sizeof(int32_t); // wfi.type_
size += SimpleSerializer<int32_t>::SizeOf(wfi.valid);
size += SimpleSerializer<u_int32_t>::SizeOf(wfi.prolog_size);
size += SimpleSerializer<u_int32_t>::SizeOf(wfi.epilog_size);
@@ -144,6 +145,8 @@ class SimpleSerializer<WindowsFrameInfo> {
return size;
}
static char *Write(const WindowsFrameInfo &wfi, char *dest) {
dest = SimpleSerializer<int32_t>::Write(
static_cast<const int32_t>(wfi.type_), dest);
dest = SimpleSerializer<int32_t>::Write(wfi.valid, dest);
dest = SimpleSerializer<u_int32_t>::Write(wfi.prolog_size, dest);
dest = SimpleSerializer<u_int32_t>::Write(wfi.epilog_size, dest);

View File

@@ -88,7 +88,7 @@ bool SourceLineResolverBase::ReadSymbolFile(char **symbol_data,
int error_code = stat(map_file.c_str(), &buf);
if (error_code == -1) {
string error_string;
int error_code = ErrnoString(&error_string);
error_code = ErrnoString(&error_string);
BPLOG(ERROR) << "Could not open " << map_file <<
", error " << error_code << ": " << error_string;
return false;
@@ -110,7 +110,7 @@ bool SourceLineResolverBase::ReadSymbolFile(char **symbol_data,
FILE *f = fopen(map_file.c_str(), "rt");
if (!f) {
string error_string;
int error_code = ErrnoString(&error_string);
error_code = ErrnoString(&error_string);
BPLOG(ERROR) << "Could not open " << map_file <<
", error " << error_code << ": " << error_string;
delete [] (*symbol_data);
@@ -126,7 +126,7 @@ bool SourceLineResolverBase::ReadSymbolFile(char **symbol_data,
if (items_read != file_size) {
string error_string;
int error_code = ErrnoString(&error_string);
error_code = ErrnoString(&error_string);
BPLOG(ERROR) << "Could not slurp " << map_file <<
", error " << error_code << ": " << error_string;
delete [] (*symbol_data);
@@ -240,11 +240,11 @@ void SourceLineResolverBase::UnloadModule(const CodeModule *code_module) {
if (!code_module)
return;
ModuleMap::iterator iter = modules_->find(code_module->code_file());
if (iter != modules_->end()) {
Module *symbol_module = iter->second;
ModuleMap::iterator mod_iter = modules_->find(code_module->code_file());
if (mod_iter != modules_->end()) {
Module *symbol_module = mod_iter->second;
delete symbol_module;
modules_->erase(iter);
modules_->erase(mod_iter);
}
if (ShouldDeleteMemoryBufferAfterLoadModule()) {

View File

@@ -44,6 +44,7 @@
#include "google_breakpad/processor/source_line_resolver_interface.h"
#include "google_breakpad/processor/stack_frame.h"
#include "google_breakpad/processor/symbol_supplier.h"
#include "google_breakpad/processor/system_info.h"
#include "processor/linked_ptr.h"
#include "processor/logging.h"
#include "processor/scoped_ptr.h"
@@ -187,10 +188,13 @@ Stackwalker* Stackwalker::StackwalkerForCPU(
break;
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,
context->GetContextARM(),
memory, modules, supplier,
resolver);
fp_register, memory, modules,
supplier, resolver);
break;
}

View File

@@ -146,18 +146,13 @@ StackFrameAMD64 *StackwalkerAMD64::GetCallerByStackScan(
const vector<StackFrame *> &frames) {
StackFrameAMD64 *last_frame = static_cast<StackFrameAMD64 *>(frames.back());
u_int64_t last_rsp = last_frame->context.rsp;
u_int64_t caller_rsp, caller_rip;
if (!ScanForReturnAddress(last_rsp, &caller_rsp, &caller_rip)) {
u_int64_t caller_rip_address, caller_rip;
if (!ScanForReturnAddress(last_rsp, &caller_rip_address, &caller_rip)) {
// No plausible return address was found.
return NULL;
}
// ScanForReturnAddress found a reasonable return address. Advance
// %rsp to the location above the one where the return address was
// found.
caller_rsp += 8;
// Create a new stack frame (ownership will be transferred to the caller)
// and fill it in.
StackFrameAMD64 *frame = new StackFrameAMD64();
@@ -165,10 +160,34 @@ StackFrameAMD64 *StackwalkerAMD64::GetCallerByStackScan(
frame->trust = StackFrame::FRAME_TRUST_SCAN;
frame->context = last_frame->context;
frame->context.rip = caller_rip;
frame->context.rsp = caller_rsp;
// The caller's %rsp is directly underneath the return address pushed by
// the call.
frame->context.rsp = caller_rip_address + 8;
frame->context_validity = StackFrameAMD64::CONTEXT_VALID_RIP |
StackFrameAMD64::CONTEXT_VALID_RSP;
// Other unwinders give up if they don't have an %rbp value, so see if we
// can pass some plausible value on.
if (last_frame->context_validity & StackFrameAMD64::CONTEXT_VALID_RBP) {
// Functions typically push their caller's %rbp immediately upon entry,
// and then set %rbp to point to that. So if the callee's %rbp is
// pointing to the first word below the alleged return address, presume
// that the caller's %rbp is saved there.
if (caller_rip_address - 8 == last_frame->context.rbp) {
u_int64_t caller_rbp = 0;
if (memory_->GetMemoryAtAddress(last_frame->context.rbp, &caller_rbp) &&
caller_rbp > caller_rip_address) {
frame->context.rbp = caller_rbp;
frame->context_validity |= StackFrameAMD64::CONTEXT_VALID_RBP;
}
} else if (last_frame->context.rbp >= caller_rip_address + 8) {
// If the callee's %rbp is plausible as a value for the caller's
// %rbp, presume that the callee left it unchanged.
frame->context.rbp = last_frame->context.rbp;
frame->context_validity |= StackFrameAMD64::CONTEXT_VALID_RBP;
}
}
return frame;
}

View File

@@ -182,7 +182,7 @@ TEST_F(GetCallerFrame, ScanWithoutSymbols) {
stack_section.start() = 0x8000000080000000ULL;
u_int64_t return_address1 = 0x50000000b0000100ULL;
u_int64_t return_address2 = 0x50000000b0000900ULL;
Label frame1_sp, frame2_sp;
Label frame1_sp, frame2_sp, frame1_rbp;
stack_section
// frame 0
.Append(16, 0) // space
@@ -198,6 +198,11 @@ TEST_F(GetCallerFrame, ScanWithoutSymbols) {
.D64(0x40000000b0000000ULL) // more junk
.D64(0x50000000d0000000ULL)
.Mark(&frame1_rbp)
.D64(stack_section.start()) // This is in the right place to be
// a saved rbp, but it's bogus, so
// we shouldn't report it.
.D64(return_address2) // actual return address
// frame 2
.Mark(&frame2_sp)
@@ -206,6 +211,7 @@ TEST_F(GetCallerFrame, ScanWithoutSymbols) {
RegionFromSection();
raw_context.rip = 0x40000000c0000200ULL;
raw_context.rbp = frame1_rbp.Value();
raw_context.rsp = stack_section.start().Value();
StackwalkerAMD64 walker(&system_info, &raw_context, &stack_region, &modules,
@@ -222,10 +228,12 @@ TEST_F(GetCallerFrame, ScanWithoutSymbols) {
StackFrameAMD64 *frame1 = static_cast<StackFrameAMD64 *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
ASSERT_EQ((StackFrameAMD64::CONTEXT_VALID_RIP |
StackFrameAMD64::CONTEXT_VALID_RSP),
StackFrameAMD64::CONTEXT_VALID_RSP |
StackFrameAMD64::CONTEXT_VALID_RBP),
frame1->context_validity);
EXPECT_EQ(return_address1, frame1->context.rip);
EXPECT_EQ(frame1_sp.Value(), frame1->context.rsp);
EXPECT_EQ(frame1_rbp.Value(), frame1->context.rbp);
StackFrameAMD64 *frame2 = static_cast<StackFrameAMD64 *>(frames->at(2));
EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame2->trust);
@@ -243,7 +251,7 @@ TEST_F(GetCallerFrame, ScanWithFunctionSymbols) {
// lies within a function's bounds.
stack_section.start() = 0x8000000080000000ULL;
u_int64_t return_address = 0x50000000b0000110ULL;
Label frame1_sp;
Label frame1_sp, frame1_rbp;
stack_section
// frame 0
@@ -258,10 +266,12 @@ TEST_F(GetCallerFrame, ScanWithFunctionSymbols) {
.D64(return_address) // actual return address
// frame 1
.Mark(&frame1_sp)
.Append(32, 0); // end of stack
.Append(32, 0) // end of stack
.Mark(&frame1_rbp);
RegionFromSection();
raw_context.rip = 0x40000000c0000200ULL;
raw_context.rbp = frame1_rbp.Value();
raw_context.rsp = stack_section.start().Value();
SetModuleSymbols(&module1,
@@ -286,14 +296,81 @@ TEST_F(GetCallerFrame, ScanWithFunctionSymbols) {
StackFrameAMD64 *frame1 = static_cast<StackFrameAMD64 *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
ASSERT_EQ((StackFrameAMD64::CONTEXT_VALID_RIP |
StackFrameAMD64::CONTEXT_VALID_RSP),
StackFrameAMD64::CONTEXT_VALID_RSP |
StackFrameAMD64::CONTEXT_VALID_RBP),
frame1->context_validity);
EXPECT_EQ(return_address, frame1->context.rip);
EXPECT_EQ(frame1_sp.Value(), frame1->context.rsp);
EXPECT_EQ(frame1_rbp.Value(), frame1->context.rbp);
EXPECT_EQ("echidna", frame1->function_name);
EXPECT_EQ(0x50000000b0000100ULL, frame1->function_base);
}
TEST_F(GetCallerFrame, CallerPushedRBP) {
// Functions typically push their %rbp upon entry and set %rbp pointing
// there. If stackwalking finds a plausible address for the next frame's
// %rbp directly below the return address, assume that it is indeed the
// next frame's %rbp.
stack_section.start() = 0x8000000080000000ULL;
u_int64_t return_address = 0x50000000b0000110ULL;
Label frame0_rbp, frame1_sp, frame1_rbp;
stack_section
// frame 0
.Append(16, 0) // space
.D64(0x40000000b0000000ULL) // junk that's not
.D64(0x50000000b0000000ULL) // a return address
.D64(0x40000000c0001000ULL) // a couple of plausible addresses
.D64(0x50000000b000aaaaULL) // that are not within functions
.Mark(&frame0_rbp)
.D64(frame1_rbp) // caller-pushed %rbp
.D64(return_address) // actual return address
// frame 1
.Mark(&frame1_sp)
.Append(32, 0) // body of frame1
.Mark(&frame1_rbp); // end of stack
RegionFromSection();
raw_context.rip = 0x40000000c0000200ULL;
raw_context.rbp = frame0_rbp.Value();
raw_context.rsp = stack_section.start().Value();
SetModuleSymbols(&module1,
// The youngest frame's function.
"FUNC 100 400 10 sasquatch\n");
SetModuleSymbols(&module2,
// The calling frame's function.
"FUNC 100 400 10 yeti\n");
StackwalkerAMD64 walker(&system_info, &raw_context, &stack_region, &modules,
&supplier, &resolver);
ASSERT_TRUE(walker.Walk(&call_stack));
frames = call_stack.frames();
ASSERT_EQ(2U, frames->size());
StackFrameAMD64 *frame0 = static_cast<StackFrameAMD64 *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
ASSERT_EQ(StackFrameAMD64::CONTEXT_VALID_ALL, frame0->context_validity);
EXPECT_EQ(frame0_rbp.Value(), frame0->context.rbp);
EXPECT_EQ("sasquatch", frame0->function_name);
EXPECT_EQ(0x40000000c0000100ULL, frame0->function_base);
StackFrameAMD64 *frame1 = static_cast<StackFrameAMD64 *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
ASSERT_EQ((StackFrameAMD64::CONTEXT_VALID_RIP |
StackFrameAMD64::CONTEXT_VALID_RSP |
StackFrameAMD64::CONTEXT_VALID_RBP),
frame1->context_validity);
EXPECT_EQ(return_address, frame1->context.rip);
EXPECT_EQ(frame1_sp.Value(), frame1->context.rsp);
EXPECT_EQ(frame1_rbp.Value(), frame1->context.rbp);
EXPECT_EQ("yeti", frame1->function_name);
EXPECT_EQ(0x50000000b0000100ULL, frame1->function_base);
}
struct CFIFixture: public StackwalkerAMD64Fixture {
CFIFixture() {
// Provide a bunch of STACK CFI records; we'll walk to the caller

View File

@@ -33,7 +33,6 @@
//
// Author: Mark Mentovai, Ted Mielczarek, Jim Blandy
#include "google_breakpad/processor/call_stack.h"
#include "google_breakpad/processor/memory_region.h"
#include "google_breakpad/processor/source_line_resolver_interface.h"
@@ -48,12 +47,13 @@ namespace google_breakpad {
StackwalkerARM::StackwalkerARM(const SystemInfo *system_info,
const MDRawContextARM *context,
int fp_register,
MemoryRegion *memory,
const CodeModules *modules,
SymbolSupplier *supplier,
SourceLineResolverInterface *resolver)
: Stackwalker(system_info, memory, modules, supplier, resolver),
context_(context),
context_(context), fp_register_(fp_register),
context_frame_validity_(StackFrameARM::CONTEXT_VALID_ALL) { }
@@ -70,7 +70,7 @@ StackFrame* StackwalkerARM::GetContextFrame() {
frame->context = *context_;
frame->context_validity = context_frame_validity_;
frame->trust = StackFrame::FRAME_TRUST_CONTEXT;
frame->instruction = frame->context.iregs[15];
frame->instruction = frame->context.iregs[MD_CONTEXT_ARM_REG_PC];
return frame;
}
@@ -125,8 +125,18 @@ StackFrameARM *StackwalkerARM::GetCallerByCFIFrameInfo(
CFIFrameInfo::RegisterValueMap<u_int32_t>::iterator entry =
caller_registers.find(".ra");
if (entry != caller_registers.end()) {
frame->context_validity |= StackFrameARM::CONTEXT_VALID_PC;
frame->context.iregs[MD_CONTEXT_ARM_REG_PC] = entry->second;
if (fp_register_ == -1) {
frame->context_validity |= StackFrameARM::CONTEXT_VALID_PC;
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.
@@ -154,7 +164,7 @@ StackFrameARM *StackwalkerARM::GetCallerByStackScan(
StackFrameARM *last_frame = static_cast<StackFrameARM *>(frames.back());
u_int32_t last_sp = last_frame->context.iregs[MD_CONTEXT_ARM_REG_SP];
u_int32_t caller_sp, caller_pc;
if (!ScanForReturnAddress(last_sp, &caller_sp, &caller_pc)) {
// No plausible return address was found.
return NULL;
@@ -179,6 +189,52 @@ StackFrameARM *StackwalkerARM::GetCallerByStackScan(
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) {
if (!memory_ || !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()));
// If CFI failed, or there wasn't CFI available, fall back
// to stack scanning.
if (!frame.get()) {
// to frame pointer, if this is configured.
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));
}
// If nothing worked, tell the caller.
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
// require the exact return address value may access
// 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();
}

View File

@@ -40,7 +40,6 @@
#ifndef PROCESSOR_STACKWALKER_ARM_H__
#define PROCESSOR_STACKWALKER_ARM_H__
#include "google_breakpad/common/breakpad_types.h"
#include "google_breakpad/common/minidump_format.h"
#include "google_breakpad/processor/stackwalker.h"
@@ -57,6 +56,7 @@ class StackwalkerARM : public Stackwalker {
// to the base Stackwalker constructor.
StackwalkerARM(const SystemInfo *system_info,
const MDRawContextARM *context,
int fp_register,
MemoryRegion *memory,
const CodeModules *modules,
SymbolSupplier *supplier,
@@ -78,14 +78,22 @@ class StackwalkerARM : public Stackwalker {
StackFrameARM *GetCallerByCFIFrameInfo(const vector<StackFrame *> &frames,
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
// of the returned frame. Return NULL on failure.
// of the returned frame. Return NULL on failure.
StackFrameARM *GetCallerByStackScan(const vector<StackFrame *> &frames);
// Stores the CPU context corresponding to the youngest stack frame, to
// be returned by GetContextFrame.
const MDRawContextARM *context_;
// The register to use a as frame pointer. The value is -1 if frame pointer
// cannot be used.
int fp_register_;
// Validity mask for youngest stack frame. This is always
// CONTEXT_VALID_ALL in real use; it is only changeable for the sake of
// unit tests.

View File

@@ -116,7 +116,7 @@ class StackwalkerARMFixture {
for (size_t i = 0; i < sizeof(*raw_context); i++)
reinterpret_cast<u_int8_t *>(raw_context)[i] = (x += 17);
}
SystemInfo system_info;
MDRawContextARM raw_context;
Section stack_section;
@@ -136,7 +136,7 @@ TEST_F(SanityCheck, NoResolver) {
// Since we have no call frame information, and all unwinding
// requires call frame information, the stack walk will end after
// the first frame.
StackwalkerARM walker(&system_info, &raw_context, &stack_region, &modules,
StackwalkerARM walker(&system_info, &raw_context, -1, &stack_region, &modules,
NULL, NULL);
// This should succeed even without a resolver or supplier.
ASSERT_TRUE(walker.Walk(&call_stack));
@@ -154,7 +154,7 @@ TEST_F(GetContextFrame, Simple) {
// Since we have no call frame information, and all unwinding
// requires call frame information, the stack walk will end after
// the first frame.
StackwalkerARM walker(&system_info, &raw_context, &stack_region, &modules,
StackwalkerARM walker(&system_info, &raw_context, -1, &stack_region, &modules,
&supplier, &resolver);
ASSERT_TRUE(walker.Walk(&call_stack));
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_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);
ASSERT_TRUE(walker.Walk(&call_stack));
frames = call_stack.frames();
@@ -264,7 +264,7 @@ TEST_F(GetCallerFrame, ScanWithFunctionSymbols) {
// The calling frame's function.
"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);
ASSERT_TRUE(walker.Walk(&call_stack));
frames = call_stack.frames();
@@ -374,8 +374,8 @@ struct CFIFixture: public StackwalkerARMFixture {
RegionFromSection();
raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = stack_section.start().Value();
StackwalkerARM walker(&system_info, &raw_context, &stack_region, &modules,
&supplier, &resolver);
StackwalkerARM walker(&system_info, &raw_context, -1, &stack_region,
&modules, &supplier, &resolver);
walker.SetContextFrameValidity(context_frame_validity);
ASSERT_TRUE(walker.Walk(&call_stack));
frames = call_stack.frames();
@@ -417,7 +417,7 @@ struct CFIFixture: public StackwalkerARMFixture {
EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM_REG_PC],
frame1->context.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);
}
@@ -564,9 +564,9 @@ TEST_F(CFI, At4006) {
// move in the wrong direction.
TEST_F(CFI, RejectBackwards) {
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;
StackwalkerARM walker(&system_info, &raw_context, &stack_region, &modules,
StackwalkerARM walker(&system_info, &raw_context, -1, &stack_region, &modules,
&supplier, &resolver);
ASSERT_TRUE(walker.Walk(&call_stack));
frames = call_stack.frames();
@@ -576,11 +576,189 @@ TEST_F(CFI, RejectBackwards) {
// Check that we reject rules whose expressions' evaluation fails.
TEST_F(CFI, RejectBadExpressions) {
raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40007000;
raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = 0x80000000;
StackwalkerARM walker(&system_info, &raw_context, &stack_region, &modules,
raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = 0x80000000;
StackwalkerARM walker(&system_info, &raw_context, -1, &stack_region, &modules,
&supplier, &resolver);
ASSERT_TRUE(walker.Walk(&call_stack));
frames = call_stack.frames();
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]);
}

View File

@@ -203,10 +203,37 @@ StackFrameX86 *StackwalkerX86::GetCallerByWindowsFrameInfo(
dictionary[".cbCalleeParams"] = last_frame_callee_parameter_size;
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;
u_int32_t raSearchStart = last_frame->context.esp +
last_frame_callee_parameter_size +
last_frame_info->local_size +
last_frame_info->saved_register_size;
u_int32_t raSearchStartOld = raSearchStart;
u_int32_t found = 0; // dummy value
// Scan up to three words above the calculated search value, in case
// the stack was aligned to a quadword boundary.
if (ScanForReturnAddress(raSearchStart, &raSearchStart, &found, 3) &&
last_frame->trust == StackFrame::FRAME_TRUST_CONTEXT &&
last_frame->windows_frame_info != NULL &&
last_frame_info->type_ == WindowsFrameInfo::STACK_INFO_FPO &&
raSearchStartOld == raSearchStart &&
found == last_frame->context.eip) {
// The context frame represents an FPO-optimized Windows system call.
// On the top of the stack we have a pointer to the current instruction.
// This means that the callee has returned but the return address is still
// on the top of the stack which is very atypical situaltion.
// Skip one slot from the stack and do another scan in order to get the
// actual return address.
raSearchStart += 4;
ScanForReturnAddress(raSearchStart, &raSearchStart, &found, 3);
}
// The difference between raSearch and raSearchStart is unknown,
// but making them the same seems to work well in practice.
dictionary[".raSearchStart"] = raSearchStart;
dictionary[".raSearch"] = raSearchStart;
dictionary[".cbParams"] = last_frame_info->parameter_size;
// Decide what type of program string to use. The program string is in

View File

@@ -69,7 +69,11 @@ class StackwalkerX86Fixture {
// Give the two modules reasonable standard locations and names
// for tests to play with.
module1(0x40000000, 0x10000, "module1", "version1"),
module2(0x50000000, 0x10000, "module2", "version2") {
module2(0x50000000, 0x10000, "module2", "version2"),
module3(0x771d0000, 0x180000, "module3", "version3"),
module4(0x75f90000, 0x46000, "module4", "version4"),
module5(0x75730000, 0x110000, "module5", "version5"),
module6(0x647f0000, 0x1ba8000, "module6", "version6") {
// Identify the system as a Linux system.
system_info.os = "Linux";
system_info.os_short = "linux";
@@ -83,6 +87,10 @@ class StackwalkerX86Fixture {
// Create some modules with some stock debugging information.
modules.Add(&module1);
modules.Add(&module2);
modules.Add(&module3);
modules.Add(&module4);
modules.Add(&module5);
modules.Add(&module6);
// By default, none of the modules have symbol info; call
// SetModuleSymbols to override this.
@@ -122,6 +130,10 @@ class StackwalkerX86Fixture {
MockMemoryRegion stack_region;
MockCodeModule module1;
MockCodeModule module2;
MockCodeModule module3;
MockCodeModule module4;
MockCodeModule module5;
MockCodeModule module6;
MockCodeModules modules;
MockSymbolSupplier supplier;
BasicSourceLineResolver resolver;
@@ -196,24 +208,28 @@ TEST_F(GetCallerFrame, Traditional) {
frames = call_stack.frames();
ASSERT_EQ(2U, frames->size());
StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
EXPECT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
EXPECT_EQ(0x4000c7a5U, frame0->instruction);
EXPECT_EQ(0x4000c7a5U, frame0->context.eip);
EXPECT_EQ(frame0_ebp.Value(), frame0->context.ebp);
EXPECT_EQ(NULL, frame0->windows_frame_info);
{ // To avoid reusing locals by mistake
StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
EXPECT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
EXPECT_EQ(0x4000c7a5U, frame0->instruction);
EXPECT_EQ(0x4000c7a5U, frame0->context.eip);
EXPECT_EQ(frame0_ebp.Value(), frame0->context.ebp);
EXPECT_EQ(NULL, frame0->windows_frame_info);
}
StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_FP, frame1->trust);
ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
| StackFrameX86::CONTEXT_VALID_ESP
| StackFrameX86::CONTEXT_VALID_EBP),
frame1->context_validity);
EXPECT_EQ(0x40008679U, frame1->instruction + 1);
EXPECT_EQ(0x40008679U, frame1->context.eip);
EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp);
EXPECT_EQ(NULL, frame1->windows_frame_info);
{ // To avoid reusing locals by mistake
StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_FP, frame1->trust);
ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
| StackFrameX86::CONTEXT_VALID_ESP
| StackFrameX86::CONTEXT_VALID_EBP),
frame1->context_validity);
EXPECT_EQ(0x40008679U, frame1->instruction + 1);
EXPECT_EQ(0x40008679U, frame1->context.eip);
EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp);
EXPECT_EQ(NULL, frame1->windows_frame_info);
}
}
// Walk a traditional frame, but use a bogus %ebp value, forcing a scan
@@ -247,30 +263,34 @@ TEST_F(GetCallerFrame, TraditionalScan) {
frames = call_stack.frames();
ASSERT_EQ(2U, frames->size());
StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
EXPECT_EQ(0x4000f49dU, frame0->instruction);
EXPECT_EQ(0x4000f49dU, frame0->context.eip);
EXPECT_EQ(stack_section.start().Value(), frame0->context.esp);
EXPECT_EQ(0xd43eed6eU, frame0->context.ebp);
EXPECT_EQ(NULL, frame0->windows_frame_info);
{ // To avoid reusing locals by mistake
StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
EXPECT_EQ(0x4000f49dU, frame0->instruction);
EXPECT_EQ(0x4000f49dU, frame0->context.eip);
EXPECT_EQ(stack_section.start().Value(), frame0->context.esp);
EXPECT_EQ(0xd43eed6eU, frame0->context.ebp);
EXPECT_EQ(NULL, frame0->windows_frame_info);
}
StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
// I'd argue that CONTEXT_VALID_EBP shouldn't be here, since the
// walker does not actually fetch the EBP after a scan (forcing the
// next frame to be scanned as well). But let's grandfather the existing
// behavior in for now.
ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
| StackFrameX86::CONTEXT_VALID_ESP
| StackFrameX86::CONTEXT_VALID_EBP),
frame1->context_validity);
EXPECT_EQ(0x4000129dU, frame1->instruction + 1);
EXPECT_EQ(0x4000129dU, frame1->context.eip);
EXPECT_EQ(0x80000014U, frame1->context.esp);
EXPECT_EQ(0xd43eed6eU, frame1->context.ebp);
EXPECT_EQ(NULL, frame1->windows_frame_info);
{ // To avoid reusing locals by mistake
StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
// I'd argue that CONTEXT_VALID_EBP shouldn't be here, since the
// walker does not actually fetch the EBP after a scan (forcing the
// next frame to be scanned as well). But let's grandfather the existing
// behavior in for now.
ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
| StackFrameX86::CONTEXT_VALID_ESP
| StackFrameX86::CONTEXT_VALID_EBP),
frame1->context_validity);
EXPECT_EQ(0x4000129dU, frame1->instruction + 1);
EXPECT_EQ(0x4000129dU, frame1->context.eip);
EXPECT_EQ(0x80000014U, frame1->context.esp);
EXPECT_EQ(0xd43eed6eU, frame1->context.ebp);
EXPECT_EQ(NULL, frame1->windows_frame_info);
}
}
// Force scanning for a return address a long way down the stack
@@ -304,30 +324,34 @@ TEST_F(GetCallerFrame, TraditionalScanLongWay) {
frames = call_stack.frames();
ASSERT_EQ(2U, frames->size());
StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
EXPECT_EQ(0x4000f49dU, frame0->instruction);
EXPECT_EQ(0x4000f49dU, frame0->context.eip);
EXPECT_EQ(stack_section.start().Value(), frame0->context.esp);
EXPECT_EQ(0xd43eed6eU, frame0->context.ebp);
EXPECT_EQ(NULL, frame0->windows_frame_info);
{ // To avoid reusing locals by mistake
StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
EXPECT_EQ(0x4000f49dU, frame0->instruction);
EXPECT_EQ(0x4000f49dU, frame0->context.eip);
EXPECT_EQ(stack_section.start().Value(), frame0->context.esp);
EXPECT_EQ(0xd43eed6eU, frame0->context.ebp);
EXPECT_EQ(NULL, frame0->windows_frame_info);
}
StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
// I'd argue that CONTEXT_VALID_EBP shouldn't be here, since the
// walker does not actually fetch the EBP after a scan (forcing the
// next frame to be scanned as well). But let's grandfather the existing
// behavior in for now.
ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
| StackFrameX86::CONTEXT_VALID_ESP
| StackFrameX86::CONTEXT_VALID_EBP),
frame1->context_validity);
EXPECT_EQ(0x4000129dU, frame1->instruction + 1);
EXPECT_EQ(0x4000129dU, frame1->context.eip);
EXPECT_EQ(0x80000064U, frame1->context.esp);
EXPECT_EQ(0xd43eed6eU, frame1->context.ebp);
EXPECT_EQ(NULL, frame1->windows_frame_info);
{ // To avoid reusing locals by mistake
StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
// I'd argue that CONTEXT_VALID_EBP shouldn't be here, since the
// walker does not actually fetch the EBP after a scan (forcing the
// next frame to be scanned as well). But let's grandfather the existing
// behavior in for now.
ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
| StackFrameX86::CONTEXT_VALID_ESP
| StackFrameX86::CONTEXT_VALID_EBP),
frame1->context_validity);
EXPECT_EQ(0x4000129dU, frame1->instruction + 1);
EXPECT_EQ(0x4000129dU, frame1->context.eip);
EXPECT_EQ(0x80000064U, frame1->context.esp);
EXPECT_EQ(0xd43eed6eU, frame1->context.ebp);
EXPECT_EQ(NULL, frame1->windows_frame_info);
}
}
// Use Windows frame data (a "STACK WIN 4" record, from a
@@ -371,32 +395,101 @@ TEST_F(GetCallerFrame, WindowsFrameData) {
frames = call_stack.frames();
ASSERT_EQ(2U, frames->size());
StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
EXPECT_EQ(0x4000aa85U, frame0->instruction);
EXPECT_EQ(0x4000aa85U, frame0->context.eip);
EXPECT_EQ(stack_section.start().Value(), frame0->context.esp);
EXPECT_EQ(0xf052c1deU, frame0->context.ebp);
EXPECT_TRUE(frame0->windows_frame_info != NULL);
{ // To avoid reusing locals by mistake
StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
EXPECT_EQ(0x4000aa85U, frame0->instruction);
EXPECT_EQ(0x4000aa85U, frame0->context.eip);
EXPECT_EQ(stack_section.start().Value(), frame0->context.esp);
EXPECT_EQ(0xf052c1deU, frame0->context.ebp);
EXPECT_TRUE(frame0->windows_frame_info != NULL);
}
StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust);
ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
| StackFrameX86::CONTEXT_VALID_ESP
| StackFrameX86::CONTEXT_VALID_EBP
| StackFrameX86::CONTEXT_VALID_EBX
| StackFrameX86::CONTEXT_VALID_ESI
| StackFrameX86::CONTEXT_VALID_EDI),
frame1->context_validity);
EXPECT_EQ(0x40001350U, frame1->instruction + 1);
EXPECT_EQ(0x40001350U, frame1->context.eip);
EXPECT_EQ(frame1_esp.Value(), frame1->context.esp);
EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp);
EXPECT_EQ(0x9068a878U, frame1->context.ebx);
EXPECT_EQ(0xa7120d1aU, frame1->context.esi);
EXPECT_EQ(0x630891beU, frame1->context.edi);
EXPECT_EQ(NULL, frame1->windows_frame_info);
{ // To avoid reusing locals by mistake
StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust);
ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
| StackFrameX86::CONTEXT_VALID_ESP
| StackFrameX86::CONTEXT_VALID_EBP
| StackFrameX86::CONTEXT_VALID_EBX
| StackFrameX86::CONTEXT_VALID_ESI
| StackFrameX86::CONTEXT_VALID_EDI),
frame1->context_validity);
EXPECT_EQ(0x40001350U, frame1->instruction + 1);
EXPECT_EQ(0x40001350U, frame1->context.eip);
EXPECT_EQ(frame1_esp.Value(), frame1->context.esp);
EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp);
EXPECT_EQ(0x9068a878U, frame1->context.ebx);
EXPECT_EQ(0xa7120d1aU, frame1->context.esi);
EXPECT_EQ(0x630891beU, frame1->context.edi);
EXPECT_EQ(NULL, frame1->windows_frame_info);
}
}
// Use Windows frame data (a "STACK WIN 4" record, from a
// FrameTypeFrameData DIA record) to walk a stack frame where the stack
// is aligned and we must search
TEST_F(GetCallerFrame, WindowsFrameDataAligned) {
SetModuleSymbols(&module1,
"STACK WIN 4 aa85 176 0 0 4 4 8 0 1"
" $T1 .raSearch ="
" $T0 $T1 4 - 8 @ ="
" $ebp $T1 4 - ^ ="
" $eip $T1 ^ ="
" $esp $T1 4 + =");
Label frame1_esp, frame1_ebp;
stack_section.start() = 0x80000000;
stack_section
// frame 0
.D32(0x0ffa0ffa) // unused saved register
.D32(0xdeaddead) // locals
.D32(0xbeefbeef)
.D32(0) // 8-byte alignment
.D32(frame1_ebp)
.D32(0x5000129d) // return address
// frame 1
.Mark(&frame1_esp)
.D32(0x1) // parameter
.Mark(&frame1_ebp)
.D32(0) // saved %ebp (stack end)
.D32(0); // saved %eip (stack end)
RegionFromSection();
raw_context.eip = 0x4000aa85;
raw_context.esp = stack_section.start().Value();
raw_context.ebp = 0xf052c1de; // should not be needed to walk frame
StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules,
&supplier, &resolver);
ASSERT_TRUE(walker.Walk(&call_stack));
frames = call_stack.frames();
ASSERT_EQ(2U, frames->size());
{ // To avoid reusing locals by mistake
StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
EXPECT_EQ(0x4000aa85U, frame0->instruction);
EXPECT_EQ(0x4000aa85U, frame0->context.eip);
EXPECT_EQ(stack_section.start().Value(), frame0->context.esp);
EXPECT_EQ(0xf052c1deU, frame0->context.ebp);
EXPECT_TRUE(frame0->windows_frame_info != NULL);
}
{ // To avoid reusing locals by mistake
StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust);
ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
| StackFrameX86::CONTEXT_VALID_ESP
| StackFrameX86::CONTEXT_VALID_EBP),
frame1->context_validity);
EXPECT_EQ(0x5000129dU, frame1->instruction + 1);
EXPECT_EQ(0x5000129dU, frame1->context.eip);
EXPECT_EQ(frame1_esp.Value(), frame1->context.esp);
EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp);
EXPECT_EQ(NULL, frame1->windows_frame_info);
}
}
// Use Windows frame data (a "STACK WIN 4" record, from a
@@ -455,56 +548,66 @@ TEST_F(GetCallerFrame, WindowsFrameDataParameterSize) {
frames = call_stack.frames();
ASSERT_EQ(3U, frames->size());
StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
EXPECT_EQ(0x40001004U, frame0->instruction);
EXPECT_EQ(0x40001004U, frame0->context.eip);
EXPECT_EQ(frame0_esp.Value(), frame0->context.esp);
EXPECT_EQ(frame0_ebp.Value(), frame0->context.ebp);
EXPECT_EQ(&module1, frame0->module);
EXPECT_EQ("module1::wheedle", frame0->function_name);
EXPECT_EQ(0x40001000U, frame0->function_base);
// The FUNC record for module1::wheedle should have produced a
// WindowsFrameInfo structure with only the parameter size valid.
ASSERT_TRUE(frame0->windows_frame_info != NULL);
EXPECT_EQ(WindowsFrameInfo::VALID_PARAMETER_SIZE,
frame0->windows_frame_info->valid);
EXPECT_EQ(12U, frame0->windows_frame_info->parameter_size);
{ // To avoid reusing locals by mistake
StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
EXPECT_EQ(0x40001004U, frame0->instruction);
EXPECT_EQ(0x40001004U, frame0->context.eip);
EXPECT_EQ(frame0_esp.Value(), frame0->context.esp);
EXPECT_EQ(frame0_ebp.Value(), frame0->context.ebp);
EXPECT_EQ(&module1, frame0->module);
EXPECT_EQ("module1::wheedle", frame0->function_name);
EXPECT_EQ(0x40001000U, frame0->function_base);
// The FUNC record for module1::wheedle should have produced a
// WindowsFrameInfo structure with only the parameter size valid.
ASSERT_TRUE(frame0->windows_frame_info != NULL);
EXPECT_EQ(WindowsFrameInfo::VALID_PARAMETER_SIZE,
frame0->windows_frame_info->valid);
EXPECT_EQ(WindowsFrameInfo::STACK_INFO_UNKNOWN,
frame0->windows_frame_info->type_);
EXPECT_EQ(12U, frame0->windows_frame_info->parameter_size);
}
StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_FP, frame1->trust);
ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
| StackFrameX86::CONTEXT_VALID_ESP
| StackFrameX86::CONTEXT_VALID_EBP),
frame1->context_validity);
EXPECT_EQ(0x5000aa95U, frame1->instruction + 1);
EXPECT_EQ(0x5000aa95U, frame1->context.eip);
EXPECT_EQ(frame1_esp.Value(), frame1->context.esp);
EXPECT_EQ(0x6fa902e0U, frame1->context.ebp);
EXPECT_EQ(&module2, frame1->module);
EXPECT_EQ("module2::whine", frame1->function_name);
EXPECT_EQ(0x5000aa85U, frame1->function_base);
ASSERT_TRUE(frame1->windows_frame_info != NULL);
EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame1->windows_frame_info->valid);
// This should not see the 0xbeef parameter size from the FUNC
// record, but should instead see the STACK WIN record.
EXPECT_EQ(4U, frame1->windows_frame_info->parameter_size);
{ // To avoid reusing locals by mistake
StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_FP, frame1->trust);
ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
| StackFrameX86::CONTEXT_VALID_ESP
| StackFrameX86::CONTEXT_VALID_EBP),
frame1->context_validity);
EXPECT_EQ(0x5000aa95U, frame1->instruction + 1);
EXPECT_EQ(0x5000aa95U, frame1->context.eip);
EXPECT_EQ(frame1_esp.Value(), frame1->context.esp);
EXPECT_EQ(0x6fa902e0U, frame1->context.ebp);
EXPECT_EQ(&module2, frame1->module);
EXPECT_EQ("module2::whine", frame1->function_name);
EXPECT_EQ(0x5000aa85U, frame1->function_base);
ASSERT_TRUE(frame1->windows_frame_info != NULL);
EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame1->windows_frame_info->valid);
EXPECT_EQ(WindowsFrameInfo::STACK_INFO_FRAME_DATA,
frame1->windows_frame_info->type_);
// This should not see the 0xbeef parameter size from the FUNC
// record, but should instead see the STACK WIN record.
EXPECT_EQ(4U, frame1->windows_frame_info->parameter_size);
}
StackFrameX86 *frame2 = static_cast<StackFrameX86 *>(frames->at(2));
EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame2->trust);
ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
| StackFrameX86::CONTEXT_VALID_ESP
| StackFrameX86::CONTEXT_VALID_EBP
| StackFrameX86::CONTEXT_VALID_EBX),
frame2->context_validity);
EXPECT_EQ(0x2a179e38U, frame2->instruction + 1);
EXPECT_EQ(0x2a179e38U, frame2->context.eip);
EXPECT_EQ(frame2_esp.Value(), frame2->context.esp);
EXPECT_EQ(frame2_ebp.Value(), frame2->context.ebp);
EXPECT_EQ(0x2558c7f3U, frame2->context.ebx);
EXPECT_EQ(NULL, frame2->module);
EXPECT_EQ(NULL, frame2->windows_frame_info);
{ // To avoid reusing locals by mistake
StackFrameX86 *frame2 = static_cast<StackFrameX86 *>(frames->at(2));
EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame2->trust);
ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
| StackFrameX86::CONTEXT_VALID_ESP
| StackFrameX86::CONTEXT_VALID_EBP
| StackFrameX86::CONTEXT_VALID_EBX),
frame2->context_validity);
EXPECT_EQ(0x2a179e38U, frame2->instruction + 1);
EXPECT_EQ(0x2a179e38U, frame2->context.eip);
EXPECT_EQ(frame2_esp.Value(), frame2->context.esp);
EXPECT_EQ(frame2_ebp.Value(), frame2->context.ebp);
EXPECT_EQ(0x2558c7f3U, frame2->context.ebx);
EXPECT_EQ(NULL, frame2->module);
EXPECT_EQ(NULL, frame2->windows_frame_info);
}
}
// Use Windows frame data (a "STACK WIN 4" record, from a
@@ -539,29 +642,33 @@ TEST_F(GetCallerFrame, WindowsFrameDataScan) {
frames = call_stack.frames();
ASSERT_EQ(2U, frames->size());
StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
EXPECT_EQ(0x40000c9cU, frame0->instruction);
EXPECT_EQ(0x40000c9cU, frame0->context.eip);
EXPECT_EQ(stack_section.start().Value(), frame0->context.esp);
EXPECT_EQ(0x2ae314cdU, frame0->context.ebp);
EXPECT_TRUE(frame0->windows_frame_info != NULL);
{ // To avoid reusing locals by mistake
StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
EXPECT_EQ(0x40000c9cU, frame0->instruction);
EXPECT_EQ(0x40000c9cU, frame0->context.eip);
EXPECT_EQ(stack_section.start().Value(), frame0->context.esp);
EXPECT_EQ(0x2ae314cdU, frame0->context.ebp);
EXPECT_TRUE(frame0->windows_frame_info != NULL);
}
StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
// I'd argue that CONTEXT_VALID_EBP shouldn't be here, since the walker
// does not actually fetch the EBP after a scan (forcing the next frame
// to be scanned as well). But let's grandfather the existing behavior in
// for now.
ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
| StackFrameX86::CONTEXT_VALID_ESP
| StackFrameX86::CONTEXT_VALID_EBP),
frame1->context_validity);
EXPECT_EQ(0x50007ce9U, frame1->instruction + 1);
EXPECT_EQ(0x50007ce9U, frame1->context.eip);
EXPECT_EQ(frame1_esp.Value(), frame1->context.esp);
EXPECT_TRUE(frame1->windows_frame_info != NULL);
{ // To avoid reusing locals by mistake
StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
// I'd argue that CONTEXT_VALID_EBP shouldn't be here, since the walker
// does not actually fetch the EBP after a scan (forcing the next frame
// to be scanned as well). But let's grandfather the existing behavior in
// for now.
ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
| StackFrameX86::CONTEXT_VALID_ESP
| StackFrameX86::CONTEXT_VALID_EBP),
frame1->context_validity);
EXPECT_EQ(0x50007ce9U, frame1->instruction + 1);
EXPECT_EQ(0x50007ce9U, frame1->context.eip);
EXPECT_EQ(frame1_esp.Value(), frame1->context.esp);
EXPECT_TRUE(frame1->windows_frame_info != NULL);
}
}
// Use Windows frame data (a "STACK WIN 4" record, from a
@@ -618,30 +725,34 @@ TEST_F(GetCallerFrame, WindowsFrameDataBadEIPScan) {
frames = call_stack.frames();
ASSERT_EQ(2U, frames->size());
StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
EXPECT_EQ(0x40000700U, frame0->instruction);
EXPECT_EQ(0x40000700U, frame0->context.eip);
EXPECT_EQ(stack_section.start().Value(), frame0->context.esp);
EXPECT_EQ(frame0_ebp.Value(), frame0->context.ebp);
EXPECT_TRUE(frame0->windows_frame_info != NULL);
{ // To avoid reusing locals by mistake
StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
EXPECT_EQ(0x40000700U, frame0->instruction);
EXPECT_EQ(0x40000700U, frame0->context.eip);
EXPECT_EQ(stack_section.start().Value(), frame0->context.esp);
EXPECT_EQ(frame0_ebp.Value(), frame0->context.ebp);
EXPECT_TRUE(frame0->windows_frame_info != NULL);
}
StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_CFI_SCAN, frame1->trust);
// I'd argue that CONTEXT_VALID_EBP shouldn't be here, since the
// walker does not actually fetch the EBP after a scan (forcing the
// next frame to be scanned as well). But let's grandfather the existing
// behavior in for now.
ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
| StackFrameX86::CONTEXT_VALID_ESP
| StackFrameX86::CONTEXT_VALID_EBP),
frame1->context_validity);
EXPECT_EQ(0x5000d000U, frame1->instruction + 1);
EXPECT_EQ(0x5000d000U, frame1->context.eip);
EXPECT_EQ(frame1_esp.Value(), frame1->context.esp);
EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp);
EXPECT_TRUE(frame1->windows_frame_info != NULL);
{ // To avoid reusing locals by mistake
StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_CFI_SCAN, frame1->trust);
// I'd argue that CONTEXT_VALID_EBP shouldn't be here, since the
// walker does not actually fetch the EBP after a scan (forcing the
// next frame to be scanned as well). But let's grandfather the existing
// behavior in for now.
ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
| StackFrameX86::CONTEXT_VALID_ESP
| StackFrameX86::CONTEXT_VALID_EBP),
frame1->context_validity);
EXPECT_EQ(0x5000d000U, frame1->instruction + 1);
EXPECT_EQ(0x5000d000U, frame1->context.eip);
EXPECT_EQ(frame1_esp.Value(), frame1->context.esp);
EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp);
EXPECT_TRUE(frame1->windows_frame_info != NULL);
}
}
// Use Windows FrameTypeFPO data to walk a stack frame for a function that
@@ -681,35 +792,41 @@ TEST_F(GetCallerFrame, WindowsFPOUnchangedEBP) {
frames = call_stack.frames();
ASSERT_EQ(2U, frames->size());
StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
EXPECT_EQ(0x4000e8b8U, frame0->instruction);
EXPECT_EQ(0x4000e8b8U, frame0->context.eip);
EXPECT_EQ(frame0_esp.Value(), frame0->context.esp);
EXPECT_EQ(frame1_ebp.Value(), frame0->context.ebp); // unchanged from caller
EXPECT_EQ(&module1, frame0->module);
EXPECT_EQ("module1::discombobulated", frame0->function_name);
EXPECT_EQ(0x4000e8a8U, frame0->function_base);
// The STACK WIN record for module1::discombobulated should have
// produced a fully populated WindowsFrameInfo structure.
ASSERT_TRUE(frame0->windows_frame_info != NULL);
EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame0->windows_frame_info->valid);
EXPECT_EQ(0x10U, frame0->windows_frame_info->local_size);
{ // To avoid reusing locals by mistake
StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
EXPECT_EQ(0x4000e8b8U, frame0->instruction);
EXPECT_EQ(0x4000e8b8U, frame0->context.eip);
EXPECT_EQ(frame0_esp.Value(), frame0->context.esp);
EXPECT_EQ(frame1_ebp.Value(), frame0->context.ebp); // unchanged from caller
EXPECT_EQ(&module1, frame0->module);
EXPECT_EQ("module1::discombobulated", frame0->function_name);
EXPECT_EQ(0x4000e8a8U, frame0->function_base);
// The STACK WIN record for module1::discombobulated should have
// produced a fully populated WindowsFrameInfo structure.
ASSERT_TRUE(frame0->windows_frame_info != NULL);
EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame0->windows_frame_info->valid);
EXPECT_EQ(WindowsFrameInfo::STACK_INFO_FPO,
frame0->windows_frame_info->type_);
EXPECT_EQ(0x10U, frame0->windows_frame_info->local_size);
}
StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust);
ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
| StackFrameX86::CONTEXT_VALID_ESP
| StackFrameX86::CONTEXT_VALID_EBP),
frame1->context_validity);
EXPECT_EQ(0x40009b5bU, frame1->instruction + 1);
EXPECT_EQ(0x40009b5bU, frame1->context.eip);
EXPECT_EQ(frame1_esp.Value(), frame1->context.esp);
EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp);
EXPECT_EQ(&module1, frame1->module);
EXPECT_EQ("", frame1->function_name);
EXPECT_EQ(NULL, frame1->windows_frame_info);
{ // To avoid reusing locals by mistake
StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust);
ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
| StackFrameX86::CONTEXT_VALID_ESP
| StackFrameX86::CONTEXT_VALID_EBP),
frame1->context_validity);
EXPECT_EQ(0x40009b5bU, frame1->instruction + 1);
EXPECT_EQ(0x40009b5bU, frame1->context.eip);
EXPECT_EQ(frame1_esp.Value(), frame1->context.esp);
EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp);
EXPECT_EQ(&module1, frame1->module);
EXPECT_EQ("", frame1->function_name);
EXPECT_EQ(NULL, frame1->windows_frame_info);
}
}
// Use Windows FrameTypeFPO data to walk a stack frame for a function
@@ -751,36 +868,188 @@ TEST_F(GetCallerFrame, WindowsFPOUsedEBP) {
frames = call_stack.frames();
ASSERT_EQ(2U, frames->size());
StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
EXPECT_EQ(0x40009ab8U, frame0->instruction);
EXPECT_EQ(0x40009ab8U, frame0->context.eip);
EXPECT_EQ(frame0_esp.Value(), frame0->context.esp);
EXPECT_EQ(0xecbdd1a5, frame0->context.ebp);
EXPECT_EQ(&module1, frame0->module);
EXPECT_EQ("module1::RaisedByTheAliens", frame0->function_name);
EXPECT_EQ(0x40009aa8U, frame0->function_base);
// The STACK WIN record for module1::RaisedByTheAliens should have
// produced a fully populated WindowsFrameInfo structure.
ASSERT_TRUE(frame0->windows_frame_info != NULL);
EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame0->windows_frame_info->valid);
EXPECT_EQ("", frame0->windows_frame_info->program_string);
EXPECT_TRUE(frame0->windows_frame_info->allocates_base_pointer);
{ // To avoid reusing locals by mistake
StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
EXPECT_EQ(0x40009ab8U, frame0->instruction);
EXPECT_EQ(0x40009ab8U, frame0->context.eip);
EXPECT_EQ(frame0_esp.Value(), frame0->context.esp);
EXPECT_EQ(0xecbdd1a5, frame0->context.ebp);
EXPECT_EQ(&module1, frame0->module);
EXPECT_EQ("module1::RaisedByTheAliens", frame0->function_name);
EXPECT_EQ(0x40009aa8U, frame0->function_base);
// The STACK WIN record for module1::RaisedByTheAliens should have
// produced a fully populated WindowsFrameInfo structure.
ASSERT_TRUE(frame0->windows_frame_info != NULL);
EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame0->windows_frame_info->valid);
EXPECT_EQ(WindowsFrameInfo::STACK_INFO_FPO,
frame0->windows_frame_info->type_);
EXPECT_EQ("", frame0->windows_frame_info->program_string);
EXPECT_TRUE(frame0->windows_frame_info->allocates_base_pointer);
}
StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust);
ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
| StackFrameX86::CONTEXT_VALID_ESP
| StackFrameX86::CONTEXT_VALID_EBP),
frame1->context_validity);
EXPECT_EQ(0x4000debeU, frame1->instruction + 1);
EXPECT_EQ(0x4000debeU, frame1->context.eip);
EXPECT_EQ(frame1_esp.Value(), frame1->context.esp);
EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp);
EXPECT_EQ(&module1, frame1->module);
EXPECT_EQ("", frame1->function_name);
EXPECT_EQ(NULL, frame1->windows_frame_info);
{ // To avoid reusing locals by mistake
StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust);
ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
| StackFrameX86::CONTEXT_VALID_ESP
| StackFrameX86::CONTEXT_VALID_EBP),
frame1->context_validity);
EXPECT_EQ(0x4000debeU, frame1->instruction + 1);
EXPECT_EQ(0x4000debeU, frame1->context.eip);
EXPECT_EQ(frame1_esp.Value(), frame1->context.esp);
EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp);
EXPECT_EQ(&module1, frame1->module);
EXPECT_EQ("", frame1->function_name);
EXPECT_EQ(NULL, frame1->windows_frame_info);
}
}
// This is a regression unit test which covers a bug which has to do with
// FPO-optimized Windows system call stubs in the context frame. There is
// a more recent Windows system call dispatch mechanism which differs from
// the one which is being tested here. The newer system call dispatch
// mechanism creates an extra context frame (KiFastSystemCallRet).
TEST_F(GetCallerFrame, WindowsFPOSystemCall) {
SetModuleSymbols(&module3, // ntdll.dll
"PUBLIC 1f8ac c ZwWaitForSingleObject\n"
"STACK WIN 0 1f8ac 1b 0 0 c 0 0 0 0 0\n");
SetModuleSymbols(&module4, // kernelbase.dll
"PUBLIC 109f9 c WaitForSingleObjectEx\n"
"PUBLIC 36590 0 _except_handler4\n"
"STACK WIN 4 109f9 df c 0 c c 48 0 1 $T0 $ebp = $eip "
"$T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L "
"$T0 .cbSavedRegs - = $P $T0 8 + .cbParams + =\n"
"STACK WIN 4 36590 154 17 0 10 0 14 0 1 $T0 $ebp = $eip "
"$T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 "
".cbSavedRegs - = $P $T0 8 + .cbParams + =\n");
SetModuleSymbols(&module5, // kernel32.dll
"PUBLIC 11136 8 WaitForSingleObject\n"
"PUBLIC 11151 c WaitForSingleObjectExImplementation\n"
"STACK WIN 4 11136 16 5 0 8 0 0 0 1 $T0 $ebp = $eip "
"$T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L "
"$T0 .cbSavedRegs - = $P $T0 8 + .cbParams + =\n"
"STACK WIN 4 11151 7a 5 0 c 0 0 0 1 $T0 $ebp = $eip "
"$T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L "
"$T0 .cbSavedRegs - = $P $T0 8 + .cbParams + =\n");
SetModuleSymbols(&module6, // chrome.dll
"FILE 7038 some_file_name.h\n"
"FILE 839776 some_file_name.cc\n"
"FUNC 217fda 17 4 function_217fda\n"
"217fda 4 102 839776\n"
"FUNC 217ff1 a 4 function_217ff1\n"
"217ff1 0 594 7038\n"
"217ff1 a 596 7038\n"
"STACK WIN 0 217ff1 a 0 0 4 0 0 0 0 0\n");
Label frame0_esp, frame1_esp;
Label frame1_ebp, frame2_ebp, frame3_ebp;
stack_section.start() = 0x002ff290;
stack_section
.Mark(&frame0_esp)
.D32(0x771ef8c1) // EIP in frame 0 (system call)
.D32(0x75fa0a91) // return address of frame 0
.Mark(&frame1_esp)
.D32(0x000017b0) // args to child
.D32(0x00000000)
.D32(0x002ff2d8)
.D32(0x88014a2e)
.D32(0x002ff364)
.D32(0x000017b0)
.D32(0x00000000)
.D32(0x00000024)
.D32(0x00000001)
.D32(0x00000000)
.D32(0x00000000)
.D32(0x00000000)
.D32(0x00000000)
.D32(0x00000000)
.D32(0x00000000)
.D32(0x00000000)
.D32(0x9e3b9800)
.D32(0xfffffff7)
.D32(0x00000000)
.D32(0x002ff2a4)
.D32(0x64a07ff1) // random value to be confused with a return address
.D32(0x002ff8dc)
.D32(0x75fc6590) // random value to be confused with a return address
.D32(0xfdd2c6ea)
.D32(0x00000000)
.Mark(&frame1_ebp)
.D32(frame2_ebp) // Child EBP
.D32(0x75741194) // return address of frame 1
.D32(0x000017b0) // args to child
.D32(0x0036ee80)
.D32(0x00000000)
.D32(0x65bc7d14)
.Mark(&frame2_ebp)
.D32(frame3_ebp) // Child EBP
.D32(0x75741148) // return address of frame 2
.D32(0x000017b0) // args to child
.D32(0x0036ee80)
.D32(0x00000000)
.Mark(&frame3_ebp)
.D32(0) // saved %ebp (stack end)
.D32(0); // saved %eip (stack end)
RegionFromSection();
raw_context.eip = 0x771ef8c1; // in ntdll::ZwWaitForSingleObject
raw_context.esp = stack_section.start().Value();
ASSERT_TRUE(raw_context.esp == frame0_esp.Value());
raw_context.ebp = frame1_ebp.Value();
StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules,
&supplier, &resolver);
ASSERT_TRUE(walker.Walk(&call_stack));
frames = call_stack.frames();
ASSERT_EQ(4U, frames->size());
{ // To avoid reusing locals by mistake
StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
EXPECT_EQ(0x771ef8c1U, frame0->instruction);
EXPECT_EQ(0x771ef8c1U, frame0->context.eip);
EXPECT_EQ(frame0_esp.Value(), frame0->context.esp);
EXPECT_EQ(frame1_ebp.Value(), frame0->context.ebp);
EXPECT_EQ(&module3, frame0->module);
EXPECT_EQ("ZwWaitForSingleObject", frame0->function_name);
// The STACK WIN record for module3!ZwWaitForSingleObject should have
// produced a fully populated WindowsFrameInfo structure.
ASSERT_TRUE(frame0->windows_frame_info != NULL);
EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame0->windows_frame_info->valid);
EXPECT_EQ(WindowsFrameInfo::STACK_INFO_FPO,
frame0->windows_frame_info->type_);
EXPECT_EQ("", frame0->windows_frame_info->program_string);
EXPECT_FALSE(frame0->windows_frame_info->allocates_base_pointer);
}
{ // To avoid reusing locals by mistake
StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust);
ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
| StackFrameX86::CONTEXT_VALID_ESP
| StackFrameX86::CONTEXT_VALID_EBP),
frame1->context_validity);
EXPECT_EQ(0x75fa0a91U, frame1->instruction + 1);
EXPECT_EQ(0x75fa0a91U, frame1->context.eip);
EXPECT_EQ(frame1_esp.Value(), frame1->context.esp);
EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp);
EXPECT_EQ(&module4, frame1->module);
EXPECT_EQ("WaitForSingleObjectEx", frame1->function_name);
// The STACK WIN record for module4!WaitForSingleObjectEx should have
// produced a fully populated WindowsFrameInfo structure.
ASSERT_TRUE(frame1->windows_frame_info != NULL);
EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame1->windows_frame_info->valid);
EXPECT_EQ(WindowsFrameInfo::STACK_INFO_FRAME_DATA,
frame1->windows_frame_info->type_);
EXPECT_EQ("$T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L "
"$T0 .cbSavedRegs - = $P $T0 8 + .cbParams + =",
frame1->windows_frame_info->program_string);
EXPECT_FALSE(frame1->windows_frame_info->allocates_base_pointer);
}
}
struct CFIFixture: public StackwalkerX86Fixture {
@@ -836,32 +1105,36 @@ struct CFIFixture: public StackwalkerX86Fixture {
frames = call_stack.frames();
ASSERT_EQ(2U, frames->size());
StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
EXPECT_EQ("enchiridion", frame0->function_name);
EXPECT_EQ(0x40004000U, frame0->function_base);
ASSERT_TRUE(frame0->windows_frame_info != NULL);
ASSERT_EQ(WindowsFrameInfo::VALID_PARAMETER_SIZE,
frame0->windows_frame_info->valid);
ASSERT_TRUE(frame0->cfi_frame_info != NULL);
{ // To avoid reusing locals by mistake
StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
EXPECT_EQ("enchiridion", frame0->function_name);
EXPECT_EQ(0x40004000U, frame0->function_base);
ASSERT_TRUE(frame0->windows_frame_info != NULL);
ASSERT_EQ(WindowsFrameInfo::VALID_PARAMETER_SIZE,
frame0->windows_frame_info->valid);
ASSERT_TRUE(frame0->cfi_frame_info != NULL);
}
StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust);
ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP |
StackFrameX86::CONTEXT_VALID_ESP |
StackFrameX86::CONTEXT_VALID_EBP |
StackFrameX86::CONTEXT_VALID_EBX |
StackFrameX86::CONTEXT_VALID_ESI |
StackFrameX86::CONTEXT_VALID_EDI),
frame1->context_validity);
EXPECT_EQ(expected.eip, frame1->context.eip);
EXPECT_EQ(expected.esp, frame1->context.esp);
EXPECT_EQ(expected.ebp, frame1->context.ebp);
EXPECT_EQ(expected.ebx, frame1->context.ebx);
EXPECT_EQ(expected.esi, frame1->context.esi);
EXPECT_EQ(expected.edi, frame1->context.edi);
EXPECT_EQ("epictetus", frame1->function_name);
{ // To avoid reusing locals by mistake
StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust);
ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP |
StackFrameX86::CONTEXT_VALID_ESP |
StackFrameX86::CONTEXT_VALID_EBP |
StackFrameX86::CONTEXT_VALID_EBX |
StackFrameX86::CONTEXT_VALID_ESI |
StackFrameX86::CONTEXT_VALID_EDI),
frame1->context_validity);
EXPECT_EQ(expected.eip, frame1->context.eip);
EXPECT_EQ(expected.esp, frame1->context.esp);
EXPECT_EQ(expected.ebp, frame1->context.ebp);
EXPECT_EQ(expected.ebx, frame1->context.ebx);
EXPECT_EQ(expected.esi, frame1->context.esi);
EXPECT_EQ(expected.edi, frame1->context.edi);
EXPECT_EQ("epictetus", frame1->function_name);
}
}
// The values the stack walker should find for the caller's registers.

View File

@@ -150,7 +150,7 @@ TEST(ContextDeathTest, X86BadFlags) {
MDRawContextX86 raw;
raw.context_flags = 0;
ASSERT_DEATH(Context context(dump, raw);,
"context\\.context_flags & 0x[0-9a-f]+");
"context\\.context_flags & (0x[0-9a-f]+|MD_CONTEXT_X86)");
}
TEST(ContextDeathTest, X86BadEndianness) {

View File

@@ -72,7 +72,8 @@ struct WindowsFrameInfo {
STACK_INFO_UNKNOWN = -1
};
WindowsFrameInfo() : valid(VALID_NONE),
WindowsFrameInfo() : type_(STACK_INFO_UNKNOWN),
valid(VALID_NONE),
prolog_size(0),
epilog_size(0),
parameter_size(0),
@@ -82,7 +83,8 @@ struct WindowsFrameInfo {
allocates_base_pointer(0),
program_string() {}
WindowsFrameInfo(u_int32_t set_prolog_size,
WindowsFrameInfo(StackInfoTypes type,
u_int32_t set_prolog_size,
u_int32_t set_epilog_size,
u_int32_t set_parameter_size,
u_int32_t set_saved_register_size,
@@ -90,7 +92,8 @@ struct WindowsFrameInfo {
u_int32_t set_max_stack_size,
int set_allocates_base_pointer,
const std::string set_program_string)
: valid(VALID_ALL),
: type_(type),
valid(VALID_ALL),
prolog_size(set_prolog_size),
epilog_size(set_epilog_size),
parameter_size(set_parameter_size),
@@ -140,7 +143,8 @@ struct WindowsFrameInfo {
allocates_base_pointer = strtoul(tokens[10], NULL, 16);
}
return new WindowsFrameInfo(prolog_size,
return new WindowsFrameInfo(static_cast<StackInfoTypes>(type),
prolog_size,
epilog_size,
parameter_size,
saved_register_size,
@@ -152,6 +156,7 @@ struct WindowsFrameInfo {
// CopyFrom makes "this" WindowsFrameInfo object identical to "that".
void CopyFrom(const WindowsFrameInfo &that) {
type_ = that.type_;
valid = that.valid;
prolog_size = that.prolog_size;
epilog_size = that.epilog_size;
@@ -166,10 +171,13 @@ struct WindowsFrameInfo {
// Clears the WindowsFrameInfo object so that users will see it as though
// it contains no information.
void Clear() {
type_ = STACK_INFO_UNKNOWN;
valid = VALID_NONE;
program_string.erase();
}
StackInfoTypes type_;
// Identifies which fields in the structure are valid. This is of
// type Validity, but it is defined as an int because it's not
// possible to OR values into an enumerated type. Users must check