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:
@@ -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;
|
||||
|
3
thirdparty/breakpad/processor/binarystream.h
vendored
3
thirdparty/breakpad/processor/binarystream.h
vendored
@@ -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:
|
||||
|
@@ -48,6 +48,9 @@ DisassemblerX86::DisassemblerX86(const u_int8_t *bytecode,
|
||||
}
|
||||
|
||||
DisassemblerX86::~DisassemblerX86() {
|
||||
if (instr_valid_)
|
||||
libdis::x86_oplist_free(¤t_instr_);
|
||||
|
||||
libdis::x86_cleanup();
|
||||
}
|
||||
|
||||
|
@@ -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:
|
||||
|
@@ -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,
|
||||
|
@@ -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;
|
||||
|
7
thirdparty/breakpad/processor/minidump.cc
vendored
7
thirdparty/breakpad/processor/minidump.cc
vendored
@@ -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;
|
||||
|
@@ -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:
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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.
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
|
@@ -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()) {
|
||||
|
8
thirdparty/breakpad/processor/stackwalker.cc
vendored
8
thirdparty/breakpad/processor/stackwalker.cc
vendored
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
|
79
thirdparty/breakpad/processor/stackwalker_arm.cc
vendored
79
thirdparty/breakpad/processor/stackwalker_arm.cc
vendored
@@ -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();
|
||||
}
|
||||
|
12
thirdparty/breakpad/processor/stackwalker_arm.h
vendored
12
thirdparty/breakpad/processor/stackwalker_arm.h
vendored
@@ -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.
|
||||
|
@@ -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]);
|
||||
}
|
||||
|
35
thirdparty/breakpad/processor/stackwalker_x86.cc
vendored
35
thirdparty/breakpad/processor/stackwalker_x86.cc
vendored
@@ -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
|
||||
|
@@ -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.
|
||||
|
@@ -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) {
|
||||
|
@@ -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
|
||||
|
Reference in New Issue
Block a user