summaryrefslogtreecommitdiffstats
path: root/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86_unittest.cc')
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86_unittest.cc2266
1 files changed, 2266 insertions, 0 deletions
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86_unittest.cc b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86_unittest.cc
new file mode 100644
index 0000000000..359f1c863b
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86_unittest.cc
@@ -0,0 +1,2266 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+// stackwalker_x86_unittest.cc: Unit tests for StackwalkerX86 class.
+
+#include <string>
+#include <vector>
+
+#include "breakpad_googletest_includes.h"
+#include "common/test_assembler.h"
+#include "common/using_std_string.h"
+#include "google_breakpad/common/minidump_format.h"
+#include "google_breakpad/processor/basic_source_line_resolver.h"
+#include "google_breakpad/processor/call_stack.h"
+#include "google_breakpad/processor/code_module.h"
+#include "google_breakpad/processor/source_line_resolver_interface.h"
+#include "google_breakpad/processor/stack_frame_cpu.h"
+#include "processor/stackwalker_unittest_utils.h"
+#include "processor/stackwalker_x86.h"
+#include "processor/windows_frame_info.h"
+
+using google_breakpad::BasicSourceLineResolver;
+using google_breakpad::CallStack;
+using google_breakpad::CodeModule;
+using google_breakpad::StackFrameSymbolizer;
+using google_breakpad::StackFrame;
+using google_breakpad::StackFrameX86;
+using google_breakpad::Stackwalker;
+using google_breakpad::StackwalkerX86;
+using google_breakpad::SystemInfo;
+using google_breakpad::WindowsFrameInfo;
+using google_breakpad::test_assembler::kLittleEndian;
+using google_breakpad::test_assembler::Label;
+using google_breakpad::test_assembler::Section;
+using std::vector;
+using testing::_;
+using testing::AnyNumber;
+using testing::DoAll;
+using testing::Return;
+using testing::SetArgumentPointee;
+using testing::Test;
+
+class StackwalkerX86Fixture {
+ public:
+ StackwalkerX86Fixture()
+ : stack_section(kLittleEndian),
+ // Give the two modules reasonable standard locations and names
+ // for tests to play with.
+ module1(0x40000000, 0x10000, "module1", "version1"),
+ 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";
+ system_info.os_version = "Salacious Skink";
+ system_info.cpu = "x86";
+ system_info.cpu_info = "";
+
+ // Put distinctive values in the raw CPU context.
+ BrandContext(&raw_context);
+
+ // 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.
+ EXPECT_CALL(supplier, GetCStringSymbolData(_, _, _, _, _))
+ .WillRepeatedly(Return(MockSymbolSupplier::NOT_FOUND));
+
+ // Avoid GMOCK WARNING "Uninteresting mock function call - returning
+ // directly" for FreeSymbolData().
+ EXPECT_CALL(supplier, FreeSymbolData(_)).Times(AnyNumber());
+
+ // Reset max_frames_scanned since it's static.
+ Stackwalker::set_max_frames_scanned(1024);
+ }
+
+ // Set the Breakpad symbol information that supplier should return for
+ // MODULE to INFO.
+ void SetModuleSymbols(MockCodeModule *module, const string &info) {
+ size_t buffer_size;
+ char *buffer = supplier.CopySymbolDataAndOwnTheCopy(info, &buffer_size);
+ EXPECT_CALL(supplier, GetCStringSymbolData(module, &system_info, _, _, _))
+ .WillRepeatedly(DoAll(SetArgumentPointee<3>(buffer),
+ SetArgumentPointee<4>(buffer_size),
+ Return(MockSymbolSupplier::FOUND)));
+ }
+
+ // Populate stack_region with the contents of stack_section. Use
+ // stack_section.start() as the region's starting address.
+ void RegionFromSection() {
+ string contents;
+ ASSERT_TRUE(stack_section.GetContents(&contents));
+ stack_region.Init(stack_section.start().Value(), contents);
+ }
+
+ // Fill RAW_CONTEXT with pseudo-random data, for round-trip checking.
+ void BrandContext(MDRawContextX86 *raw_context) {
+ uint8_t x = 173;
+ for (size_t i = 0; i < sizeof(*raw_context); i++)
+ reinterpret_cast<uint8_t *>(raw_context)[i] = (x += 17);
+ }
+
+ SystemInfo system_info;
+ MDRawContextX86 raw_context;
+ Section stack_section;
+ MockMemoryRegion stack_region;
+ MockCodeModule module1;
+ MockCodeModule module2;
+ MockCodeModule module3;
+ MockCodeModule module4;
+ MockCodeModule module5;
+ MockCodeModule module6;
+ MockCodeModules modules;
+ MockSymbolSupplier supplier;
+ BasicSourceLineResolver resolver;
+ CallStack call_stack;
+ const vector<StackFrame *> *frames;
+};
+
+class SanityCheck: public StackwalkerX86Fixture, public Test { };
+
+TEST_F(SanityCheck, NoResolver) {
+ stack_section.start() = 0x80000000;
+ stack_section.D32(0).D32(0); // end-of-stack marker
+ RegionFromSection();
+ raw_context.eip = 0x40000200;
+ raw_context.ebp = 0x80000000;
+
+ StackFrameSymbolizer frame_symbolizer(NULL, NULL);
+ StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules,
+ &frame_symbolizer);
+ // This should succeed, even without a resolver or supplier.
+ vector<const CodeModule*> modules_without_symbols;
+ vector<const CodeModule*> modules_with_corrupt_symbols;
+ ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
+ &modules_with_corrupt_symbols));
+ ASSERT_EQ(1U, modules_without_symbols.size());
+ ASSERT_EQ("module1", modules_without_symbols[0]->debug_file());
+ ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
+ frames = call_stack.frames();
+ StackFrameX86 *frame = static_cast<StackFrameX86 *>(frames->at(0));
+ // Check that the values from the original raw context made it
+ // through to the context in the stack frame.
+ EXPECT_EQ(0, memcmp(&raw_context, &frame->context, sizeof(raw_context)));
+}
+
+class GetContextFrame: public StackwalkerX86Fixture, public Test { };
+
+TEST_F(GetContextFrame, Simple) {
+ stack_section.start() = 0x80000000;
+ stack_section.D32(0).D32(0); // end-of-stack marker
+ RegionFromSection();
+ raw_context.eip = 0x40000200;
+ raw_context.ebp = 0x80000000;
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules,
+ &frame_symbolizer);
+ vector<const CodeModule*> modules_without_symbols;
+ vector<const CodeModule*> modules_with_corrupt_symbols;
+ ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
+ &modules_with_corrupt_symbols));
+ ASSERT_EQ(1U, modules_without_symbols.size());
+ ASSERT_EQ("module1", modules_without_symbols[0]->debug_file());
+ ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
+ frames = call_stack.frames();
+ StackFrameX86 *frame = static_cast<StackFrameX86 *>(frames->at(0));
+ // Check that the values from the original raw context made it
+ // through to the context in the stack frame.
+ EXPECT_EQ(0, memcmp(&raw_context, &frame->context, sizeof(raw_context)));
+}
+
+// The stackwalker should be able to produce the context frame even
+// without stack memory present.
+TEST_F(GetContextFrame, NoStackMemory) {
+ raw_context.eip = 0x40000200;
+ raw_context.ebp = 0x80000000;
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerX86 walker(&system_info, &raw_context, NULL, &modules,
+ &frame_symbolizer);
+ vector<const CodeModule*> modules_without_symbols;
+ vector<const CodeModule*> modules_with_corrupt_symbols;
+ ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
+ &modules_with_corrupt_symbols));
+ ASSERT_EQ(1U, modules_without_symbols.size());
+ ASSERT_EQ("module1", modules_without_symbols[0]->debug_file());
+ ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
+ frames = call_stack.frames();
+ StackFrameX86 *frame = static_cast<StackFrameX86 *>(frames->at(0));
+ // Check that the values from the original raw context made it
+ // through to the context in the stack frame.
+ EXPECT_EQ(0, memcmp(&raw_context, &frame->context, sizeof(raw_context)));
+}
+
+class GetCallerFrame: public StackwalkerX86Fixture, public Test {
+ protected:
+ void IPAddressIsNotInKnownModuleTestImpl(bool has_corrupt_symbols);
+};
+
+// Walk a traditional frame. A traditional frame saves the caller's
+// %ebp just below the return address, and has its own %ebp pointing
+// at the saved %ebp.
+TEST_F(GetCallerFrame, Traditional) {
+ stack_section.start() = 0x80000000;
+ Label frame0_ebp, frame1_ebp;
+ stack_section
+ .Append(12, 0) // frame 0: space
+ .Mark(&frame0_ebp) // frame 0 %ebp points here
+ .D32(frame1_ebp) // frame 0: saved %ebp
+ .D32(0x40008679) // frame 0: return address
+ .Append(8, 0) // frame 1: space
+ .Mark(&frame1_ebp) // frame 1 %ebp points here
+ .D32(0) // frame 1: saved %ebp (stack end)
+ .D32(0); // frame 1: return address (stack end)
+ RegionFromSection();
+ raw_context.eip = 0x4000c7a5;
+ raw_context.esp = stack_section.start().Value();
+ raw_context.ebp = frame0_ebp.Value();
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules,
+ &frame_symbolizer);
+ vector<const CodeModule*> modules_without_symbols;
+ vector<const CodeModule*> modules_with_corrupt_symbols;
+ ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
+ &modules_with_corrupt_symbols));
+ ASSERT_EQ(1U, modules_without_symbols.size());
+ ASSERT_EQ("module1", modules_without_symbols[0]->debug_file());
+ ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
+ 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);
+ 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 *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
+// of the stack for something that looks like a return address.
+TEST_F(GetCallerFrame, TraditionalScan) {
+ stack_section.start() = 0x80000000;
+ Label frame1_ebp;
+ Label frame1_esp;
+ stack_section
+ // frame 0
+ .D32(0xf065dc76) // locals area:
+ .D32(0x46ee2167) // garbage that doesn't look like
+ .D32(0xbab023ec) // a return address
+ .D32(frame1_ebp) // saved %ebp (%ebp fails to point here, forcing scan)
+ .D32(0x4000129d) // return address
+ // frame 1
+ .Mark(&frame1_esp)
+ .Append(8, 0) // space
+ .Mark(&frame1_ebp) // %ebp points here
+ .D32(0) // saved %ebp (stack end)
+ .D32(0); // return address (stack end)
+
+ RegionFromSection();
+ raw_context.eip = 0x4000f49d;
+ raw_context.esp = stack_section.start().Value();
+ // Make the frame pointer bogus, to make the stackwalker scan the stack
+ // for something that looks like a return address.
+ raw_context.ebp = 0xd43eed6e;
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules,
+ &frame_symbolizer);
+ vector<const CodeModule*> modules_without_symbols;
+ vector<const CodeModule*> modules_with_corrupt_symbols;
+ ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
+ &modules_with_corrupt_symbols));
+ ASSERT_EQ(1U, modules_without_symbols.size());
+ ASSERT_EQ("module1", modules_without_symbols[0]->debug_file());
+ ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
+ 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(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 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
+ 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(frame1_esp.Value(), frame1->context.esp);
+ EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp);
+ EXPECT_EQ(NULL, frame1->windows_frame_info);
+ }
+}
+
+// Force scanning for a return address a long way down the stack
+TEST_F(GetCallerFrame, TraditionalScanLongWay) {
+ stack_section.start() = 0x80000000;
+ Label frame1_ebp;
+ Label frame1_esp;
+ stack_section
+ // frame 0
+ .D32(0xf065dc76) // locals area:
+ .D32(0x46ee2167) // garbage that doesn't look like
+ .D32(0xbab023ec) // a return address
+ .Append(20 * 4, 0) // a bunch of space
+ .D32(frame1_ebp) // saved %ebp (%ebp fails to point here, forcing scan)
+ .D32(0x4000129d) // return address
+ // frame 1
+ .Mark(&frame1_esp)
+ .Append(8, 0) // space
+ .Mark(&frame1_ebp) // %ebp points here
+ .D32(0) // saved %ebp (stack end)
+ .D32(0); // return address (stack end)
+
+ RegionFromSection();
+ raw_context.eip = 0x4000f49d;
+ raw_context.esp = stack_section.start().Value();
+ // Make the frame pointer bogus, to make the stackwalker scan the stack
+ // for something that looks like a return address.
+ raw_context.ebp = 0xd43eed6e;
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules,
+ &frame_symbolizer);
+ vector<const CodeModule*> modules_without_symbols;
+ vector<const CodeModule*> modules_with_corrupt_symbols;
+ ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
+ &modules_with_corrupt_symbols));
+ ASSERT_EQ(1U, modules_without_symbols.size());
+ ASSERT_EQ("module1", modules_without_symbols[0]->debug_file());
+ ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
+ 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(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 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
+ 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(frame1_esp.Value(), frame1->context.esp);
+ EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp);
+ EXPECT_EQ(NULL, frame1->windows_frame_info);
+ }
+}
+
+// Test that set_max_frames_scanned prevents using stack scanning
+// to find caller frames.
+TEST_F(GetCallerFrame, ScanningNotAllowed) {
+ stack_section.start() = 0x80000000;
+ Label frame1_ebp;
+ stack_section
+ // frame 0
+ .D32(0xf065dc76) // locals area:
+ .D32(0x46ee2167) // garbage that doesn't look like
+ .D32(0xbab023ec) // a return address
+ .D32(frame1_ebp) // saved %ebp (%ebp fails to point here, forcing scan)
+ .D32(0x4000129d) // return address
+ // frame 1
+ .Append(8, 0) // space
+ .Mark(&frame1_ebp) // %ebp points here
+ .D32(0) // saved %ebp (stack end)
+ .D32(0); // return address (stack end)
+
+ RegionFromSection();
+ raw_context.eip = 0x4000f49d;
+ raw_context.esp = stack_section.start().Value();
+ // Make the frame pointer bogus, to make the stackwalker scan the stack
+ // for something that looks like a return address.
+ raw_context.ebp = 0xd43eed6e;
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules,
+ &frame_symbolizer);
+ Stackwalker::set_max_frames_scanned(0);
+
+ vector<const CodeModule*> modules_without_symbols;
+ vector<const CodeModule*> modules_with_corrupt_symbols;
+ ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
+ &modules_with_corrupt_symbols));
+ ASSERT_EQ(1U, modules_without_symbols.size());
+ ASSERT_EQ("module1", modules_without_symbols[0]->debug_file());
+ ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
+ frames = call_stack.frames();
+ ASSERT_EQ(1U, 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(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);
+ }
+}
+
+// Use Windows frame data (a "STACK WIN 4" record, from a
+// FrameTypeFrameData DIA record) to walk a stack frame.
+TEST_F(GetCallerFrame, WindowsFrameData) {
+ SetModuleSymbols(&module1,
+ "STACK WIN 4 aa85 176 0 0 4 10 4 0 1"
+ " $T2 $esp .cbSavedRegs + ="
+ " $T0 .raSearchStart ="
+ " $eip $T0 ^ ="
+ " $esp $T0 4 + ="
+ " $ebx $T2 4 - ^ ="
+ " $edi $T2 8 - ^ ="
+ " $esi $T2 12 - ^ ="
+ " $ebp $T2 16 - ^ =\n");
+ Label frame1_esp, frame1_ebp;
+ stack_section.start() = 0x80000000;
+ stack_section
+ // frame 0
+ .D32(frame1_ebp) // saved regs: %ebp
+ .D32(0xa7120d1a) // %esi
+ .D32(0x630891be) // %edi
+ .D32(0x9068a878) // %ebx
+ .D32(0xa08ea45f) // locals: unused
+ .D32(0x40001350) // return address
+ // frame 1
+ .Mark(&frame1_esp)
+ .Append(12, 0) // empty space
+ .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
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules,
+ &frame_symbolizer);
+ vector<const CodeModule*> modules_without_symbols;
+ vector<const CodeModule*> modules_with_corrupt_symbols;
+ ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
+ &modules_with_corrupt_symbols));
+ ASSERT_EQ(0U, modules_without_symbols.size());
+ ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
+ 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
+ | 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 frame0_esp, frame0_ebp;
+ Label frame1_esp, frame1_ebp;
+ stack_section.start() = 0x80000000;
+ stack_section
+ // frame 0
+ .Mark(&frame0_esp)
+ .D32(0x0ffa0ffa) // unused saved register
+ .D32(0xdeaddead) // locals
+ .D32(0xbeefbeef)
+ .D32(0) // 8-byte alignment
+ .Mark(&frame0_ebp)
+ .D32(frame1_ebp) // saved %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 = frame0_esp.Value();
+ raw_context.ebp = frame0_ebp.Value();
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules,
+ &frame_symbolizer);
+ vector<const CodeModule*> modules_without_symbols;
+ vector<const CodeModule*> modules_with_corrupt_symbols;
+ ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
+ &modules_with_corrupt_symbols));
+ ASSERT_EQ(1U, modules_without_symbols.size());
+ ASSERT_EQ("module2", modules_without_symbols[0]->debug_file());
+ ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
+ 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(frame0_esp.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 *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
+// FrameTypeFrameData DIA record) to walk a frame, and depend on the
+// parameter size from the callee as well.
+TEST_F(GetCallerFrame, WindowsFrameDataParameterSize) {
+ SetModuleSymbols(&module1, "FUNC 1000 100 c module1::wheedle\n");
+ SetModuleSymbols(&module2,
+ // Note bogus parameter size in FUNC record; the stack walker
+ // should prefer the STACK WIN record, and see '4' below.
+ "FUNC aa85 176 beef module2::whine\n"
+ "STACK WIN 4 aa85 176 0 0 4 10 4 0 1"
+ " $T2 $esp .cbLocals + .cbSavedRegs + ="
+ " $T0 .raSearchStart ="
+ " $eip $T0 ^ ="
+ " $esp $T0 4 + ="
+ " $ebp $T0 20 - ^ ="
+ " $ebx $T0 8 - ^ =\n");
+ Label frame0_esp, frame0_ebp;
+ Label frame1_esp;
+ Label frame2_esp, frame2_ebp;
+ stack_section.start() = 0x80000000;
+ stack_section
+ // frame 0, in module1::wheedle. Traditional frame.
+ .Mark(&frame0_esp)
+ .Append(16, 0) // frame space
+ .Mark(&frame0_ebp)
+ .D32(0x6fa902e0) // saved %ebp. Not a frame pointer.
+ .D32(0x5000aa95) // return address, in module2::whine
+ // frame 1, in module2::whine. FrameData frame.
+ .Mark(&frame1_esp)
+ .D32(0xbaa0cb7a) // argument 3 passed to module1::wheedle
+ .D32(0xbdc92f9f) // argument 2
+ .D32(0x0b1d8442) // argument 1
+ .D32(frame2_ebp) // saved %ebp
+ .D32(0xb1b90a15) // unused
+ .D32(0xf18e072d) // unused
+ .D32(0x2558c7f3) // saved %ebx
+ .D32(0x0365e25e) // unused
+ .D32(0x2a179e38) // return address; $T0 points here
+ // frame 2, in no module
+ .Mark(&frame2_esp)
+ .Append(12, 0) // empty space
+ .Mark(&frame2_ebp)
+ .D32(0) // saved %ebp (stack end)
+ .D32(0); // saved %eip (stack end)
+
+ RegionFromSection();
+ raw_context.eip = 0x40001004; // in module1::wheedle
+ raw_context.esp = stack_section.start().Value();
+ raw_context.ebp = frame0_ebp.Value();
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules,
+ &frame_symbolizer);
+ vector<const CodeModule*> modules_without_symbols;
+ vector<const CodeModule*> modules_with_corrupt_symbols;
+ ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
+ &modules_with_corrupt_symbols));
+ ASSERT_EQ(0U, modules_without_symbols.size());
+ ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
+ frames = call_stack.frames();
+ ASSERT_EQ(3U, 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(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);
+ }
+
+ { // 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);
+ }
+
+ { // 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
+// FrameTypeFrameData DIA record) to walk a stack frame, where the
+// expression fails to yield both an $eip and an $ebp value, and the stack
+// walker must scan.
+TEST_F(GetCallerFrame, WindowsFrameDataScan) {
+ SetModuleSymbols(&module1,
+ "STACK WIN 4 c8c 111 0 0 4 10 4 0 1 bad program string\n");
+ // Mark frame 1's PC as the end of the stack.
+ SetModuleSymbols(&module2,
+ "FUNC 7c38 accf 0 module2::function\n"
+ "STACK WIN 4 7c38 accf 0 0 4 10 4 0 1 $eip 0 = $ebp 0 =\n");
+ Label frame1_esp;
+ stack_section.start() = 0x80000000;
+ stack_section
+ // frame 0
+ .Append(16, 0x2a) // unused, garbage
+ .D32(0x50007ce9) // return address
+ // frame 1
+ .Mark(&frame1_esp)
+ .Append(8, 0); // empty space
+
+ RegionFromSection();
+ raw_context.eip = 0x40000c9c;
+ raw_context.esp = stack_section.start().Value();
+ raw_context.ebp = 0x2ae314cd; // should not be needed to walk frame
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules,
+ &frame_symbolizer);
+ vector<const CodeModule*> modules_without_symbols;
+ vector<const CodeModule*> modules_with_corrupt_symbols;
+ ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
+ &modules_with_corrupt_symbols));
+ ASSERT_EQ(0U, modules_without_symbols.size());
+ ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
+ 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(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 *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
+// FrameTypeFrameData DIA record) to walk a stack frame, where the
+// expression yields an $eip that falls outside of any module, and the
+// stack walker must scan.
+TEST_F(GetCallerFrame, WindowsFrameDataBadEIPScan) {
+ SetModuleSymbols(&module1,
+ "STACK WIN 4 6e6 e7 0 0 0 8 4 0 1"
+ // A traditional frame, actually.
+ " $eip $ebp 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ =\n");
+ // Mark frame 1's PC as the end of the stack.
+ SetModuleSymbols(&module2,
+ "FUNC cfdb 8406 0 module2::function\n"
+ "STACK WIN 4 cfdb 8406 0 0 0 0 0 0 1 $eip 0 = $ebp 0 =\n");
+ stack_section.start() = 0x80000000;
+
+ // In this stack, the context's %ebp is pointing at the wrong place, so
+ // the stack walker needs to scan to find the return address, and then
+ // scan again to find the caller's saved %ebp.
+ Label frame0_ebp, frame1_ebp, frame1_esp;
+ stack_section
+ // frame 0
+ .Append(8, 0x2a) // garbage
+ .Mark(&frame0_ebp) // frame 0 %ebp points here, but should point
+ // at *** below
+ // The STACK WIN record says that the following two values are
+ // frame 1's saved %ebp and return address, but the %ebp is wrong;
+ // they're garbage. The stack walker will scan for the right values.
+ .D32(0x3d937b2b) // alleged to be frame 1's saved %ebp
+ .D32(0x17847f5b) // alleged to be frame 1's return address
+ .D32(frame1_ebp) // frame 1's real saved %ebp; scan will find
+ .D32(0x2b2b2b2b) // first word of realigned register save area
+ // *** frame 0 %ebp ought to be pointing here
+ .D32(0x2c2c2c2c) // realigned locals area
+ .D32(0x5000d000) // frame 1's real saved %eip; scan will find
+ // Frame 1, in module2::function. The STACK WIN record describes
+ // this as the oldest frame, without referring to its contents, so
+ // we needn't to provide any actual data here.
+ .Mark(&frame1_esp)
+ .Mark(&frame1_ebp) // frame 1 %ebp points here
+ // A dummy value for frame 1's %ebp to point at. The scan recognizes the
+ // saved %ebp because it points to a valid word in the stack memory region.
+ .D32(0x2d2d2d2d);
+
+ RegionFromSection();
+ raw_context.eip = 0x40000700;
+ raw_context.esp = stack_section.start().Value();
+ raw_context.ebp = frame0_ebp.Value();
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules,
+ &frame_symbolizer);
+ vector<const CodeModule*> modules_without_symbols;
+ vector<const CodeModule*> modules_with_corrupt_symbols;
+ ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
+ &modules_with_corrupt_symbols));
+ ASSERT_EQ(0U, modules_without_symbols.size());
+ ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
+ 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(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 *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
+// does not modify %ebp from the value it had in the caller.
+TEST_F(GetCallerFrame, WindowsFPOUnchangedEBP) {
+ SetModuleSymbols(&module1,
+ // Note bogus parameter size in FUNC record; the walker
+ // should prefer the STACK WIN record, and see the '8' below.
+ "FUNC e8a8 100 feeb module1::discombobulated\n"
+ "STACK WIN 0 e8a8 100 0 0 8 4 10 0 0 0\n");
+ Label frame0_esp;
+ Label frame1_esp, frame1_ebp;
+ stack_section.start() = 0x80000000;
+ stack_section
+ // frame 0, in module1::wheedle. FrameTypeFPO (STACK WIN 0) frame.
+ .Mark(&frame0_esp)
+ // no outgoing parameters; this is the youngest frame.
+ .D32(0x7c521352) // four bytes of saved registers
+ .Append(0x10, 0x42) // local area
+ .D32(0x40009b5b) // return address, in module1, no function
+ // frame 1, in module1, no function.
+ .Mark(&frame1_esp)
+ .D32(0xf60ea7fc) // junk
+ .Mark(&frame1_ebp)
+ .D32(0) // saved %ebp (stack end)
+ .D32(0); // saved %eip (stack end)
+
+ RegionFromSection();
+ raw_context.eip = 0x4000e8b8; // in module1::whine
+ raw_context.esp = stack_section.start().Value();
+ // Frame pointer unchanged from caller.
+ raw_context.ebp = frame1_ebp.Value();
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules,
+ &frame_symbolizer);
+ vector<const CodeModule*> modules_without_symbols;
+ vector<const CodeModule*> modules_with_corrupt_symbols;
+ ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
+ &modules_with_corrupt_symbols));
+ ASSERT_EQ(0U, modules_without_symbols.size());
+ ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
+ 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(0x4000e8b8U, frame0->instruction);
+ EXPECT_EQ(0x4000e8b8U, frame0->context.eip);
+ EXPECT_EQ(frame0_esp.Value(), frame0->context.esp);
+ // unchanged from caller
+ EXPECT_EQ(frame1_ebp.Value(), frame0->context.ebp);
+ 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);
+ }
+
+ { // 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),
+ 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
+// that uses %ebp for its own purposes, saving the value it had in the
+// caller in the standard place in the saved register area.
+TEST_F(GetCallerFrame, WindowsFPOUsedEBP) {
+ SetModuleSymbols(&module1,
+ // Note bogus parameter size in FUNC record; the walker
+ // should prefer the STACK WIN record, and see the '8' below.
+ "FUNC 9aa8 e6 abbe module1::RaisedByTheAliens\n"
+ "STACK WIN 0 9aa8 e6 a 0 10 8 4 0 0 1\n");
+ Label frame0_esp;
+ Label frame1_esp, frame1_ebp;
+ stack_section.start() = 0x80000000;
+ stack_section
+ // frame 0, in module1::wheedle. FrameTypeFPO (STACK WIN 0) frame.
+ .Mark(&frame0_esp)
+ // no outgoing parameters; this is the youngest frame.
+ .D32(frame1_ebp) // saved register area: saved %ebp
+ .D32(0xb68bd5f9) // saved register area: something else
+ .D32(0xd25d05fc) // local area
+ .D32(0x4000debe) // return address, in module1, no function
+ // frame 1, in module1, no function.
+ .Mark(&frame1_esp)
+ .D32(0xf0c9a974) // junk
+ .Mark(&frame1_ebp)
+ .D32(0) // saved %ebp (stack end)
+ .D32(0); // saved %eip (stack end)
+
+ RegionFromSection();
+ raw_context.eip = 0x40009ab8; // in module1::RaisedByTheAliens
+ raw_context.esp = stack_section.start().Value();
+ // RaisedByTheAliens uses %ebp for its own mysterious purposes.
+ raw_context.ebp = 0xecbdd1a5;
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules,
+ &frame_symbolizer);
+ vector<const CodeModule*> modules_without_symbols;
+ vector<const CodeModule*> modules_with_corrupt_symbols;
+ ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
+ &modules_with_corrupt_symbols));
+ ASSERT_EQ(0U, modules_without_symbols.size());
+ ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
+ 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(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);
+ }
+
+ { // 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 test for FPO code that references the initial value of
+// EBX.
+TEST_F(GetCallerFrame, WindowsFPOReferencesEBX) {
+ MockCodeModule ntdll(0x776e0000, 0x142000, "ntdll", "ntdllver");
+ MockCodeModule kernel32(0x77250000, 0xd5000, "kernel32", "kernel32ver");
+ MockCodeModule chrome_child(0x5a710000, 0x2b7c000, "chrome_child",
+ "chrome_childver");
+ MockCodeModules modules;
+ modules.Add(&ntdll);
+ modules.Add(&kernel32);
+ modules.Add(&chrome_child);
+ SetModuleSymbols(&ntdll,
+ "PUBLIC 46bf4 0 KiFastSystemCallRet\n"
+ "STACK WIN 0 46bf4 1 0 0 0 0 0 0 0 0\n"
+
+ "PUBLIC 46550 10 NtWaitForKeyedEvent\n"
+ "STACK WIN 0 46550 f 0 0 10 0 0 0 0 0\n"
+
+ "PUBLIC 4965 10 RtlSleepConditionVariableSRW\n"
+
+ "STACK WIN 4 4965 23 23 0 10 8 38 0 1 $T0 $ebx = "
+ "$eip $T0 4 + ^ = $ebx $T0 ^ = $esp $T0 8 + = "
+ "$ebp $ebp ^ = $L $ebp = $P $T0 8 + .cbParams + =\n"
+
+ "STACK WIN 4 4988 e3 0 0 10 8 38 0 1 $T0 $ebx = "
+ "$eip $T0 4 + ^ = $ebx $T0 ^ = $esp $T0 8 + = "
+ "$ebp $ebp ^ = $L $ebp = $P $T0 8 + .cbParams + =\n"
+
+ "STACK WIN 4 4b2e b 0 0 10 8 38 0 1 $T0 $ebx = "
+ "$eip $T0 4 + ^ = $ebx $T0 ^ = $esp $T0 8 + = "
+ "$ebp $ebp ^ = $L $ebp = $P $T0 8 + .cbParams + =\n");
+
+ SetModuleSymbols(&kernel32,
+ "PUBLIC 3217a 10 SleepConditionVariableSRW\n"
+ "STACK WIN 4 3217a 40 8 0 10 0 8 0 1 $T0 $ebp = $eip $T0 4 "
+ "+ ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 .cbSavedRegs - = "
+ "$P $T0 8 + .cbParams + =\n");
+
+ SetModuleSymbols(&chrome_child,
+ "FUNC 4f4851 20 0 base::ConditionVariable::TimedWait\n");
+
+ stack_section.start() = 0x0026c048;
+ stack_section
+ .D32(0x7772655c)
+ .D32(0x776e4a3f)
+ .D32(0x00000000)
+ .D32(0x0026c070)
+ .D32(0x00000000)
+ .D32(0x00000000)
+ .D32(0x00000001)
+ .D32(0x0026c168)
+ .D32(0x00000000)
+ .D32(0x00000000)
+ .D32(0x00000000)
+ .D32(0x0026c070)
+ .D32(0x00000000)
+ .D32(0x00000000)
+ .D32(0x00000000)
+ .D32(0x0026c164)
+ .D32(0x00be0000)
+ .D32(0x0026fc60)
+ .D32(0x0026c164)
+ .D32(0x00000000)
+ .D32(0xfffffffe)
+ .D32(0x00000000)
+ .D32(0x0026c0d0)
+ .D32(0x7728219e)
+ .D32(0x000003e8)
+ .D32(0x00000000)
+ .D32(0x7728219e)
+ .D32(0x0026c168)
+ .D32(0x0026c164)
+ .D32(0x00000000)
+ .D32(0x00000000)
+ .D32(0x0026c168)
+ .D32(0x000003e8)
+ .D32(0x000003e8)
+ .D32(0x0026c0ec)
+ .D32(0x5ac0486c)
+ .D32(0x0026c168)
+ .D32(0x0026c108)
+ .D32(0x5ac0484c)
+ .D32(0x0026c100)
+ .D32(0x0026c160);
+
+ RegionFromSection();
+ raw_context.eip = 0x77726bf4; // in ntdll!KiFastSystemCallRet
+ raw_context.esp = stack_section.start().Value();
+ raw_context.ebp = 0x26c0a0;
+ raw_context.ebx = 0x26c0ac;
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules,
+ &frame_symbolizer);
+ vector<const CodeModule *> modules_without_symbols;
+ vector<const CodeModule *> modules_with_corrupt_symbols;
+ ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
+ &modules_with_corrupt_symbols));
+ ASSERT_EQ(0U, modules_without_symbols.size());
+ ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
+ frames = call_stack.frames();
+
+ ASSERT_EQ(5U, frames->size());
+ {
+ const StackFrameX86 &frame = *static_cast<StackFrameX86 *>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame.trust);
+ EXPECT_EQ(0x77726bf4U, frame.context.eip);
+ EXPECT_EQ("KiFastSystemCallRet", frame.function_name);
+ }
+ {
+ const StackFrameX86 &frame = *static_cast<StackFrameX86 *>(frames->at(1));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame.trust);
+ EXPECT_EQ(0x7772655cU, frame.context.eip);
+ EXPECT_EQ("NtWaitForKeyedEvent", frame.function_name);
+ }
+ {
+ const StackFrameX86 &frame = *static_cast<StackFrameX86 *>(frames->at(2));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame.trust);
+ EXPECT_EQ(0x776e4a3fU, frame.context.eip);
+ EXPECT_EQ("RtlSleepConditionVariableSRW", frame.function_name);
+ }
+ {
+ const StackFrameX86 &frame = *static_cast<StackFrameX86 *>(frames->at(3));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame.trust);
+ EXPECT_EQ(0x7728219eU, frame.context.eip);
+ EXPECT_EQ("SleepConditionVariableSRW", frame.function_name);
+ }
+ {
+ const StackFrameX86 &frame = *static_cast<StackFrameX86 *>(frames->at(4));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame.trust);
+ EXPECT_EQ(0x5ac0486cU, frame.context.eip);
+ EXPECT_EQ("base::ConditionVariable::TimedWait", frame.function_name);
+ }
+}
+
+// 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();
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules,
+ &frame_symbolizer);
+ vector<const CodeModule*> modules_without_symbols;
+ vector<const CodeModule*> modules_with_corrupt_symbols;
+ ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
+ &modules_with_corrupt_symbols));
+ ASSERT_EQ(0U, modules_without_symbols.size());
+ ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
+ 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
+ | StackFrameX86::CONTEXT_VALID_EBX),
+ 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);
+ }
+}
+
+// Scan the stack for a better return address and potentially skip frames
+// when the calculated return address is not in a known module. Note, that
+// the span of this scan is somewhat arbitrarily limited to 160 search words
+// for the context frame and 40 search words (pointers) for the other frames:
+// const int kRASearchWords = 40;
+// This means that frames can be skipped only when their size is relatively
+// small: smaller than 4 * kRASearchWords * sizeof(InstructionType)
+TEST_F(GetCallerFrame, ReturnAddressIsNotInKnownModule) {
+ MockCodeModule msvcrt_dll(0x77be0000, 0x58000, "msvcrt.dll", "version1");
+ SetModuleSymbols(&msvcrt_dll, // msvcrt.dll
+ "PUBLIC 38180 0 wcsstr\n"
+ "STACK WIN 4 38180 61 10 0 8 0 0 0 1 $T0 $ebp = $eip $T0 "
+ "4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 .cbSavedRegs "
+ "- = $P $T0 4 + .cbParams + =\n");
+
+ MockCodeModule kernel32_dll(0x7c800000, 0x103000, "kernel32.dll", "version1");
+ SetModuleSymbols(&kernel32_dll, // kernel32.dll
+ "PUBLIC efda 8 FindNextFileW\n"
+ "STACK WIN 4 efda 1bb c 0 8 8 3c 0 1 $T0 $ebp = $eip $T0 "
+ "4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 .cbSavedRegs "
+ "- = $P $T0 4 + .cbParams + =\n");
+
+ MockCodeModule chrome_dll(0x1c30000, 0x28C8000, "chrome.dll", "version1");
+ SetModuleSymbols(&chrome_dll, // chrome.dll
+ "FUNC e3cff 4af 0 file_util::FileEnumerator::Next()\n"
+ "e3cff 1a 711 2505\n"
+ "STACK WIN 4 e3cff 4af 20 0 4 c 94 0 1 $T1 .raSearch = "
+ "$T0 $T1 4 - 8 @ = $ebp $T1 4 - ^ = $eip $T1 ^ = $esp "
+ "$T1 4 + = $20 $T0 152 - ^ = $23 $T0 156 - ^ = $24 "
+ "$T0 160 - ^ =\n");
+
+ // Create some modules with some stock debugging information.
+ MockCodeModules local_modules;
+ local_modules.Add(&msvcrt_dll);
+ local_modules.Add(&kernel32_dll);
+ local_modules.Add(&chrome_dll);
+
+ Label frame0_esp;
+ Label frame0_ebp;
+ Label frame1_ebp;
+ Label frame2_ebp;
+ Label frame3_ebp;
+
+ stack_section.start() = 0x0932f2d0;
+ stack_section
+ .Mark(&frame0_esp)
+ .D32(0x0764e000)
+ .D32(0x0764e068)
+ .Mark(&frame0_ebp)
+ .D32(frame1_ebp) // Child EBP
+ .D32(0x001767a0) // return address of frame 0
+ // Not in known module
+ .D32(0x0764e0c6)
+ .D32(0x001bb1b8)
+ .D32(0x0764e068)
+ .D32(0x00000003)
+ .D32(0x0764e068)
+ .D32(0x00000003)
+ .D32(0x07578828)
+ .D32(0x0764e000)
+ .D32(0x00000000)
+ .D32(0x001c0010)
+ .D32(0x0764e0c6)
+ .Mark(&frame1_ebp)
+ .D32(frame2_ebp) // Child EBP
+ .D32(0x7c80f10f) // return address of frame 1
+ // inside kernel32!FindNextFileW
+ .D32(0x000008f8)
+ .D32(0x00000000)
+ .D32(0x00000000)
+ .D32(0x00000000)
+ .D32(0x0932f34c)
+ .D32(0x0764e000)
+ .D32(0x00001000)
+ .D32(0x00000000)
+ .D32(0x00000001)
+ .D32(0x00000000)
+ .D32(0x00000000)
+ .D32(0x0932f6a8)
+ .D32(0x00000000)
+ .D32(0x0932f6d8)
+ .D32(0x00000000)
+ .D32(0x000000d6)
+ .D32(0x0764e000)
+ .D32(0x7ff9a000)
+ .D32(0x0932f3fc)
+ .D32(0x00000001)
+ .D32(0x00000001)
+ .D32(0x07578828)
+ .D32(0x0000002e)
+ .D32(0x0932f340)
+ .D32(0x0932eef4)
+ .D32(0x0932ffdc)
+ .D32(0x7c839ad8)
+ .D32(0x7c80f0d8)
+ .D32(0x00000000)
+ .Mark(&frame2_ebp)
+ .D32(frame3_ebp) // Child EBP
+ .D32(0x01d13f91) // return address of frame 2
+ // inside chrome_dll!file_util::FileEnumerator::Next
+ .D32(0x07578828)
+ .D32(0x0932f6ac)
+ .D32(0x0932f9c4)
+ .D32(0x0932f9b4)
+ .D32(0x00000000)
+ .D32(0x00000003)
+ .D32(0x0932f978)
+ .D32(0x01094330)
+ .D32(0x00000000)
+ .D32(0x00000001)
+ .D32(0x01094330)
+ .D32(0x00000000)
+ .D32(0x00000000)
+ .D32(0x07f30000)
+ .D32(0x01c3ba17)
+ .D32(0x08bab840)
+ .D32(0x07f31580)
+ .D32(0x00000000)
+ .D32(0x00000007)
+ .D32(0x0932f940)
+ .D32(0x0000002e)
+ .D32(0x0932f40c)
+ .D32(0x01d13b53)
+ .D32(0x0932f958)
+ .D32(0x00000001)
+ .D32(0x00000007)
+ .D32(0x0932f940)
+ .D32(0x0000002e)
+ .D32(0x00000000)
+ .D32(0x0932f6ac)
+ .D32(0x01e13ef0)
+ .D32(0x00000001)
+ .D32(0x00000007)
+ .D32(0x0932f958)
+ .D32(0x08bab840)
+ .D32(0x0932f9b4)
+ .D32(0x00000000)
+ .D32(0x0932f9b4)
+ .D32(0x000000a7)
+ .D32(0x000000a7)
+ .D32(0x0932f998)
+ .D32(0x579627a2)
+ .Mark(&frame3_ebp)
+ .D32(0) // saved %ebp (stack end)
+ .D32(0); // saved %eip (stack end)
+
+ RegionFromSection();
+ raw_context.eip = 0x77c181cd; // inside msvcrt!wcsstr
+ raw_context.esp = frame0_esp.Value();
+ raw_context.ebp = frame0_ebp.Value();
+ // sanity
+ ASSERT_TRUE(raw_context.esp == stack_section.start().Value());
+ ASSERT_TRUE(raw_context.ebp == stack_section.start().Value() + 8);
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerX86 walker(&system_info, &raw_context, &stack_region,
+ &local_modules, &frame_symbolizer);
+ vector<const CodeModule*> modules_without_symbols;
+ vector<const CodeModule*> modules_with_corrupt_symbols;
+ ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
+ &modules_with_corrupt_symbols));
+ ASSERT_EQ(0U, modules_without_symbols.size());
+ ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
+ frames = call_stack.frames();
+
+ ASSERT_EQ(3U, 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(0x77c181cdU, frame0->instruction);
+ EXPECT_EQ(0x77c181cdU, frame0->context.eip);
+ EXPECT_EQ(frame0_esp.Value(), frame0->context.esp);
+ EXPECT_EQ(frame0_ebp.Value(), frame0->context.ebp);
+ EXPECT_EQ(&msvcrt_dll, frame0->module);
+ EXPECT_EQ("wcsstr", frame0->function_name);
+ ASSERT_TRUE(frame0->windows_frame_info != NULL);
+ EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame0->windows_frame_info->valid);
+ EXPECT_EQ(WindowsFrameInfo::STACK_INFO_FRAME_DATA,
+ frame0->windows_frame_info->type_);
+ EXPECT_EQ("$T0 $ebp = $eip $T0 "
+ "4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 .cbSavedRegs "
+ "- = $P $T0 4 + .cbParams + =",
+ frame0->windows_frame_info->program_string);
+ // It has program string, so allocates_base_pointer is not expected
+ 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_SCAN, frame1->trust);
+ ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP |
+ StackFrameX86::CONTEXT_VALID_ESP |
+ StackFrameX86::CONTEXT_VALID_EBP),
+ frame1->context_validity);
+ EXPECT_EQ(0x7c80f10fU, frame1->instruction + 1);
+ EXPECT_EQ(0x7c80f10fU, frame1->context.eip);
+ // frame 1 was skipped, so intead of frame1_ebp compare with frame2_ebp.
+ EXPECT_EQ(frame2_ebp.Value(), frame1->context.ebp);
+ EXPECT_EQ(&kernel32_dll, frame1->module);
+ EXPECT_EQ("FindNextFileW", frame1->function_name);
+ 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 4 + .cbParams + =",
+ frame1->windows_frame_info->program_string);
+ EXPECT_FALSE(frame1->windows_frame_info->allocates_base_pointer);
+ }
+
+ { // 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),
+ frame2->context_validity);
+ EXPECT_EQ(0x01d13f91U, frame2->instruction + 1);
+ EXPECT_EQ(0x01d13f91U, frame2->context.eip);
+ // frame 1 was skipped, so intead of frame2_ebp compare with frame3_ebp.
+ EXPECT_EQ(frame3_ebp.Value(), frame2->context.ebp);
+ EXPECT_EQ(&chrome_dll, frame2->module);
+ EXPECT_EQ("file_util::FileEnumerator::Next()", frame2->function_name);
+ ASSERT_TRUE(frame2->windows_frame_info != NULL);
+ EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame2->windows_frame_info->valid);
+ EXPECT_EQ(WindowsFrameInfo::STACK_INFO_FRAME_DATA,
+ frame2->windows_frame_info->type_);
+ EXPECT_EQ("$T1 .raSearch = "
+ "$T0 $T1 4 - 8 @ = $ebp $T1 4 - ^ = $eip $T1 ^ = $esp "
+ "$T1 4 + = $20 $T0 152 - ^ = $23 $T0 156 - ^ = $24 "
+ "$T0 160 - ^ =",
+ frame2->windows_frame_info->program_string);
+ EXPECT_FALSE(frame2->windows_frame_info->allocates_base_pointer);
+ }
+}
+
+// Test the .raSearchStart/.raSearch calculation when alignment operators are
+// used in the program string. The current %ebp must be valid and it is the
+// only reliable data point that can be used for that calculation.
+TEST_F(GetCallerFrame, HandleAlignmentInProgramString) {
+ MockCodeModule chrome_dll(0x59630000, 0x19e3000, "chrome.dll", "version1");
+ SetModuleSymbols(&chrome_dll, // chrome.dll
+ "FUNC 56422 50c 8 base::MessageLoop::RunTask"
+ "(base::PendingTask const &)\n"
+ "56422 e 458 4589\n"
+ "STACK WIN 4 56422 50c 11 0 8 c ac 0 1 $T1 .raSearch = $T0 "
+ "$T1 4 - 8 @ = $ebp $T1 4 - ^ = $eip $T1 ^ = $esp $T1 4 + = "
+ "$20 $T0 176 - ^ = $23 $T0 180 - ^ = $24 $T0 184 - ^ =\n"
+ "FUNC 55d34 34a 0 base::MessageLoop::DoWork()\n"
+ "55d34 11 596 4589\n"
+ "STACK WIN 4 55d34 34a 19 0 0 c 134 0 1 $T1 .raSearch = "
+ "$T0 $T1 4 - 8 @ = $ebp $T1 4 - ^ = $eip $T1 ^ = $esp "
+ "$T1 4 + = $20 $T0 312 - ^ = $23 $T0 316 - ^ = $24 $T0 "
+ "320 - ^ =\n"
+ "FUNC 55c39 fb 0 base::MessagePumpForIO::DoRunLoop()\n"
+ "55c39 d 518 19962\n"
+ "STACK WIN 4 55c39 fb d 0 0 c 34 0 1 $T1 .raSearch = $T0 "
+ "$T1 4 - 64 @ = $ebp $T1 4 - ^ = $eip $T1 ^ = $esp $T1 4 + "
+ "= $20 $T0 56 - ^ = $23 $T0 60 - ^ = $24 $T0 64 - ^ =\n"
+ "FUNC 55bf0 49 4 base::MessagePumpWin::Run(base::"
+ "MessagePump::Delegate *)\n"
+ "55bf0 49 48 4724\n"
+ "STACK WIN 4 55bf0 49 c 0 4 0 10 0 1 $T0 $ebp = $eip $T0 4 "
+ "+ ^ = $ebp $T0 ^ = $esp $T0 8 + =\n"
+ "FUNC 165d de 4 malloc\n"
+ "165d 6 119 54\n"
+ "STACK WIN 4 165d de d 0 4 8 0 0 1 $T1 .raSearch = $T0 "
+ "$T1 4 - 8 @ = $ebp $T1 4 - ^ = $eip $T1 ^ = $esp $T1 4 "
+ "+ = $23 $T0 4 - ^ = $24 $T0 8 - ^ =\n"
+ "FUNC 55ac9 79 0 base::MessageLoop::RunInternal()\n"
+ "55ac9 d 427 4589\n"
+ "STACK WIN 4 55ac9 79 d 0 0 8 10 0 1 $T1 .raSearch = $T0 "
+ "$T1 4 - 8 @ = $ebp $T1 4 - ^ = $eip $T1 ^ = $esp $T1 4 + = "
+ "$23 $T0 20 - ^ = $24 $T0 24 - ^ =\n");
+
+ // Create some modules with some stock debugging information.
+ MockCodeModules local_modules;
+ local_modules.Add(&chrome_dll);
+
+ Label frame0_esp;
+ Label frame0_ebp;
+ Label frame1_esp;
+ Label frame1_ebp;
+ Label frame2_esp;
+ Label frame2_ebp;
+ Label frame3_esp;
+ Label frame3_ebp;
+
+ stack_section.start() = 0x046bfc80;
+ stack_section
+ .D32(0)
+ .Mark(&frame0_esp)
+ .D32(0x01e235a0)
+ .D32(0x00000000)
+ .D32(0x01e9f580)
+ .D32(0x01e9f580)
+ .D32(0x00000020)
+ .D32(0x00000000)
+ .D32(0x00463674)
+ .D32(0x00000020)
+ .D32(0x00000000)
+ .D32(0x046bfcd8)
+ .D32(0x046bfcd8)
+ .D32(0x0001204b)
+ .D32(0x00000000)
+ .D32(0xfdddb523)
+ .D32(0x00000000)
+ .D32(0x00000007)
+ .D32(0x00000040)
+ .D32(0x00000000)
+ .D32(0x59631693) // chrome_59630000!malloc+0x36
+ .D32(0x01e9f580)
+ .D32(0x01e9f580)
+ .D32(0x046bfcf8)
+ .D32(0x77da6704) // ntdll!NtSetIoCompletion+0xc
+ .D32(0x046bfd4c)
+ .D32(0x59685bec) // chrome_59630000!base::MessageLoop::StartHistogrammer..
+ .D32(0x01e235a0)
+
+ .Mark(&frame0_ebp)
+ .D32(frame1_ebp) // Child EBP .D32(0x046bfd0c)
+ .D32(0x59685c2e) // Return address in
+ // chrome_59630000!base::MessagePumpWin::Run+0x3e
+ .Mark(&frame1_esp)
+ .D32(0x01e75a90)
+ .D32(0x046bfd4c)
+ .D32(0x01e75a90)
+ .D32(0x00000000)
+ .D32(0x00000300)
+ .D32(0x00000001)
+
+ .Mark(&frame1_ebp)
+ .D32(frame2_ebp) // Child EBP .D32(0x046bfd30)
+ .D32(0x59685b3c) // Return address in
+ // chrome_59630000!base::MessageLoop::RunInternal+0x73
+ .Mark(&frame2_esp)
+ .D32(0x01e75a90)
+ .D32(0x00000000)
+ .D32(0x046bfd4c)
+ .D32(0x59658123) // chrome_59630000!std::deque..
+ .D32(0x046bfda0)
+ .D32(0x01e79d70)
+ .D32(0x046bfda0)
+
+ .Mark(&frame2_ebp) // .D32(0x046bfd40)
+ .D32(0) // saved %ebp (stack end)
+ .D32(0); // saved %eip (stack end)
+
+ RegionFromSection();
+ raw_context.eip = 0x59685c46; // Context frame in
+ // base::MessagePumpForIO::DoRunLoop
+ raw_context.esp = frame0_esp.Value();
+ raw_context.ebp = frame0_ebp.Value();
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerX86 walker(&system_info, &raw_context, &stack_region,
+ &local_modules, &frame_symbolizer);
+ vector<const CodeModule*> modules_without_symbols;
+ vector<const CodeModule*> modules_with_corrupt_symbols;
+ ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
+ &modules_with_corrupt_symbols));
+ ASSERT_EQ(0U, modules_without_symbols.size());
+ ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
+ frames = call_stack.frames();
+
+ ASSERT_EQ(3U, frames->size());
+
+ { // To avoid reusing locals by mistake
+ StackFrameX86 *frame = static_cast<StackFrameX86 *>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame->trust);
+ ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame->context_validity);
+ EXPECT_EQ("base::MessagePumpForIO::DoRunLoop()", frame->function_name);
+ EXPECT_EQ(0x59685c46U, frame->instruction);
+ EXPECT_EQ(0x59685c46U, frame->context.eip);
+ EXPECT_EQ(frame0_esp.Value(), frame->context.esp);
+ EXPECT_EQ(frame0_ebp.Value(), frame->context.ebp);
+ EXPECT_EQ(&chrome_dll, frame->module);
+ ASSERT_TRUE(frame->windows_frame_info != NULL);
+ EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame->windows_frame_info->valid);
+ EXPECT_EQ(WindowsFrameInfo::STACK_INFO_FRAME_DATA,
+ frame->windows_frame_info->type_);
+ EXPECT_EQ("$T1 .raSearch = $T0 "
+ "$T1 4 - 64 @ = $ebp $T1 4 - ^ = $eip $T1 ^ = $esp $T1 4 + "
+ "= $20 $T0 56 - ^ = $23 $T0 60 - ^ = $24 $T0 64 - ^ =",
+ frame->windows_frame_info->program_string);
+ EXPECT_FALSE(frame->windows_frame_info->allocates_base_pointer);
+ }
+
+ { // To avoid reusing locals by mistake
+ StackFrameX86 *frame = static_cast<StackFrameX86 *>(frames->at(1));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame->trust);
+ ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP |
+ StackFrameX86::CONTEXT_VALID_ESP |
+ StackFrameX86::CONTEXT_VALID_EBP),
+ frame->context_validity);
+ EXPECT_EQ("base::MessagePumpWin::Run(base::MessagePump::Delegate *)",
+ frame->function_name);
+ EXPECT_EQ(1500011566U, frame->instruction + 1);
+ EXPECT_EQ(1500011566U, frame->context.eip);
+ EXPECT_EQ(frame1_esp.Value(), frame->context.esp);
+ EXPECT_EQ(frame1_ebp.Value(), frame->context.ebp);
+ EXPECT_EQ(&chrome_dll, frame->module);
+ ASSERT_TRUE(frame->windows_frame_info != NULL);
+ EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame->windows_frame_info->valid);
+ EXPECT_EQ(WindowsFrameInfo::STACK_INFO_FRAME_DATA,
+ frame->windows_frame_info->type_);
+ EXPECT_EQ("$T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + =",
+ frame->windows_frame_info->program_string);
+ EXPECT_FALSE(frame->windows_frame_info->allocates_base_pointer);
+ }
+
+ { // To avoid reusing locals by mistake
+ StackFrameX86 *frame = static_cast<StackFrameX86 *>(frames->at(2));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame->trust);
+ ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP |
+ StackFrameX86::CONTEXT_VALID_ESP |
+ StackFrameX86::CONTEXT_VALID_EBP),
+ frame->context_validity);
+ EXPECT_EQ("base::MessageLoop::RunInternal()", frame->function_name);
+ EXPECT_EQ(1500011324U, frame->instruction + 1);
+ EXPECT_EQ(1500011324U, frame->context.eip);
+ EXPECT_EQ(frame2_esp.Value(), frame->context.esp);
+ EXPECT_EQ(frame2_ebp.Value(), frame->context.ebp);
+ EXPECT_EQ(&chrome_dll, frame->module);
+ ASSERT_TRUE(frame->windows_frame_info != NULL);
+ EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame->windows_frame_info->valid);
+ EXPECT_EQ(WindowsFrameInfo::STACK_INFO_FRAME_DATA,
+ frame->windows_frame_info->type_);
+ EXPECT_EQ("$T1 .raSearch = $T0 "
+ "$T1 4 - 8 @ = $ebp $T1 4 - ^ = $eip $T1 ^ = $esp $T1 4 + = "
+ "$23 $T0 20 - ^ = $24 $T0 24 - ^ =",
+ frame->windows_frame_info->program_string);
+ EXPECT_FALSE(frame->windows_frame_info->allocates_base_pointer);
+ }
+}
+
+// Scan the stack for a return address and potentially skip frames when the
+// current IP address is not in a known module. Note, that that the span of
+// this scan is limited to 120 search words for the context frame and 30
+// search words (pointers) for the other frames:
+// const int kRASearchWords = 30;
+void GetCallerFrame::IPAddressIsNotInKnownModuleTestImpl(
+ bool has_corrupt_symbols) {
+ MockCodeModule remoting_core_dll(0x54080000, 0x501000, "remoting_core.dll",
+ "version1");
+ string symbols_func_section =
+ "FUNC 137214 17d 10 PK11_Verify\n"
+ "FUNC 15c834 37 14 nsc_ECDSAVerifyStub\n"
+ "FUNC 1611d3 91 14 NSC_Verify\n"
+ "FUNC 162ff7 60 4 sftk_SessionFromHandle\n";
+ string symbols_stack_section =
+ "STACK WIN 4 137214 17d 9 0 10 0 10 0 1 $T0 $ebp = "
+ "$eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + =\n"
+ "STACK WIN 4 15c834 37 6 0 14 0 18 0 1 $T0 $ebp = "
+ "$eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + =\n"
+ "STACK WIN 4 1611d3 91 7 0 14 0 8 0 1 $T0 $ebp = "
+ "$eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + =\n"
+ "STACK WIN 4 162ff7 60 5 0 4 0 0 0 1 $T0 $ebp = "
+ "$eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + =\n";
+
+ string symbols = symbols_func_section;
+ if (has_corrupt_symbols) {
+ symbols.append(string(1, '\0')); // null terminator in the middle
+ symbols.append("\n");
+ symbols.append("FUNC 1234\n" // invalid FUNC records
+ "FUNNC 1234\n"
+ "STACK WIN 4 1234 234 23 " // invalid STACK record
+ "23423423 234 23 234 234 "
+ "234 23 234 23 234 234 "
+ "234 234 234\n");
+ }
+ symbols.append(symbols_stack_section);
+ SetModuleSymbols(&remoting_core_dll, symbols);
+
+ // Create some modules with some stock debugging information.
+ MockCodeModules local_modules;
+ local_modules.Add(&remoting_core_dll);
+
+ Label frame0_esp;
+ Label frame0_ebp;
+ Label frame1_ebp;
+ Label frame1_esp;
+ Label frame2_ebp;
+ Label frame2_esp;
+ Label frame3_ebp;
+ Label frame3_esp;
+ Label bogus_stack_location_1;
+ Label bogus_stack_location_2;
+ Label bogus_stack_location_3;
+
+ stack_section.start() = 0x01a3ea28;
+ stack_section
+ .Mark(&frame0_esp)
+ .D32(bogus_stack_location_2)
+ .D32(bogus_stack_location_1)
+ .D32(0x042478e4)
+ .D32(bogus_stack_location_2)
+ .D32(0x00000000)
+ .D32(0x041f0420)
+ .D32(0x00000000)
+ .D32(0x00000000)
+ .D32(0x00000040)
+ .D32(0x00000001)
+ .D32(0x00b7e0d0)
+ .D32(0x00000000)
+ .D32(0x00000040)
+ .D32(0x00000001)
+ .D32(0x00b7f570)
+ .Mark(&bogus_stack_location_1)
+ .D32(0x00000000)
+ .D32(0x00000040)
+ .D32(0x00000008)
+ .D32(0x04289530)
+ .D32(0x00000000)
+ .D32(0x00000040)
+ .D32(0x00000008)
+ .D32(0x00b7e910)
+ .D32(0x00000000)
+ .D32(0x00000040)
+ .D32(0x00000008)
+ .D32(0x00b7d998)
+ .D32(0x00000000)
+ .D32(0x00000040)
+ .D32(0x00000008)
+ .D32(0x00b7dec0)
+ .Mark(&bogus_stack_location_2)
+ .D32(0x00000000)
+ .D32(0x00000040)
+ .D32(0x00000008)
+ .D32(0x04289428)
+ .D32(0x00000000)
+ .D32(0x00000040)
+ .D32(0x00000008)
+ .D32(0x00b7f258)
+ .Mark(&bogus_stack_location_3)
+ .D32(0x00000000)
+ .D32(0x041f3560)
+ .D32(0x00000041)
+ .D32(0x00000020)
+ .D32(0xffffffff)
+ .Mark(&frame0_ebp)
+ .D32(frame1_ebp) // Child %ebp
+ .D32(0x541dc866) // return address of frame 0
+ // inside remoting_core!nsc_ECDSAVerifyStub+0x32
+ .Mark(&frame1_esp)
+ .D32(0x04247860)
+ .D32(0x01a3eaec)
+ .D32(0x01a3eaf8)
+ .D32(0x541e304f) // remoting_core!sftk_SessionFromHandle+0x58
+ .D32(0x0404c620)
+ .D32(0x00000040)
+ .D32(0x01a3eb2c)
+ .D32(0x01a3ec08)
+ .D32(0x00000014)
+ .Mark(&frame1_ebp)
+ .D32(frame2_ebp) // Child %ebp
+ .D32(0x541e1234) // return address of frame 1
+ // inside remoting_core!NSC_Verify+0x61
+ .Mark(&frame2_esp)
+ .D32(0x04247858)
+ .D32(0x0404c620)
+ .D32(0x00000040)
+ .D32(0x01a3ec08)
+ .D32(0x00000014)
+ .D32(0x01000005)
+ .D32(0x00b2f7a0)
+ .D32(0x041f0420)
+ .D32(0x041f3650)
+ .Mark(&frame2_ebp)
+ .D32(frame3_ebp) // Child %ebp
+ .D32(0x541b734d) // return address of frame 1
+ // inside remoting_core!PK11_Verify+0x139
+ .Mark(&frame3_esp)
+ .D32(0x01000005)
+ .D32(0x01a3ec08)
+ .D32(0x00000014)
+ .D32(0x0404c620)
+ .D32(0x00000040)
+ .D32(0x04073e00)
+ .D32(0x04073e00)
+ .D32(0x04247050)
+ .D32(0x00001041)
+ .D32(0x00000000)
+ .D32(0x00000000)
+ .D32(0x00000000)
+ .Mark(&frame3_ebp)
+ .D32(0) // saved %ebp (stack end)
+ .D32(0); // saved %eip (stack end)
+
+ RegionFromSection();
+ raw_context.eip = 0x4247860; // IP address not in known module
+ raw_context.ebp = 0x5420362d; // bogus
+ raw_context.esp = frame0_esp.Value();
+
+ // sanity
+ ASSERT_TRUE(raw_context.esp == stack_section.start().Value());
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerX86 walker(&system_info, &raw_context, &stack_region,
+ &local_modules, &frame_symbolizer);
+ vector<const CodeModule*> modules_without_symbols;
+ vector<const CodeModule*> modules_with_corrupt_symbols;
+ ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
+ &modules_with_corrupt_symbols));
+ ASSERT_EQ(0U, modules_without_symbols.size());
+ if (has_corrupt_symbols) {
+ ASSERT_EQ(1U, modules_with_corrupt_symbols.size());
+ ASSERT_EQ("remoting_core.dll",
+ modules_with_corrupt_symbols[0]->debug_file());
+ } else {
+ ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
+ }
+ 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(raw_context.eip, frame0->context.eip);
+ EXPECT_EQ(raw_context.ebp, frame0->context.ebp);
+ EXPECT_EQ(raw_context.esp, frame0->context.esp);
+ EXPECT_EQ(NULL, frame0->module); // IP not in known module
+ EXPECT_EQ("", frame0->function_name);
+ ASSERT_EQ(NULL, frame0->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);
+ ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP |
+ StackFrameX86::CONTEXT_VALID_ESP |
+ StackFrameX86::CONTEXT_VALID_EBP),
+ frame1->context_validity);
+ EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp);
+ EXPECT_EQ(frame1_esp.Value(), frame1->context.esp);
+ EXPECT_EQ(&remoting_core_dll, frame1->module);
+ EXPECT_EQ("nsc_ECDSAVerifyStub", frame1->function_name);
+ 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 + =",
+ frame1->windows_frame_info->program_string);
+ EXPECT_FALSE(frame1->windows_frame_info->allocates_base_pointer);
+ }
+
+ { // 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),
+ frame2->context_validity);
+ EXPECT_EQ(frame2_ebp.Value(), frame2->context.ebp);
+ EXPECT_EQ(frame2_esp.Value(), frame2->context.esp);
+ EXPECT_EQ(&remoting_core_dll, frame2->module);
+ EXPECT_EQ("NSC_Verify", frame2->function_name);
+ ASSERT_TRUE(frame2->windows_frame_info != NULL);
+ EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame2->windows_frame_info->valid);
+ EXPECT_EQ(WindowsFrameInfo::STACK_INFO_FRAME_DATA,
+ frame2->windows_frame_info->type_);
+ EXPECT_EQ("$T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + =",
+ frame2->windows_frame_info->program_string);
+ EXPECT_FALSE(frame2->windows_frame_info->allocates_base_pointer);
+ }
+
+ { // To avoid reusing locals by mistake
+ StackFrameX86 *frame3 = static_cast<StackFrameX86 *>(frames->at(3));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame3->trust);
+ ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP |
+ StackFrameX86::CONTEXT_VALID_ESP |
+ StackFrameX86::CONTEXT_VALID_EBP),
+ frame3->context_validity);
+ EXPECT_EQ(frame3_ebp.Value(), frame3->context.ebp);
+ EXPECT_EQ(frame3_esp.Value(), frame3->context.esp);
+ EXPECT_EQ(&remoting_core_dll, frame3->module);
+ EXPECT_EQ("PK11_Verify", frame3->function_name);
+ ASSERT_TRUE(frame3->windows_frame_info != NULL);
+ EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame3->windows_frame_info->valid);
+ EXPECT_EQ(WindowsFrameInfo::STACK_INFO_FRAME_DATA,
+ frame3->windows_frame_info->type_);
+ EXPECT_EQ("$T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + =",
+ frame3->windows_frame_info->program_string);
+ EXPECT_FALSE(frame3->windows_frame_info->allocates_base_pointer);
+ }
+}
+
+// Runs IPAddressIsNotInKnownModule test with good symbols
+TEST_F(GetCallerFrame, IPAddressIsNotInKnownModule) {
+ IPAddressIsNotInKnownModuleTestImpl(false /* has_corrupt_modules */);
+}
+
+// Runs IPAddressIsNotInKnownModule test with corrupt symbols
+TEST_F(GetCallerFrame, IPAddressIsNotInKnownModule_CorruptSymbols) {
+ IPAddressIsNotInKnownModuleTestImpl(true /* has_corrupt_modules */);
+}
+
+struct CFIFixture: public StackwalkerX86Fixture {
+ CFIFixture() {
+ // Provide a bunch of STACK CFI records; individual tests walk to the
+ // caller from every point in this series, expecting to find the same
+ // set of register values.
+ SetModuleSymbols(&module1,
+ // The youngest frame's function.
+ "FUNC 4000 1000 10 enchiridion\n"
+ // Initially, just a return address.
+ "STACK CFI INIT 4000 100 .cfa: $esp 4 + .ra: .cfa 4 - ^\n"
+ // Push %ebx.
+ "STACK CFI 4001 .cfa: $esp 8 + $ebx: .cfa 8 - ^\n"
+ // Move %esi into %ebx. Weird, but permitted.
+ "STACK CFI 4002 $esi: $ebx\n"
+ // Allocate frame space, and save %edi.
+ "STACK CFI 4003 .cfa: $esp 20 + $edi: .cfa 16 - ^\n"
+ // Put the return address in %edi.
+ "STACK CFI 4005 .ra: $edi\n"
+ // Save %ebp, and use it as a frame pointer.
+ "STACK CFI 4006 .cfa: $ebp 8 + $ebp: .cfa 12 - ^\n"
+
+ // The calling function.
+ "FUNC 5000 1000 10 epictetus\n"
+ // Mark it as end of stack.
+ "STACK CFI INIT 5000 1000 .cfa: $esp .ra 0\n");
+
+ // Provide some distinctive values for the caller's registers.
+ expected.esp = 0x80000000;
+ expected.eip = 0x40005510;
+ expected.ebp = 0xc0d4aab9;
+ expected.ebx = 0x60f20ce6;
+ expected.esi = 0x53d1379d;
+ expected.edi = 0xafbae234;
+
+ // By default, registers are unchanged.
+ raw_context = expected;
+ }
+
+ // Walk the stack, using stack_section as the contents of the stack
+ // and raw_context as the current register values. (Set
+ // raw_context.esp to the stack's starting address.) Expect two
+ // stack frames; in the older frame, expect the callee-saves
+ // registers to have values matching those in 'expected'.
+ void CheckWalk() {
+ RegionFromSection();
+ raw_context.esp = stack_section.start().Value();
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules,
+ &frame_symbolizer);
+ vector<const CodeModule*> modules_without_symbols;
+ vector<const CodeModule*> modules_with_corrupt_symbols;
+ ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
+ &modules_with_corrupt_symbols));
+ ASSERT_EQ(0U, modules_without_symbols.size());
+ ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
+ 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("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 *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.
+ MDRawContextX86 expected;
+};
+
+class CFI: public CFIFixture, public Test { };
+
+TEST_F(CFI, At4000) {
+ Label frame1_esp = expected.esp;
+ stack_section
+ .D32(0x40005510) // return address
+ .Mark(&frame1_esp); // This effectively sets stack_section.start().
+ raw_context.eip = 0x40004000;
+ CheckWalk();
+}
+
+TEST_F(CFI, At4001) {
+ Label frame1_esp = expected.esp;
+ stack_section
+ .D32(0x60f20ce6) // saved %ebx
+ .D32(0x40005510) // return address
+ .Mark(&frame1_esp); // This effectively sets stack_section.start().
+ raw_context.eip = 0x40004001;
+ raw_context.ebx = 0x91aa9a8b; // callee's %ebx value
+ CheckWalk();
+}
+
+TEST_F(CFI, At4002) {
+ Label frame1_esp = expected.esp;
+ stack_section
+ .D32(0x60f20ce6) // saved %ebx
+ .D32(0x40005510) // return address
+ .Mark(&frame1_esp); // This effectively sets stack_section.start().
+ raw_context.eip = 0x40004002;
+ raw_context.ebx = 0x53d1379d; // saved %esi
+ raw_context.esi = 0xa5c790ed; // callee's %esi value
+ CheckWalk();
+}
+
+TEST_F(CFI, At4003) {
+ Label frame1_esp = expected.esp;
+ stack_section
+ .D32(0x56ec3db7) // garbage
+ .D32(0xafbae234) // saved %edi
+ .D32(0x53d67131) // garbage
+ .D32(0x60f20ce6) // saved %ebx
+ .D32(0x40005510) // return address
+ .Mark(&frame1_esp); // This effectively sets stack_section.start().
+ raw_context.eip = 0x40004003;
+ raw_context.ebx = 0x53d1379d; // saved %esi
+ raw_context.esi = 0xa97f229d; // callee's %esi
+ raw_context.edi = 0xb05cc997; // callee's %edi
+ CheckWalk();
+}
+
+// The results here should be the same as those at module offset
+// 0x4003.
+TEST_F(CFI, At4004) {
+ Label frame1_esp = expected.esp;
+ stack_section
+ .D32(0xe29782c2) // garbage
+ .D32(0xafbae234) // saved %edi
+ .D32(0x5ba29ce9) // garbage
+ .D32(0x60f20ce6) // saved %ebx
+ .D32(0x40005510) // return address
+ .Mark(&frame1_esp); // This effectively sets stack_section.start().
+ raw_context.eip = 0x40004004;
+ raw_context.ebx = 0x53d1379d; // saved %esi
+ raw_context.esi = 0x0fb7dc4e; // callee's %esi
+ raw_context.edi = 0x993b4280; // callee's %edi
+ CheckWalk();
+}
+
+TEST_F(CFI, At4005) {
+ Label frame1_esp = expected.esp;
+ stack_section
+ .D32(0xe29782c2) // garbage
+ .D32(0xafbae234) // saved %edi
+ .D32(0x5ba29ce9) // garbage
+ .D32(0x60f20ce6) // saved %ebx
+ .D32(0x8036cc02) // garbage
+ .Mark(&frame1_esp); // This effectively sets stack_section.start().
+ raw_context.eip = 0x40004005;
+ raw_context.ebx = 0x53d1379d; // saved %esi
+ raw_context.esi = 0x0fb7dc4e; // callee's %esi
+ raw_context.edi = 0x40005510; // return address
+ CheckWalk();
+}
+
+TEST_F(CFI, At4006) {
+ Label frame0_ebp;
+ Label frame1_esp = expected.esp;
+ stack_section
+ .D32(0xdcdd25cd) // garbage
+ .D32(0xafbae234) // saved %edi
+ .D32(0xc0d4aab9) // saved %ebp
+ .Mark(&frame0_ebp) // frame pointer points here
+ .D32(0x60f20ce6) // saved %ebx
+ .D32(0x8036cc02) // garbage
+ .Mark(&frame1_esp); // This effectively sets stack_section.start().
+ raw_context.eip = 0x40004006;
+ raw_context.ebp = frame0_ebp.Value();
+ raw_context.ebx = 0x53d1379d; // saved %esi
+ raw_context.esi = 0x743833c9; // callee's %esi
+ raw_context.edi = 0x40005510; // return address
+ CheckWalk();
+}
+