Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | // Copyright (c) 2010, Google Inc. |
michael@0 | 2 | // All rights reserved. |
michael@0 | 3 | // |
michael@0 | 4 | // Redistribution and use in source and binary forms, with or without |
michael@0 | 5 | // modification, are permitted provided that the following conditions are |
michael@0 | 6 | // met: |
michael@0 | 7 | // |
michael@0 | 8 | // * Redistributions of source code must retain the above copyright |
michael@0 | 9 | // notice, this list of conditions and the following disclaimer. |
michael@0 | 10 | // * Redistributions in binary form must reproduce the above |
michael@0 | 11 | // copyright notice, this list of conditions and the following disclaimer |
michael@0 | 12 | // in the documentation and/or other materials provided with the |
michael@0 | 13 | // distribution. |
michael@0 | 14 | // * Neither the name of Google Inc. nor the names of its |
michael@0 | 15 | // contributors may be used to endorse or promote products derived from |
michael@0 | 16 | // this software without specific prior written permission. |
michael@0 | 17 | // |
michael@0 | 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
michael@0 | 19 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
michael@0 | 20 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
michael@0 | 21 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
michael@0 | 22 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
michael@0 | 23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
michael@0 | 24 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
michael@0 | 25 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
michael@0 | 26 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
michael@0 | 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
michael@0 | 28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
michael@0 | 29 | |
michael@0 | 30 | // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> |
michael@0 | 31 | |
michael@0 | 32 | // stackwalker_x86_unittest.cc: Unit tests for StackwalkerX86 class. |
michael@0 | 33 | |
michael@0 | 34 | #include <string> |
michael@0 | 35 | #include <vector> |
michael@0 | 36 | |
michael@0 | 37 | #include "breakpad_googletest_includes.h" |
michael@0 | 38 | #include "common/test_assembler.h" |
michael@0 | 39 | #include "common/using_std_string.h" |
michael@0 | 40 | #include "google_breakpad/common/minidump_format.h" |
michael@0 | 41 | #include "google_breakpad/processor/basic_source_line_resolver.h" |
michael@0 | 42 | #include "google_breakpad/processor/call_stack.h" |
michael@0 | 43 | #include "google_breakpad/processor/code_module.h" |
michael@0 | 44 | #include "google_breakpad/processor/source_line_resolver_interface.h" |
michael@0 | 45 | #include "google_breakpad/processor/stack_frame_cpu.h" |
michael@0 | 46 | #include "processor/stackwalker_unittest_utils.h" |
michael@0 | 47 | #include "processor/stackwalker_x86.h" |
michael@0 | 48 | #include "processor/windows_frame_info.h" |
michael@0 | 49 | |
michael@0 | 50 | using google_breakpad::BasicSourceLineResolver; |
michael@0 | 51 | using google_breakpad::CallStack; |
michael@0 | 52 | using google_breakpad::CodeModule; |
michael@0 | 53 | using google_breakpad::StackFrameSymbolizer; |
michael@0 | 54 | using google_breakpad::StackFrame; |
michael@0 | 55 | using google_breakpad::StackFrameX86; |
michael@0 | 56 | using google_breakpad::StackwalkerX86; |
michael@0 | 57 | using google_breakpad::SystemInfo; |
michael@0 | 58 | using google_breakpad::WindowsFrameInfo; |
michael@0 | 59 | using google_breakpad::test_assembler::kLittleEndian; |
michael@0 | 60 | using google_breakpad::test_assembler::Label; |
michael@0 | 61 | using google_breakpad::test_assembler::Section; |
michael@0 | 62 | using std::vector; |
michael@0 | 63 | using testing::_; |
michael@0 | 64 | using testing::Return; |
michael@0 | 65 | using testing::SetArgumentPointee; |
michael@0 | 66 | using testing::Test; |
michael@0 | 67 | |
michael@0 | 68 | class StackwalkerX86Fixture { |
michael@0 | 69 | public: |
michael@0 | 70 | StackwalkerX86Fixture() |
michael@0 | 71 | : stack_section(kLittleEndian), |
michael@0 | 72 | // Give the two modules reasonable standard locations and names |
michael@0 | 73 | // for tests to play with. |
michael@0 | 74 | module1(0x40000000, 0x10000, "module1", "version1"), |
michael@0 | 75 | module2(0x50000000, 0x10000, "module2", "version2"), |
michael@0 | 76 | module3(0x771d0000, 0x180000, "module3", "version3"), |
michael@0 | 77 | module4(0x75f90000, 0x46000, "module4", "version4"), |
michael@0 | 78 | module5(0x75730000, 0x110000, "module5", "version5"), |
michael@0 | 79 | module6(0x647f0000, 0x1ba8000, "module6", "version6") { |
michael@0 | 80 | // Identify the system as a Linux system. |
michael@0 | 81 | system_info.os = "Linux"; |
michael@0 | 82 | system_info.os_short = "linux"; |
michael@0 | 83 | system_info.os_version = "Salacious Skink"; |
michael@0 | 84 | system_info.cpu = "x86"; |
michael@0 | 85 | system_info.cpu_info = ""; |
michael@0 | 86 | |
michael@0 | 87 | // Put distinctive values in the raw CPU context. |
michael@0 | 88 | BrandContext(&raw_context); |
michael@0 | 89 | |
michael@0 | 90 | // Create some modules with some stock debugging information. |
michael@0 | 91 | modules.Add(&module1); |
michael@0 | 92 | modules.Add(&module2); |
michael@0 | 93 | modules.Add(&module3); |
michael@0 | 94 | modules.Add(&module4); |
michael@0 | 95 | modules.Add(&module5); |
michael@0 | 96 | modules.Add(&module6); |
michael@0 | 97 | |
michael@0 | 98 | // By default, none of the modules have symbol info; call |
michael@0 | 99 | // SetModuleSymbols to override this. |
michael@0 | 100 | EXPECT_CALL(supplier, GetCStringSymbolData(_, _, _, _)) |
michael@0 | 101 | .WillRepeatedly(Return(MockSymbolSupplier::NOT_FOUND)); |
michael@0 | 102 | } |
michael@0 | 103 | |
michael@0 | 104 | // Set the Breakpad symbol information that supplier should return for |
michael@0 | 105 | // MODULE to INFO. |
michael@0 | 106 | void SetModuleSymbols(MockCodeModule *module, const string &info) { |
michael@0 | 107 | char *buffer = supplier.CopySymbolDataAndOwnTheCopy(info); |
michael@0 | 108 | EXPECT_CALL(supplier, GetCStringSymbolData(module, &system_info, _, _)) |
michael@0 | 109 | .WillRepeatedly(DoAll(SetArgumentPointee<3>(buffer), |
michael@0 | 110 | Return(MockSymbolSupplier::FOUND))); |
michael@0 | 111 | } |
michael@0 | 112 | |
michael@0 | 113 | // Populate stack_region with the contents of stack_section. Use |
michael@0 | 114 | // stack_section.start() as the region's starting address. |
michael@0 | 115 | void RegionFromSection() { |
michael@0 | 116 | string contents; |
michael@0 | 117 | ASSERT_TRUE(stack_section.GetContents(&contents)); |
michael@0 | 118 | stack_region.Init(stack_section.start().Value(), contents); |
michael@0 | 119 | } |
michael@0 | 120 | |
michael@0 | 121 | // Fill RAW_CONTEXT with pseudo-random data, for round-trip checking. |
michael@0 | 122 | void BrandContext(MDRawContextX86 *raw_context) { |
michael@0 | 123 | uint8_t x = 173; |
michael@0 | 124 | for (size_t i = 0; i < sizeof(*raw_context); i++) |
michael@0 | 125 | reinterpret_cast<uint8_t *>(raw_context)[i] = (x += 17); |
michael@0 | 126 | } |
michael@0 | 127 | |
michael@0 | 128 | SystemInfo system_info; |
michael@0 | 129 | MDRawContextX86 raw_context; |
michael@0 | 130 | Section stack_section; |
michael@0 | 131 | MockMemoryRegion stack_region; |
michael@0 | 132 | MockCodeModule module1; |
michael@0 | 133 | MockCodeModule module2; |
michael@0 | 134 | MockCodeModule module3; |
michael@0 | 135 | MockCodeModule module4; |
michael@0 | 136 | MockCodeModule module5; |
michael@0 | 137 | MockCodeModule module6; |
michael@0 | 138 | MockCodeModules modules; |
michael@0 | 139 | MockSymbolSupplier supplier; |
michael@0 | 140 | BasicSourceLineResolver resolver; |
michael@0 | 141 | CallStack call_stack; |
michael@0 | 142 | const vector<StackFrame *> *frames; |
michael@0 | 143 | }; |
michael@0 | 144 | |
michael@0 | 145 | class SanityCheck: public StackwalkerX86Fixture, public Test { }; |
michael@0 | 146 | |
michael@0 | 147 | TEST_F(SanityCheck, NoResolver) { |
michael@0 | 148 | stack_section.start() = 0x80000000; |
michael@0 | 149 | stack_section.D32(0).D32(0); // end-of-stack marker |
michael@0 | 150 | RegionFromSection(); |
michael@0 | 151 | raw_context.eip = 0x40000200; |
michael@0 | 152 | raw_context.ebp = 0x80000000; |
michael@0 | 153 | |
michael@0 | 154 | StackFrameSymbolizer frame_symbolizer(NULL, NULL); |
michael@0 | 155 | StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules, |
michael@0 | 156 | &frame_symbolizer); |
michael@0 | 157 | // This should succeed, even without a resolver or supplier. |
michael@0 | 158 | vector<const CodeModule*> modules_without_symbols; |
michael@0 | 159 | ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols)); |
michael@0 | 160 | ASSERT_EQ(1U, modules_without_symbols.size()); |
michael@0 | 161 | ASSERT_EQ("module1", modules_without_symbols[0]->debug_file()); |
michael@0 | 162 | frames = call_stack.frames(); |
michael@0 | 163 | StackFrameX86 *frame = static_cast<StackFrameX86 *>(frames->at(0)); |
michael@0 | 164 | // Check that the values from the original raw context made it |
michael@0 | 165 | // through to the context in the stack frame. |
michael@0 | 166 | EXPECT_EQ(0, memcmp(&raw_context, &frame->context, sizeof(raw_context))); |
michael@0 | 167 | } |
michael@0 | 168 | |
michael@0 | 169 | class GetContextFrame: public StackwalkerX86Fixture, public Test { }; |
michael@0 | 170 | |
michael@0 | 171 | TEST_F(GetContextFrame, Simple) { |
michael@0 | 172 | stack_section.start() = 0x80000000; |
michael@0 | 173 | stack_section.D32(0).D32(0); // end-of-stack marker |
michael@0 | 174 | RegionFromSection(); |
michael@0 | 175 | raw_context.eip = 0x40000200; |
michael@0 | 176 | raw_context.ebp = 0x80000000; |
michael@0 | 177 | |
michael@0 | 178 | StackFrameSymbolizer frame_symbolizer(&supplier, &resolver); |
michael@0 | 179 | StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules, |
michael@0 | 180 | &frame_symbolizer); |
michael@0 | 181 | vector<const CodeModule*> modules_without_symbols; |
michael@0 | 182 | ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols)); |
michael@0 | 183 | ASSERT_EQ(1U, modules_without_symbols.size()); |
michael@0 | 184 | ASSERT_EQ("module1", modules_without_symbols[0]->debug_file()); |
michael@0 | 185 | frames = call_stack.frames(); |
michael@0 | 186 | StackFrameX86 *frame = static_cast<StackFrameX86 *>(frames->at(0)); |
michael@0 | 187 | // Check that the values from the original raw context made it |
michael@0 | 188 | // through to the context in the stack frame. |
michael@0 | 189 | EXPECT_EQ(0, memcmp(&raw_context, &frame->context, sizeof(raw_context))); |
michael@0 | 190 | } |
michael@0 | 191 | |
michael@0 | 192 | // The stackwalker should be able to produce the context frame even |
michael@0 | 193 | // without stack memory present. |
michael@0 | 194 | TEST_F(GetContextFrame, NoStackMemory) { |
michael@0 | 195 | raw_context.eip = 0x40000200; |
michael@0 | 196 | raw_context.ebp = 0x80000000; |
michael@0 | 197 | |
michael@0 | 198 | StackFrameSymbolizer frame_symbolizer(&supplier, &resolver); |
michael@0 | 199 | StackwalkerX86 walker(&system_info, &raw_context, NULL, &modules, |
michael@0 | 200 | &frame_symbolizer); |
michael@0 | 201 | vector<const CodeModule*> modules_without_symbols; |
michael@0 | 202 | ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols)); |
michael@0 | 203 | ASSERT_EQ(1U, modules_without_symbols.size()); |
michael@0 | 204 | ASSERT_EQ("module1", modules_without_symbols[0]->debug_file()); |
michael@0 | 205 | frames = call_stack.frames(); |
michael@0 | 206 | StackFrameX86 *frame = static_cast<StackFrameX86 *>(frames->at(0)); |
michael@0 | 207 | // Check that the values from the original raw context made it |
michael@0 | 208 | // through to the context in the stack frame. |
michael@0 | 209 | EXPECT_EQ(0, memcmp(&raw_context, &frame->context, sizeof(raw_context))); |
michael@0 | 210 | } |
michael@0 | 211 | |
michael@0 | 212 | class GetCallerFrame: public StackwalkerX86Fixture, public Test { }; |
michael@0 | 213 | |
michael@0 | 214 | // Walk a traditional frame. A traditional frame saves the caller's |
michael@0 | 215 | // %ebp just below the return address, and has its own %ebp pointing |
michael@0 | 216 | // at the saved %ebp. |
michael@0 | 217 | TEST_F(GetCallerFrame, Traditional) { |
michael@0 | 218 | stack_section.start() = 0x80000000; |
michael@0 | 219 | Label frame0_ebp, frame1_ebp; |
michael@0 | 220 | stack_section |
michael@0 | 221 | .Append(12, 0) // frame 0: space |
michael@0 | 222 | .Mark(&frame0_ebp) // frame 0 %ebp points here |
michael@0 | 223 | .D32(frame1_ebp) // frame 0: saved %ebp |
michael@0 | 224 | .D32(0x40008679) // frame 0: return address |
michael@0 | 225 | .Append(8, 0) // frame 1: space |
michael@0 | 226 | .Mark(&frame1_ebp) // frame 1 %ebp points here |
michael@0 | 227 | .D32(0) // frame 1: saved %ebp (stack end) |
michael@0 | 228 | .D32(0); // frame 1: return address (stack end) |
michael@0 | 229 | RegionFromSection(); |
michael@0 | 230 | raw_context.eip = 0x4000c7a5; |
michael@0 | 231 | raw_context.esp = stack_section.start().Value(); |
michael@0 | 232 | raw_context.ebp = frame0_ebp.Value(); |
michael@0 | 233 | |
michael@0 | 234 | StackFrameSymbolizer frame_symbolizer(&supplier, &resolver); |
michael@0 | 235 | StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules, |
michael@0 | 236 | &frame_symbolizer); |
michael@0 | 237 | vector<const CodeModule*> modules_without_symbols; |
michael@0 | 238 | ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols)); |
michael@0 | 239 | ASSERT_EQ(1U, modules_without_symbols.size()); |
michael@0 | 240 | ASSERT_EQ("module1", modules_without_symbols[0]->debug_file()); |
michael@0 | 241 | frames = call_stack.frames(); |
michael@0 | 242 | ASSERT_EQ(2U, frames->size()); |
michael@0 | 243 | |
michael@0 | 244 | { // To avoid reusing locals by mistake |
michael@0 | 245 | StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0)); |
michael@0 | 246 | EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); |
michael@0 | 247 | EXPECT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); |
michael@0 | 248 | EXPECT_EQ(0x4000c7a5U, frame0->instruction); |
michael@0 | 249 | EXPECT_EQ(0x4000c7a5U, frame0->context.eip); |
michael@0 | 250 | EXPECT_EQ(frame0_ebp.Value(), frame0->context.ebp); |
michael@0 | 251 | EXPECT_EQ(NULL, frame0->windows_frame_info); |
michael@0 | 252 | } |
michael@0 | 253 | |
michael@0 | 254 | { // To avoid reusing locals by mistake |
michael@0 | 255 | StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1)); |
michael@0 | 256 | EXPECT_EQ(StackFrame::FRAME_TRUST_FP, frame1->trust); |
michael@0 | 257 | ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP |
michael@0 | 258 | | StackFrameX86::CONTEXT_VALID_ESP |
michael@0 | 259 | | StackFrameX86::CONTEXT_VALID_EBP), |
michael@0 | 260 | frame1->context_validity); |
michael@0 | 261 | EXPECT_EQ(0x40008679U, frame1->instruction + 1); |
michael@0 | 262 | EXPECT_EQ(0x40008679U, frame1->context.eip); |
michael@0 | 263 | EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp); |
michael@0 | 264 | EXPECT_EQ(NULL, frame1->windows_frame_info); |
michael@0 | 265 | } |
michael@0 | 266 | } |
michael@0 | 267 | |
michael@0 | 268 | // Walk a traditional frame, but use a bogus %ebp value, forcing a scan |
michael@0 | 269 | // of the stack for something that looks like a return address. |
michael@0 | 270 | TEST_F(GetCallerFrame, TraditionalScan) { |
michael@0 | 271 | stack_section.start() = 0x80000000; |
michael@0 | 272 | Label frame1_ebp; |
michael@0 | 273 | stack_section |
michael@0 | 274 | // frame 0 |
michael@0 | 275 | .D32(0xf065dc76) // locals area: |
michael@0 | 276 | .D32(0x46ee2167) // garbage that doesn't look like |
michael@0 | 277 | .D32(0xbab023ec) // a return address |
michael@0 | 278 | .D32(frame1_ebp) // saved %ebp (%ebp fails to point here, forcing scan) |
michael@0 | 279 | .D32(0x4000129d) // return address |
michael@0 | 280 | // frame 1 |
michael@0 | 281 | .Append(8, 0) // space |
michael@0 | 282 | .Mark(&frame1_ebp) // %ebp points here |
michael@0 | 283 | .D32(0) // saved %ebp (stack end) |
michael@0 | 284 | .D32(0); // return address (stack end) |
michael@0 | 285 | |
michael@0 | 286 | RegionFromSection(); |
michael@0 | 287 | raw_context.eip = 0x4000f49d; |
michael@0 | 288 | raw_context.esp = stack_section.start().Value(); |
michael@0 | 289 | // Make the frame pointer bogus, to make the stackwalker scan the stack |
michael@0 | 290 | // for something that looks like a return address. |
michael@0 | 291 | raw_context.ebp = 0xd43eed6e; |
michael@0 | 292 | |
michael@0 | 293 | StackFrameSymbolizer frame_symbolizer(&supplier, &resolver); |
michael@0 | 294 | StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules, |
michael@0 | 295 | &frame_symbolizer); |
michael@0 | 296 | vector<const CodeModule*> modules_without_symbols; |
michael@0 | 297 | ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols)); |
michael@0 | 298 | ASSERT_EQ(1U, modules_without_symbols.size()); |
michael@0 | 299 | ASSERT_EQ("module1", modules_without_symbols[0]->debug_file()); |
michael@0 | 300 | frames = call_stack.frames(); |
michael@0 | 301 | ASSERT_EQ(2U, frames->size()); |
michael@0 | 302 | |
michael@0 | 303 | { // To avoid reusing locals by mistake |
michael@0 | 304 | StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0)); |
michael@0 | 305 | EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); |
michael@0 | 306 | ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); |
michael@0 | 307 | EXPECT_EQ(0x4000f49dU, frame0->instruction); |
michael@0 | 308 | EXPECT_EQ(0x4000f49dU, frame0->context.eip); |
michael@0 | 309 | EXPECT_EQ(stack_section.start().Value(), frame0->context.esp); |
michael@0 | 310 | EXPECT_EQ(0xd43eed6eU, frame0->context.ebp); |
michael@0 | 311 | EXPECT_EQ(NULL, frame0->windows_frame_info); |
michael@0 | 312 | } |
michael@0 | 313 | |
michael@0 | 314 | { // To avoid reusing locals by mistake |
michael@0 | 315 | StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1)); |
michael@0 | 316 | EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust); |
michael@0 | 317 | // I'd argue that CONTEXT_VALID_EBP shouldn't be here, since the |
michael@0 | 318 | // walker does not actually fetch the EBP after a scan (forcing the |
michael@0 | 319 | // next frame to be scanned as well). But let's grandfather the existing |
michael@0 | 320 | // behavior in for now. |
michael@0 | 321 | ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP |
michael@0 | 322 | | StackFrameX86::CONTEXT_VALID_ESP |
michael@0 | 323 | | StackFrameX86::CONTEXT_VALID_EBP), |
michael@0 | 324 | frame1->context_validity); |
michael@0 | 325 | EXPECT_EQ(0x4000129dU, frame1->instruction + 1); |
michael@0 | 326 | EXPECT_EQ(0x4000129dU, frame1->context.eip); |
michael@0 | 327 | EXPECT_EQ(0x80000014U, frame1->context.esp); |
michael@0 | 328 | EXPECT_EQ(0xd43eed6eU, frame1->context.ebp); |
michael@0 | 329 | EXPECT_EQ(NULL, frame1->windows_frame_info); |
michael@0 | 330 | } |
michael@0 | 331 | } |
michael@0 | 332 | |
michael@0 | 333 | // Force scanning for a return address a long way down the stack |
michael@0 | 334 | TEST_F(GetCallerFrame, TraditionalScanLongWay) { |
michael@0 | 335 | stack_section.start() = 0x80000000; |
michael@0 | 336 | Label frame1_ebp; |
michael@0 | 337 | stack_section |
michael@0 | 338 | // frame 0 |
michael@0 | 339 | .D32(0xf065dc76) // locals area: |
michael@0 | 340 | .D32(0x46ee2167) // garbage that doesn't look like |
michael@0 | 341 | .D32(0xbab023ec) // a return address |
michael@0 | 342 | .Append(20 * 4, 0) // a bunch of space |
michael@0 | 343 | .D32(frame1_ebp) // saved %ebp (%ebp fails to point here, forcing scan) |
michael@0 | 344 | .D32(0x4000129d) // return address |
michael@0 | 345 | // frame 1 |
michael@0 | 346 | .Append(8, 0) // space |
michael@0 | 347 | .Mark(&frame1_ebp) // %ebp points here |
michael@0 | 348 | .D32(0) // saved %ebp (stack end) |
michael@0 | 349 | .D32(0); // return address (stack end) |
michael@0 | 350 | |
michael@0 | 351 | RegionFromSection(); |
michael@0 | 352 | raw_context.eip = 0x4000f49d; |
michael@0 | 353 | raw_context.esp = stack_section.start().Value(); |
michael@0 | 354 | // Make the frame pointer bogus, to make the stackwalker scan the stack |
michael@0 | 355 | // for something that looks like a return address. |
michael@0 | 356 | raw_context.ebp = 0xd43eed6e; |
michael@0 | 357 | |
michael@0 | 358 | StackFrameSymbolizer frame_symbolizer(&supplier, &resolver); |
michael@0 | 359 | StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules, |
michael@0 | 360 | &frame_symbolizer); |
michael@0 | 361 | vector<const CodeModule*> modules_without_symbols; |
michael@0 | 362 | ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols)); |
michael@0 | 363 | ASSERT_EQ(1U, modules_without_symbols.size()); |
michael@0 | 364 | ASSERT_EQ("module1", modules_without_symbols[0]->debug_file()); |
michael@0 | 365 | frames = call_stack.frames(); |
michael@0 | 366 | ASSERT_EQ(2U, frames->size()); |
michael@0 | 367 | |
michael@0 | 368 | { // To avoid reusing locals by mistake |
michael@0 | 369 | StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0)); |
michael@0 | 370 | EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); |
michael@0 | 371 | ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); |
michael@0 | 372 | EXPECT_EQ(0x4000f49dU, frame0->instruction); |
michael@0 | 373 | EXPECT_EQ(0x4000f49dU, frame0->context.eip); |
michael@0 | 374 | EXPECT_EQ(stack_section.start().Value(), frame0->context.esp); |
michael@0 | 375 | EXPECT_EQ(0xd43eed6eU, frame0->context.ebp); |
michael@0 | 376 | EXPECT_EQ(NULL, frame0->windows_frame_info); |
michael@0 | 377 | } |
michael@0 | 378 | |
michael@0 | 379 | { // To avoid reusing locals by mistake |
michael@0 | 380 | StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1)); |
michael@0 | 381 | EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust); |
michael@0 | 382 | // I'd argue that CONTEXT_VALID_EBP shouldn't be here, since the |
michael@0 | 383 | // walker does not actually fetch the EBP after a scan (forcing the |
michael@0 | 384 | // next frame to be scanned as well). But let's grandfather the existing |
michael@0 | 385 | // behavior in for now. |
michael@0 | 386 | ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP |
michael@0 | 387 | | StackFrameX86::CONTEXT_VALID_ESP |
michael@0 | 388 | | StackFrameX86::CONTEXT_VALID_EBP), |
michael@0 | 389 | frame1->context_validity); |
michael@0 | 390 | EXPECT_EQ(0x4000129dU, frame1->instruction + 1); |
michael@0 | 391 | EXPECT_EQ(0x4000129dU, frame1->context.eip); |
michael@0 | 392 | EXPECT_EQ(0x80000064U, frame1->context.esp); |
michael@0 | 393 | EXPECT_EQ(0xd43eed6eU, frame1->context.ebp); |
michael@0 | 394 | EXPECT_EQ(NULL, frame1->windows_frame_info); |
michael@0 | 395 | } |
michael@0 | 396 | } |
michael@0 | 397 | |
michael@0 | 398 | // Use Windows frame data (a "STACK WIN 4" record, from a |
michael@0 | 399 | // FrameTypeFrameData DIA record) to walk a stack frame. |
michael@0 | 400 | TEST_F(GetCallerFrame, WindowsFrameData) { |
michael@0 | 401 | SetModuleSymbols(&module1, |
michael@0 | 402 | "STACK WIN 4 aa85 176 0 0 4 10 4 0 1" |
michael@0 | 403 | " $T2 $esp .cbSavedRegs + =" |
michael@0 | 404 | " $T0 .raSearchStart =" |
michael@0 | 405 | " $eip $T0 ^ =" |
michael@0 | 406 | " $esp $T0 4 + =" |
michael@0 | 407 | " $ebx $T2 4 - ^ =" |
michael@0 | 408 | " $edi $T2 8 - ^ =" |
michael@0 | 409 | " $esi $T2 12 - ^ =" |
michael@0 | 410 | " $ebp $T2 16 - ^ =\n"); |
michael@0 | 411 | Label frame1_esp, frame1_ebp; |
michael@0 | 412 | stack_section.start() = 0x80000000; |
michael@0 | 413 | stack_section |
michael@0 | 414 | // frame 0 |
michael@0 | 415 | .D32(frame1_ebp) // saved regs: %ebp |
michael@0 | 416 | .D32(0xa7120d1a) // %esi |
michael@0 | 417 | .D32(0x630891be) // %edi |
michael@0 | 418 | .D32(0x9068a878) // %ebx |
michael@0 | 419 | .D32(0xa08ea45f) // locals: unused |
michael@0 | 420 | .D32(0x40001350) // return address |
michael@0 | 421 | // frame 1 |
michael@0 | 422 | .Mark(&frame1_esp) |
michael@0 | 423 | .Append(12, 0) // empty space |
michael@0 | 424 | .Mark(&frame1_ebp) |
michael@0 | 425 | .D32(0) // saved %ebp (stack end) |
michael@0 | 426 | .D32(0); // saved %eip (stack end) |
michael@0 | 427 | |
michael@0 | 428 | RegionFromSection(); |
michael@0 | 429 | raw_context.eip = 0x4000aa85; |
michael@0 | 430 | raw_context.esp = stack_section.start().Value(); |
michael@0 | 431 | raw_context.ebp = 0xf052c1de; // should not be needed to walk frame |
michael@0 | 432 | |
michael@0 | 433 | StackFrameSymbolizer frame_symbolizer(&supplier, &resolver); |
michael@0 | 434 | StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules, |
michael@0 | 435 | &frame_symbolizer); |
michael@0 | 436 | vector<const CodeModule*> modules_without_symbols; |
michael@0 | 437 | ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols)); |
michael@0 | 438 | ASSERT_EQ(0U, modules_without_symbols.size()); |
michael@0 | 439 | frames = call_stack.frames(); |
michael@0 | 440 | ASSERT_EQ(2U, frames->size()); |
michael@0 | 441 | |
michael@0 | 442 | { // To avoid reusing locals by mistake |
michael@0 | 443 | StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0)); |
michael@0 | 444 | EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); |
michael@0 | 445 | ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); |
michael@0 | 446 | EXPECT_EQ(0x4000aa85U, frame0->instruction); |
michael@0 | 447 | EXPECT_EQ(0x4000aa85U, frame0->context.eip); |
michael@0 | 448 | EXPECT_EQ(stack_section.start().Value(), frame0->context.esp); |
michael@0 | 449 | EXPECT_EQ(0xf052c1deU, frame0->context.ebp); |
michael@0 | 450 | EXPECT_TRUE(frame0->windows_frame_info != NULL); |
michael@0 | 451 | } |
michael@0 | 452 | |
michael@0 | 453 | { // To avoid reusing locals by mistake |
michael@0 | 454 | StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1)); |
michael@0 | 455 | EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust); |
michael@0 | 456 | ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP |
michael@0 | 457 | | StackFrameX86::CONTEXT_VALID_ESP |
michael@0 | 458 | | StackFrameX86::CONTEXT_VALID_EBP |
michael@0 | 459 | | StackFrameX86::CONTEXT_VALID_EBX |
michael@0 | 460 | | StackFrameX86::CONTEXT_VALID_ESI |
michael@0 | 461 | | StackFrameX86::CONTEXT_VALID_EDI), |
michael@0 | 462 | frame1->context_validity); |
michael@0 | 463 | EXPECT_EQ(0x40001350U, frame1->instruction + 1); |
michael@0 | 464 | EXPECT_EQ(0x40001350U, frame1->context.eip); |
michael@0 | 465 | EXPECT_EQ(frame1_esp.Value(), frame1->context.esp); |
michael@0 | 466 | EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp); |
michael@0 | 467 | EXPECT_EQ(0x9068a878U, frame1->context.ebx); |
michael@0 | 468 | EXPECT_EQ(0xa7120d1aU, frame1->context.esi); |
michael@0 | 469 | EXPECT_EQ(0x630891beU, frame1->context.edi); |
michael@0 | 470 | EXPECT_EQ(NULL, frame1->windows_frame_info); |
michael@0 | 471 | } |
michael@0 | 472 | } |
michael@0 | 473 | |
michael@0 | 474 | // Use Windows frame data (a "STACK WIN 4" record, from a |
michael@0 | 475 | // FrameTypeFrameData DIA record) to walk a stack frame where the stack |
michael@0 | 476 | // is aligned and we must search |
michael@0 | 477 | TEST_F(GetCallerFrame, WindowsFrameDataAligned) { |
michael@0 | 478 | SetModuleSymbols(&module1, |
michael@0 | 479 | "STACK WIN 4 aa85 176 0 0 4 4 8 0 1" |
michael@0 | 480 | " $T1 .raSearch =" |
michael@0 | 481 | " $T0 $T1 4 - 8 @ =" |
michael@0 | 482 | " $ebp $T1 4 - ^ =" |
michael@0 | 483 | " $eip $T1 ^ =" |
michael@0 | 484 | " $esp $T1 4 + ="); |
michael@0 | 485 | Label frame1_esp, frame1_ebp; |
michael@0 | 486 | stack_section.start() = 0x80000000; |
michael@0 | 487 | stack_section |
michael@0 | 488 | // frame 0 |
michael@0 | 489 | .D32(0x0ffa0ffa) // unused saved register |
michael@0 | 490 | .D32(0xdeaddead) // locals |
michael@0 | 491 | .D32(0xbeefbeef) |
michael@0 | 492 | .D32(0) // 8-byte alignment |
michael@0 | 493 | .D32(frame1_ebp) |
michael@0 | 494 | .D32(0x5000129d) // return address |
michael@0 | 495 | // frame 1 |
michael@0 | 496 | .Mark(&frame1_esp) |
michael@0 | 497 | .D32(0x1) // parameter |
michael@0 | 498 | .Mark(&frame1_ebp) |
michael@0 | 499 | .D32(0) // saved %ebp (stack end) |
michael@0 | 500 | .D32(0); // saved %eip (stack end) |
michael@0 | 501 | |
michael@0 | 502 | RegionFromSection(); |
michael@0 | 503 | raw_context.eip = 0x4000aa85; |
michael@0 | 504 | raw_context.esp = stack_section.start().Value(); |
michael@0 | 505 | raw_context.ebp = 0xf052c1de; // should not be needed to walk frame |
michael@0 | 506 | |
michael@0 | 507 | StackFrameSymbolizer frame_symbolizer(&supplier, &resolver); |
michael@0 | 508 | StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules, |
michael@0 | 509 | &frame_symbolizer); |
michael@0 | 510 | vector<const CodeModule*> modules_without_symbols; |
michael@0 | 511 | ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols)); |
michael@0 | 512 | ASSERT_EQ(1U, modules_without_symbols.size()); |
michael@0 | 513 | ASSERT_EQ("module2", modules_without_symbols[0]->debug_file()); |
michael@0 | 514 | frames = call_stack.frames(); |
michael@0 | 515 | ASSERT_EQ(2U, frames->size()); |
michael@0 | 516 | |
michael@0 | 517 | { // To avoid reusing locals by mistake |
michael@0 | 518 | StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0)); |
michael@0 | 519 | EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); |
michael@0 | 520 | ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); |
michael@0 | 521 | EXPECT_EQ(0x4000aa85U, frame0->instruction); |
michael@0 | 522 | EXPECT_EQ(0x4000aa85U, frame0->context.eip); |
michael@0 | 523 | EXPECT_EQ(stack_section.start().Value(), frame0->context.esp); |
michael@0 | 524 | EXPECT_EQ(0xf052c1deU, frame0->context.ebp); |
michael@0 | 525 | EXPECT_TRUE(frame0->windows_frame_info != NULL); |
michael@0 | 526 | } |
michael@0 | 527 | |
michael@0 | 528 | { // To avoid reusing locals by mistake |
michael@0 | 529 | StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1)); |
michael@0 | 530 | EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust); |
michael@0 | 531 | ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP |
michael@0 | 532 | | StackFrameX86::CONTEXT_VALID_ESP |
michael@0 | 533 | | StackFrameX86::CONTEXT_VALID_EBP), |
michael@0 | 534 | frame1->context_validity); |
michael@0 | 535 | EXPECT_EQ(0x5000129dU, frame1->instruction + 1); |
michael@0 | 536 | EXPECT_EQ(0x5000129dU, frame1->context.eip); |
michael@0 | 537 | EXPECT_EQ(frame1_esp.Value(), frame1->context.esp); |
michael@0 | 538 | EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp); |
michael@0 | 539 | EXPECT_EQ(NULL, frame1->windows_frame_info); |
michael@0 | 540 | } |
michael@0 | 541 | } |
michael@0 | 542 | |
michael@0 | 543 | // Use Windows frame data (a "STACK WIN 4" record, from a |
michael@0 | 544 | // FrameTypeFrameData DIA record) to walk a frame, and depend on the |
michael@0 | 545 | // parameter size from the callee as well. |
michael@0 | 546 | TEST_F(GetCallerFrame, WindowsFrameDataParameterSize) { |
michael@0 | 547 | SetModuleSymbols(&module1, "FUNC 1000 100 c module1::wheedle\n"); |
michael@0 | 548 | SetModuleSymbols(&module2, |
michael@0 | 549 | // Note bogus parameter size in FUNC record; the stack walker |
michael@0 | 550 | // should prefer the STACK WIN record, and see '4' below. |
michael@0 | 551 | "FUNC aa85 176 beef module2::whine\n" |
michael@0 | 552 | "STACK WIN 4 aa85 176 0 0 4 10 4 0 1" |
michael@0 | 553 | " $T2 $esp .cbLocals + .cbSavedRegs + =" |
michael@0 | 554 | " $T0 .raSearchStart =" |
michael@0 | 555 | " $eip $T0 ^ =" |
michael@0 | 556 | " $esp $T0 4 + =" |
michael@0 | 557 | " $ebp $T0 20 - ^ =" |
michael@0 | 558 | " $ebx $T0 8 - ^ =\n"); |
michael@0 | 559 | Label frame0_esp, frame0_ebp; |
michael@0 | 560 | Label frame1_esp; |
michael@0 | 561 | Label frame2_esp, frame2_ebp; |
michael@0 | 562 | stack_section.start() = 0x80000000; |
michael@0 | 563 | stack_section |
michael@0 | 564 | // frame 0, in module1::wheedle. Traditional frame. |
michael@0 | 565 | .Mark(&frame0_esp) |
michael@0 | 566 | .Append(16, 0) // frame space |
michael@0 | 567 | .Mark(&frame0_ebp) |
michael@0 | 568 | .D32(0x6fa902e0) // saved %ebp. Not a frame pointer. |
michael@0 | 569 | .D32(0x5000aa95) // return address, in module2::whine |
michael@0 | 570 | // frame 1, in module2::whine. FrameData frame. |
michael@0 | 571 | .Mark(&frame1_esp) |
michael@0 | 572 | .D32(0xbaa0cb7a) // argument 3 passed to module1::wheedle |
michael@0 | 573 | .D32(0xbdc92f9f) // argument 2 |
michael@0 | 574 | .D32(0x0b1d8442) // argument 1 |
michael@0 | 575 | .D32(frame2_ebp) // saved %ebp |
michael@0 | 576 | .D32(0xb1b90a15) // unused |
michael@0 | 577 | .D32(0xf18e072d) // unused |
michael@0 | 578 | .D32(0x2558c7f3) // saved %ebx |
michael@0 | 579 | .D32(0x0365e25e) // unused |
michael@0 | 580 | .D32(0x2a179e38) // return address; $T0 points here |
michael@0 | 581 | // frame 2, in no module |
michael@0 | 582 | .Mark(&frame2_esp) |
michael@0 | 583 | .Append(12, 0) // empty space |
michael@0 | 584 | .Mark(&frame2_ebp) |
michael@0 | 585 | .D32(0) // saved %ebp (stack end) |
michael@0 | 586 | .D32(0); // saved %eip (stack end) |
michael@0 | 587 | |
michael@0 | 588 | RegionFromSection(); |
michael@0 | 589 | raw_context.eip = 0x40001004; // in module1::wheedle |
michael@0 | 590 | raw_context.esp = stack_section.start().Value(); |
michael@0 | 591 | raw_context.ebp = frame0_ebp.Value(); |
michael@0 | 592 | |
michael@0 | 593 | StackFrameSymbolizer frame_symbolizer(&supplier, &resolver); |
michael@0 | 594 | StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules, |
michael@0 | 595 | &frame_symbolizer); |
michael@0 | 596 | vector<const CodeModule*> modules_without_symbols; |
michael@0 | 597 | ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols)); |
michael@0 | 598 | ASSERT_EQ(0U, modules_without_symbols.size()); |
michael@0 | 599 | frames = call_stack.frames(); |
michael@0 | 600 | ASSERT_EQ(3U, frames->size()); |
michael@0 | 601 | |
michael@0 | 602 | { // To avoid reusing locals by mistake |
michael@0 | 603 | StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0)); |
michael@0 | 604 | EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); |
michael@0 | 605 | ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); |
michael@0 | 606 | EXPECT_EQ(0x40001004U, frame0->instruction); |
michael@0 | 607 | EXPECT_EQ(0x40001004U, frame0->context.eip); |
michael@0 | 608 | EXPECT_EQ(frame0_esp.Value(), frame0->context.esp); |
michael@0 | 609 | EXPECT_EQ(frame0_ebp.Value(), frame0->context.ebp); |
michael@0 | 610 | EXPECT_EQ(&module1, frame0->module); |
michael@0 | 611 | EXPECT_EQ("module1::wheedle", frame0->function_name); |
michael@0 | 612 | EXPECT_EQ(0x40001000U, frame0->function_base); |
michael@0 | 613 | // The FUNC record for module1::wheedle should have produced a |
michael@0 | 614 | // WindowsFrameInfo structure with only the parameter size valid. |
michael@0 | 615 | ASSERT_TRUE(frame0->windows_frame_info != NULL); |
michael@0 | 616 | EXPECT_EQ(WindowsFrameInfo::VALID_PARAMETER_SIZE, |
michael@0 | 617 | frame0->windows_frame_info->valid); |
michael@0 | 618 | EXPECT_EQ(WindowsFrameInfo::STACK_INFO_UNKNOWN, |
michael@0 | 619 | frame0->windows_frame_info->type_); |
michael@0 | 620 | EXPECT_EQ(12U, frame0->windows_frame_info->parameter_size); |
michael@0 | 621 | } |
michael@0 | 622 | |
michael@0 | 623 | { // To avoid reusing locals by mistake |
michael@0 | 624 | StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1)); |
michael@0 | 625 | EXPECT_EQ(StackFrame::FRAME_TRUST_FP, frame1->trust); |
michael@0 | 626 | ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP |
michael@0 | 627 | | StackFrameX86::CONTEXT_VALID_ESP |
michael@0 | 628 | | StackFrameX86::CONTEXT_VALID_EBP), |
michael@0 | 629 | frame1->context_validity); |
michael@0 | 630 | EXPECT_EQ(0x5000aa95U, frame1->instruction + 1); |
michael@0 | 631 | EXPECT_EQ(0x5000aa95U, frame1->context.eip); |
michael@0 | 632 | EXPECT_EQ(frame1_esp.Value(), frame1->context.esp); |
michael@0 | 633 | EXPECT_EQ(0x6fa902e0U, frame1->context.ebp); |
michael@0 | 634 | EXPECT_EQ(&module2, frame1->module); |
michael@0 | 635 | EXPECT_EQ("module2::whine", frame1->function_name); |
michael@0 | 636 | EXPECT_EQ(0x5000aa85U, frame1->function_base); |
michael@0 | 637 | ASSERT_TRUE(frame1->windows_frame_info != NULL); |
michael@0 | 638 | EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame1->windows_frame_info->valid); |
michael@0 | 639 | EXPECT_EQ(WindowsFrameInfo::STACK_INFO_FRAME_DATA, |
michael@0 | 640 | frame1->windows_frame_info->type_); |
michael@0 | 641 | // This should not see the 0xbeef parameter size from the FUNC |
michael@0 | 642 | // record, but should instead see the STACK WIN record. |
michael@0 | 643 | EXPECT_EQ(4U, frame1->windows_frame_info->parameter_size); |
michael@0 | 644 | } |
michael@0 | 645 | |
michael@0 | 646 | { // To avoid reusing locals by mistake |
michael@0 | 647 | StackFrameX86 *frame2 = static_cast<StackFrameX86 *>(frames->at(2)); |
michael@0 | 648 | EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame2->trust); |
michael@0 | 649 | ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP |
michael@0 | 650 | | StackFrameX86::CONTEXT_VALID_ESP |
michael@0 | 651 | | StackFrameX86::CONTEXT_VALID_EBP |
michael@0 | 652 | | StackFrameX86::CONTEXT_VALID_EBX), |
michael@0 | 653 | frame2->context_validity); |
michael@0 | 654 | EXPECT_EQ(0x2a179e38U, frame2->instruction + 1); |
michael@0 | 655 | EXPECT_EQ(0x2a179e38U, frame2->context.eip); |
michael@0 | 656 | EXPECT_EQ(frame2_esp.Value(), frame2->context.esp); |
michael@0 | 657 | EXPECT_EQ(frame2_ebp.Value(), frame2->context.ebp); |
michael@0 | 658 | EXPECT_EQ(0x2558c7f3U, frame2->context.ebx); |
michael@0 | 659 | EXPECT_EQ(NULL, frame2->module); |
michael@0 | 660 | EXPECT_EQ(NULL, frame2->windows_frame_info); |
michael@0 | 661 | } |
michael@0 | 662 | } |
michael@0 | 663 | |
michael@0 | 664 | // Use Windows frame data (a "STACK WIN 4" record, from a |
michael@0 | 665 | // FrameTypeFrameData DIA record) to walk a stack frame, where the |
michael@0 | 666 | // expression fails to yield both an $eip and an $ebp value, and the stack |
michael@0 | 667 | // walker must scan. |
michael@0 | 668 | TEST_F(GetCallerFrame, WindowsFrameDataScan) { |
michael@0 | 669 | SetModuleSymbols(&module1, |
michael@0 | 670 | "STACK WIN 4 c8c 111 0 0 4 10 4 0 1 bad program string\n"); |
michael@0 | 671 | // Mark frame 1's PC as the end of the stack. |
michael@0 | 672 | SetModuleSymbols(&module2, |
michael@0 | 673 | "FUNC 7c38 accf 0 module2::function\n" |
michael@0 | 674 | "STACK WIN 4 7c38 accf 0 0 4 10 4 0 1 $eip 0 = $ebp 0 =\n"); |
michael@0 | 675 | Label frame1_esp; |
michael@0 | 676 | stack_section.start() = 0x80000000; |
michael@0 | 677 | stack_section |
michael@0 | 678 | // frame 0 |
michael@0 | 679 | .Append(16, 0x2a) // unused, garbage |
michael@0 | 680 | .D32(0x50007ce9) // return address |
michael@0 | 681 | // frame 1 |
michael@0 | 682 | .Mark(&frame1_esp) |
michael@0 | 683 | .Append(8, 0); // empty space |
michael@0 | 684 | |
michael@0 | 685 | RegionFromSection(); |
michael@0 | 686 | raw_context.eip = 0x40000c9c; |
michael@0 | 687 | raw_context.esp = stack_section.start().Value(); |
michael@0 | 688 | raw_context.ebp = 0x2ae314cd; // should not be needed to walk frame |
michael@0 | 689 | |
michael@0 | 690 | StackFrameSymbolizer frame_symbolizer(&supplier, &resolver); |
michael@0 | 691 | StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules, |
michael@0 | 692 | &frame_symbolizer); |
michael@0 | 693 | vector<const CodeModule*> modules_without_symbols; |
michael@0 | 694 | ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols)); |
michael@0 | 695 | ASSERT_EQ(0U, modules_without_symbols.size()); |
michael@0 | 696 | frames = call_stack.frames(); |
michael@0 | 697 | ASSERT_EQ(2U, frames->size()); |
michael@0 | 698 | |
michael@0 | 699 | { // To avoid reusing locals by mistake |
michael@0 | 700 | StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0)); |
michael@0 | 701 | EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); |
michael@0 | 702 | ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); |
michael@0 | 703 | EXPECT_EQ(0x40000c9cU, frame0->instruction); |
michael@0 | 704 | EXPECT_EQ(0x40000c9cU, frame0->context.eip); |
michael@0 | 705 | EXPECT_EQ(stack_section.start().Value(), frame0->context.esp); |
michael@0 | 706 | EXPECT_EQ(0x2ae314cdU, frame0->context.ebp); |
michael@0 | 707 | EXPECT_TRUE(frame0->windows_frame_info != NULL); |
michael@0 | 708 | } |
michael@0 | 709 | |
michael@0 | 710 | { // To avoid reusing locals by mistake |
michael@0 | 711 | StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1)); |
michael@0 | 712 | EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust); |
michael@0 | 713 | // I'd argue that CONTEXT_VALID_EBP shouldn't be here, since the walker |
michael@0 | 714 | // does not actually fetch the EBP after a scan (forcing the next frame |
michael@0 | 715 | // to be scanned as well). But let's grandfather the existing behavior in |
michael@0 | 716 | // for now. |
michael@0 | 717 | ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP |
michael@0 | 718 | | StackFrameX86::CONTEXT_VALID_ESP |
michael@0 | 719 | | StackFrameX86::CONTEXT_VALID_EBP), |
michael@0 | 720 | frame1->context_validity); |
michael@0 | 721 | EXPECT_EQ(0x50007ce9U, frame1->instruction + 1); |
michael@0 | 722 | EXPECT_EQ(0x50007ce9U, frame1->context.eip); |
michael@0 | 723 | EXPECT_EQ(frame1_esp.Value(), frame1->context.esp); |
michael@0 | 724 | EXPECT_TRUE(frame1->windows_frame_info != NULL); |
michael@0 | 725 | } |
michael@0 | 726 | } |
michael@0 | 727 | |
michael@0 | 728 | // Use Windows frame data (a "STACK WIN 4" record, from a |
michael@0 | 729 | // FrameTypeFrameData DIA record) to walk a stack frame, where the |
michael@0 | 730 | // expression yields an $eip that falls outside of any module, and the |
michael@0 | 731 | // stack walker must scan. |
michael@0 | 732 | TEST_F(GetCallerFrame, WindowsFrameDataBadEIPScan) { |
michael@0 | 733 | SetModuleSymbols(&module1, |
michael@0 | 734 | "STACK WIN 4 6e6 e7 0 0 0 8 4 0 1" |
michael@0 | 735 | // A traditional frame, actually. |
michael@0 | 736 | " $eip $ebp 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ =\n"); |
michael@0 | 737 | // Mark frame 1's PC as the end of the stack. |
michael@0 | 738 | SetModuleSymbols(&module2, |
michael@0 | 739 | "FUNC cfdb 8406 0 module2::function\n" |
michael@0 | 740 | "STACK WIN 4 cfdb 8406 0 0 0 0 0 0 1 $eip 0 = $ebp 0 =\n"); |
michael@0 | 741 | stack_section.start() = 0x80000000; |
michael@0 | 742 | |
michael@0 | 743 | // In this stack, the context's %ebp is pointing at the wrong place, so |
michael@0 | 744 | // the stack walker needs to scan to find the return address, and then |
michael@0 | 745 | // scan again to find the caller's saved %ebp. |
michael@0 | 746 | Label frame0_ebp, frame1_ebp, frame1_esp; |
michael@0 | 747 | stack_section |
michael@0 | 748 | // frame 0 |
michael@0 | 749 | .Append(8, 0x2a) // garbage |
michael@0 | 750 | .Mark(&frame0_ebp) // frame 0 %ebp points here, but should point |
michael@0 | 751 | // at *** below |
michael@0 | 752 | // The STACK WIN record says that the following two values are |
michael@0 | 753 | // frame 1's saved %ebp and return address, but the %ebp is wrong; |
michael@0 | 754 | // they're garbage. The stack walker will scan for the right values. |
michael@0 | 755 | .D32(0x3d937b2b) // alleged to be frame 1's saved %ebp |
michael@0 | 756 | .D32(0x17847f5b) // alleged to be frame 1's return address |
michael@0 | 757 | .D32(frame1_ebp) // frame 1's real saved %ebp; scan will find |
michael@0 | 758 | .D32(0x2b2b2b2b) // first word of realigned register save area |
michael@0 | 759 | // *** frame 0 %ebp ought to be pointing here |
michael@0 | 760 | .D32(0x2c2c2c2c) // realigned locals area |
michael@0 | 761 | .D32(0x5000d000) // frame 1's real saved %eip; scan will find |
michael@0 | 762 | // Frame 1, in module2::function. The STACK WIN record describes |
michael@0 | 763 | // this as the oldest frame, without referring to its contents, so |
michael@0 | 764 | // we needn't to provide any actual data here. |
michael@0 | 765 | .Mark(&frame1_esp) |
michael@0 | 766 | .Mark(&frame1_ebp) // frame 1 %ebp points here |
michael@0 | 767 | // A dummy value for frame 1's %ebp to point at. The scan recognizes the |
michael@0 | 768 | // saved %ebp because it points to a valid word in the stack memory region. |
michael@0 | 769 | .D32(0x2d2d2d2d); |
michael@0 | 770 | |
michael@0 | 771 | RegionFromSection(); |
michael@0 | 772 | raw_context.eip = 0x40000700; |
michael@0 | 773 | raw_context.esp = stack_section.start().Value(); |
michael@0 | 774 | raw_context.ebp = frame0_ebp.Value(); |
michael@0 | 775 | |
michael@0 | 776 | StackFrameSymbolizer frame_symbolizer(&supplier, &resolver); |
michael@0 | 777 | StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules, |
michael@0 | 778 | &frame_symbolizer); |
michael@0 | 779 | vector<const CodeModule*> modules_without_symbols; |
michael@0 | 780 | ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols)); |
michael@0 | 781 | ASSERT_EQ(0U, modules_without_symbols.size()); |
michael@0 | 782 | frames = call_stack.frames(); |
michael@0 | 783 | ASSERT_EQ(2U, frames->size()); |
michael@0 | 784 | |
michael@0 | 785 | { // To avoid reusing locals by mistake |
michael@0 | 786 | StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0)); |
michael@0 | 787 | EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); |
michael@0 | 788 | ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); |
michael@0 | 789 | EXPECT_EQ(0x40000700U, frame0->instruction); |
michael@0 | 790 | EXPECT_EQ(0x40000700U, frame0->context.eip); |
michael@0 | 791 | EXPECT_EQ(stack_section.start().Value(), frame0->context.esp); |
michael@0 | 792 | EXPECT_EQ(frame0_ebp.Value(), frame0->context.ebp); |
michael@0 | 793 | EXPECT_TRUE(frame0->windows_frame_info != NULL); |
michael@0 | 794 | } |
michael@0 | 795 | |
michael@0 | 796 | { // To avoid reusing locals by mistake |
michael@0 | 797 | StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1)); |
michael@0 | 798 | EXPECT_EQ(StackFrame::FRAME_TRUST_CFI_SCAN, frame1->trust); |
michael@0 | 799 | // I'd argue that CONTEXT_VALID_EBP shouldn't be here, since the |
michael@0 | 800 | // walker does not actually fetch the EBP after a scan (forcing the |
michael@0 | 801 | // next frame to be scanned as well). But let's grandfather the existing |
michael@0 | 802 | // behavior in for now. |
michael@0 | 803 | ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP |
michael@0 | 804 | | StackFrameX86::CONTEXT_VALID_ESP |
michael@0 | 805 | | StackFrameX86::CONTEXT_VALID_EBP), |
michael@0 | 806 | frame1->context_validity); |
michael@0 | 807 | EXPECT_EQ(0x5000d000U, frame1->instruction + 1); |
michael@0 | 808 | EXPECT_EQ(0x5000d000U, frame1->context.eip); |
michael@0 | 809 | EXPECT_EQ(frame1_esp.Value(), frame1->context.esp); |
michael@0 | 810 | EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp); |
michael@0 | 811 | EXPECT_TRUE(frame1->windows_frame_info != NULL); |
michael@0 | 812 | } |
michael@0 | 813 | } |
michael@0 | 814 | |
michael@0 | 815 | // Use Windows FrameTypeFPO data to walk a stack frame for a function that |
michael@0 | 816 | // does not modify %ebp from the value it had in the caller. |
michael@0 | 817 | TEST_F(GetCallerFrame, WindowsFPOUnchangedEBP) { |
michael@0 | 818 | SetModuleSymbols(&module1, |
michael@0 | 819 | // Note bogus parameter size in FUNC record; the walker |
michael@0 | 820 | // should prefer the STACK WIN record, and see the '8' below. |
michael@0 | 821 | "FUNC e8a8 100 feeb module1::discombobulated\n" |
michael@0 | 822 | "STACK WIN 0 e8a8 100 0 0 8 4 10 0 0 0\n"); |
michael@0 | 823 | Label frame0_esp; |
michael@0 | 824 | Label frame1_esp, frame1_ebp; |
michael@0 | 825 | stack_section.start() = 0x80000000; |
michael@0 | 826 | stack_section |
michael@0 | 827 | // frame 0, in module1::wheedle. FrameTypeFPO (STACK WIN 0) frame. |
michael@0 | 828 | .Mark(&frame0_esp) |
michael@0 | 829 | // no outgoing parameters; this is the youngest frame. |
michael@0 | 830 | .D32(0x7c521352) // four bytes of saved registers |
michael@0 | 831 | .Append(0x10, 0x42) // local area |
michael@0 | 832 | .D32(0x40009b5b) // return address, in module1, no function |
michael@0 | 833 | // frame 1, in module1, no function. |
michael@0 | 834 | .Mark(&frame1_esp) |
michael@0 | 835 | .D32(0xf60ea7fc) // junk |
michael@0 | 836 | .Mark(&frame1_ebp) |
michael@0 | 837 | .D32(0) // saved %ebp (stack end) |
michael@0 | 838 | .D32(0); // saved %eip (stack end) |
michael@0 | 839 | |
michael@0 | 840 | RegionFromSection(); |
michael@0 | 841 | raw_context.eip = 0x4000e8b8; // in module1::whine |
michael@0 | 842 | raw_context.esp = stack_section.start().Value(); |
michael@0 | 843 | // Frame pointer unchanged from caller. |
michael@0 | 844 | raw_context.ebp = frame1_ebp.Value(); |
michael@0 | 845 | |
michael@0 | 846 | StackFrameSymbolizer frame_symbolizer(&supplier, &resolver); |
michael@0 | 847 | StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules, |
michael@0 | 848 | &frame_symbolizer); |
michael@0 | 849 | vector<const CodeModule*> modules_without_symbols; |
michael@0 | 850 | ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols)); |
michael@0 | 851 | ASSERT_EQ(0U, modules_without_symbols.size()); |
michael@0 | 852 | frames = call_stack.frames(); |
michael@0 | 853 | ASSERT_EQ(2U, frames->size()); |
michael@0 | 854 | |
michael@0 | 855 | { // To avoid reusing locals by mistake |
michael@0 | 856 | StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0)); |
michael@0 | 857 | EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); |
michael@0 | 858 | ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); |
michael@0 | 859 | EXPECT_EQ(0x4000e8b8U, frame0->instruction); |
michael@0 | 860 | EXPECT_EQ(0x4000e8b8U, frame0->context.eip); |
michael@0 | 861 | EXPECT_EQ(frame0_esp.Value(), frame0->context.esp); |
michael@0 | 862 | EXPECT_EQ(frame1_ebp.Value(), frame0->context.ebp); // unchanged from caller |
michael@0 | 863 | EXPECT_EQ(&module1, frame0->module); |
michael@0 | 864 | EXPECT_EQ("module1::discombobulated", frame0->function_name); |
michael@0 | 865 | EXPECT_EQ(0x4000e8a8U, frame0->function_base); |
michael@0 | 866 | // The STACK WIN record for module1::discombobulated should have |
michael@0 | 867 | // produced a fully populated WindowsFrameInfo structure. |
michael@0 | 868 | ASSERT_TRUE(frame0->windows_frame_info != NULL); |
michael@0 | 869 | EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame0->windows_frame_info->valid); |
michael@0 | 870 | EXPECT_EQ(WindowsFrameInfo::STACK_INFO_FPO, |
michael@0 | 871 | frame0->windows_frame_info->type_); |
michael@0 | 872 | EXPECT_EQ(0x10U, frame0->windows_frame_info->local_size); |
michael@0 | 873 | } |
michael@0 | 874 | |
michael@0 | 875 | { // To avoid reusing locals by mistake |
michael@0 | 876 | StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1)); |
michael@0 | 877 | EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust); |
michael@0 | 878 | ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP |
michael@0 | 879 | | StackFrameX86::CONTEXT_VALID_ESP |
michael@0 | 880 | | StackFrameX86::CONTEXT_VALID_EBP), |
michael@0 | 881 | frame1->context_validity); |
michael@0 | 882 | EXPECT_EQ(0x40009b5bU, frame1->instruction + 1); |
michael@0 | 883 | EXPECT_EQ(0x40009b5bU, frame1->context.eip); |
michael@0 | 884 | EXPECT_EQ(frame1_esp.Value(), frame1->context.esp); |
michael@0 | 885 | EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp); |
michael@0 | 886 | EXPECT_EQ(&module1, frame1->module); |
michael@0 | 887 | EXPECT_EQ("", frame1->function_name); |
michael@0 | 888 | EXPECT_EQ(NULL, frame1->windows_frame_info); |
michael@0 | 889 | } |
michael@0 | 890 | } |
michael@0 | 891 | |
michael@0 | 892 | // Use Windows FrameTypeFPO data to walk a stack frame for a function |
michael@0 | 893 | // that uses %ebp for its own purposes, saving the value it had in the |
michael@0 | 894 | // caller in the standard place in the saved register area. |
michael@0 | 895 | TEST_F(GetCallerFrame, WindowsFPOUsedEBP) { |
michael@0 | 896 | SetModuleSymbols(&module1, |
michael@0 | 897 | // Note bogus parameter size in FUNC record; the walker |
michael@0 | 898 | // should prefer the STACK WIN record, and see the '8' below. |
michael@0 | 899 | "FUNC 9aa8 e6 abbe module1::RaisedByTheAliens\n" |
michael@0 | 900 | "STACK WIN 0 9aa8 e6 a 0 10 8 4 0 0 1\n"); |
michael@0 | 901 | Label frame0_esp; |
michael@0 | 902 | Label frame1_esp, frame1_ebp; |
michael@0 | 903 | stack_section.start() = 0x80000000; |
michael@0 | 904 | stack_section |
michael@0 | 905 | // frame 0, in module1::wheedle. FrameTypeFPO (STACK WIN 0) frame. |
michael@0 | 906 | .Mark(&frame0_esp) |
michael@0 | 907 | // no outgoing parameters; this is the youngest frame. |
michael@0 | 908 | .D32(frame1_ebp) // saved register area: saved %ebp |
michael@0 | 909 | .D32(0xb68bd5f9) // saved register area: something else |
michael@0 | 910 | .D32(0xd25d05fc) // local area |
michael@0 | 911 | .D32(0x4000debe) // return address, in module1, no function |
michael@0 | 912 | // frame 1, in module1, no function. |
michael@0 | 913 | .Mark(&frame1_esp) |
michael@0 | 914 | .D32(0xf0c9a974) // junk |
michael@0 | 915 | .Mark(&frame1_ebp) |
michael@0 | 916 | .D32(0) // saved %ebp (stack end) |
michael@0 | 917 | .D32(0); // saved %eip (stack end) |
michael@0 | 918 | |
michael@0 | 919 | RegionFromSection(); |
michael@0 | 920 | raw_context.eip = 0x40009ab8; // in module1::RaisedByTheAliens |
michael@0 | 921 | raw_context.esp = stack_section.start().Value(); |
michael@0 | 922 | // RaisedByTheAliens uses %ebp for its own mysterious purposes. |
michael@0 | 923 | raw_context.ebp = 0xecbdd1a5; |
michael@0 | 924 | |
michael@0 | 925 | StackFrameSymbolizer frame_symbolizer(&supplier, &resolver); |
michael@0 | 926 | StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules, |
michael@0 | 927 | &frame_symbolizer); |
michael@0 | 928 | vector<const CodeModule*> modules_without_symbols; |
michael@0 | 929 | ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols)); |
michael@0 | 930 | ASSERT_EQ(0U, modules_without_symbols.size()); |
michael@0 | 931 | frames = call_stack.frames(); |
michael@0 | 932 | ASSERT_EQ(2U, frames->size()); |
michael@0 | 933 | |
michael@0 | 934 | { // To avoid reusing locals by mistake |
michael@0 | 935 | StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0)); |
michael@0 | 936 | EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); |
michael@0 | 937 | ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); |
michael@0 | 938 | EXPECT_EQ(0x40009ab8U, frame0->instruction); |
michael@0 | 939 | EXPECT_EQ(0x40009ab8U, frame0->context.eip); |
michael@0 | 940 | EXPECT_EQ(frame0_esp.Value(), frame0->context.esp); |
michael@0 | 941 | EXPECT_EQ(0xecbdd1a5, frame0->context.ebp); |
michael@0 | 942 | EXPECT_EQ(&module1, frame0->module); |
michael@0 | 943 | EXPECT_EQ("module1::RaisedByTheAliens", frame0->function_name); |
michael@0 | 944 | EXPECT_EQ(0x40009aa8U, frame0->function_base); |
michael@0 | 945 | // The STACK WIN record for module1::RaisedByTheAliens should have |
michael@0 | 946 | // produced a fully populated WindowsFrameInfo structure. |
michael@0 | 947 | ASSERT_TRUE(frame0->windows_frame_info != NULL); |
michael@0 | 948 | EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame0->windows_frame_info->valid); |
michael@0 | 949 | EXPECT_EQ(WindowsFrameInfo::STACK_INFO_FPO, |
michael@0 | 950 | frame0->windows_frame_info->type_); |
michael@0 | 951 | EXPECT_EQ("", frame0->windows_frame_info->program_string); |
michael@0 | 952 | EXPECT_TRUE(frame0->windows_frame_info->allocates_base_pointer); |
michael@0 | 953 | } |
michael@0 | 954 | |
michael@0 | 955 | { // To avoid reusing locals by mistake |
michael@0 | 956 | StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1)); |
michael@0 | 957 | EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust); |
michael@0 | 958 | ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP |
michael@0 | 959 | | StackFrameX86::CONTEXT_VALID_ESP |
michael@0 | 960 | | StackFrameX86::CONTEXT_VALID_EBP), |
michael@0 | 961 | frame1->context_validity); |
michael@0 | 962 | EXPECT_EQ(0x4000debeU, frame1->instruction + 1); |
michael@0 | 963 | EXPECT_EQ(0x4000debeU, frame1->context.eip); |
michael@0 | 964 | EXPECT_EQ(frame1_esp.Value(), frame1->context.esp); |
michael@0 | 965 | EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp); |
michael@0 | 966 | EXPECT_EQ(&module1, frame1->module); |
michael@0 | 967 | EXPECT_EQ("", frame1->function_name); |
michael@0 | 968 | EXPECT_EQ(NULL, frame1->windows_frame_info); |
michael@0 | 969 | } |
michael@0 | 970 | } |
michael@0 | 971 | |
michael@0 | 972 | // This is a regression unit test which covers a bug which has to do with |
michael@0 | 973 | // FPO-optimized Windows system call stubs in the context frame. There is |
michael@0 | 974 | // a more recent Windows system call dispatch mechanism which differs from |
michael@0 | 975 | // the one which is being tested here. The newer system call dispatch |
michael@0 | 976 | // mechanism creates an extra context frame (KiFastSystemCallRet). |
michael@0 | 977 | TEST_F(GetCallerFrame, WindowsFPOSystemCall) { |
michael@0 | 978 | SetModuleSymbols(&module3, // ntdll.dll |
michael@0 | 979 | "PUBLIC 1f8ac c ZwWaitForSingleObject\n" |
michael@0 | 980 | "STACK WIN 0 1f8ac 1b 0 0 c 0 0 0 0 0\n"); |
michael@0 | 981 | SetModuleSymbols(&module4, // kernelbase.dll |
michael@0 | 982 | "PUBLIC 109f9 c WaitForSingleObjectEx\n" |
michael@0 | 983 | "PUBLIC 36590 0 _except_handler4\n" |
michael@0 | 984 | "STACK WIN 4 109f9 df c 0 c c 48 0 1 $T0 $ebp = $eip " |
michael@0 | 985 | "$T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L " |
michael@0 | 986 | "$T0 .cbSavedRegs - = $P $T0 8 + .cbParams + =\n" |
michael@0 | 987 | "STACK WIN 4 36590 154 17 0 10 0 14 0 1 $T0 $ebp = $eip " |
michael@0 | 988 | "$T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 " |
michael@0 | 989 | ".cbSavedRegs - = $P $T0 8 + .cbParams + =\n"); |
michael@0 | 990 | SetModuleSymbols(&module5, // kernel32.dll |
michael@0 | 991 | "PUBLIC 11136 8 WaitForSingleObject\n" |
michael@0 | 992 | "PUBLIC 11151 c WaitForSingleObjectExImplementation\n" |
michael@0 | 993 | "STACK WIN 4 11136 16 5 0 8 0 0 0 1 $T0 $ebp = $eip " |
michael@0 | 994 | "$T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L " |
michael@0 | 995 | "$T0 .cbSavedRegs - = $P $T0 8 + .cbParams + =\n" |
michael@0 | 996 | "STACK WIN 4 11151 7a 5 0 c 0 0 0 1 $T0 $ebp = $eip " |
michael@0 | 997 | "$T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L " |
michael@0 | 998 | "$T0 .cbSavedRegs - = $P $T0 8 + .cbParams + =\n"); |
michael@0 | 999 | SetModuleSymbols(&module6, // chrome.dll |
michael@0 | 1000 | "FILE 7038 some_file_name.h\n" |
michael@0 | 1001 | "FILE 839776 some_file_name.cc\n" |
michael@0 | 1002 | "FUNC 217fda 17 4 function_217fda\n" |
michael@0 | 1003 | "217fda 4 102 839776\n" |
michael@0 | 1004 | "FUNC 217ff1 a 4 function_217ff1\n" |
michael@0 | 1005 | "217ff1 0 594 7038\n" |
michael@0 | 1006 | "217ff1 a 596 7038\n" |
michael@0 | 1007 | "STACK WIN 0 217ff1 a 0 0 4 0 0 0 0 0\n"); |
michael@0 | 1008 | |
michael@0 | 1009 | Label frame0_esp, frame1_esp; |
michael@0 | 1010 | Label frame1_ebp, frame2_ebp, frame3_ebp; |
michael@0 | 1011 | stack_section.start() = 0x002ff290; |
michael@0 | 1012 | stack_section |
michael@0 | 1013 | .Mark(&frame0_esp) |
michael@0 | 1014 | .D32(0x771ef8c1) // EIP in frame 0 (system call) |
michael@0 | 1015 | .D32(0x75fa0a91) // return address of frame 0 |
michael@0 | 1016 | .Mark(&frame1_esp) |
michael@0 | 1017 | .D32(0x000017b0) // args to child |
michael@0 | 1018 | .D32(0x00000000) |
michael@0 | 1019 | .D32(0x002ff2d8) |
michael@0 | 1020 | .D32(0x88014a2e) |
michael@0 | 1021 | .D32(0x002ff364) |
michael@0 | 1022 | .D32(0x000017b0) |
michael@0 | 1023 | .D32(0x00000000) |
michael@0 | 1024 | .D32(0x00000024) |
michael@0 | 1025 | .D32(0x00000001) |
michael@0 | 1026 | .D32(0x00000000) |
michael@0 | 1027 | .D32(0x00000000) |
michael@0 | 1028 | .D32(0x00000000) |
michael@0 | 1029 | .D32(0x00000000) |
michael@0 | 1030 | .D32(0x00000000) |
michael@0 | 1031 | .D32(0x00000000) |
michael@0 | 1032 | .D32(0x00000000) |
michael@0 | 1033 | .D32(0x9e3b9800) |
michael@0 | 1034 | .D32(0xfffffff7) |
michael@0 | 1035 | .D32(0x00000000) |
michael@0 | 1036 | .D32(0x002ff2a4) |
michael@0 | 1037 | .D32(0x64a07ff1) // random value to be confused with a return address |
michael@0 | 1038 | .D32(0x002ff8dc) |
michael@0 | 1039 | .D32(0x75fc6590) // random value to be confused with a return address |
michael@0 | 1040 | .D32(0xfdd2c6ea) |
michael@0 | 1041 | .D32(0x00000000) |
michael@0 | 1042 | .Mark(&frame1_ebp) |
michael@0 | 1043 | .D32(frame2_ebp) // Child EBP |
michael@0 | 1044 | .D32(0x75741194) // return address of frame 1 |
michael@0 | 1045 | .D32(0x000017b0) // args to child |
michael@0 | 1046 | .D32(0x0036ee80) |
michael@0 | 1047 | .D32(0x00000000) |
michael@0 | 1048 | .D32(0x65bc7d14) |
michael@0 | 1049 | .Mark(&frame2_ebp) |
michael@0 | 1050 | .D32(frame3_ebp) // Child EBP |
michael@0 | 1051 | .D32(0x75741148) // return address of frame 2 |
michael@0 | 1052 | .D32(0x000017b0) // args to child |
michael@0 | 1053 | .D32(0x0036ee80) |
michael@0 | 1054 | .D32(0x00000000) |
michael@0 | 1055 | .Mark(&frame3_ebp) |
michael@0 | 1056 | .D32(0) // saved %ebp (stack end) |
michael@0 | 1057 | .D32(0); // saved %eip (stack end) |
michael@0 | 1058 | |
michael@0 | 1059 | RegionFromSection(); |
michael@0 | 1060 | raw_context.eip = 0x771ef8c1; // in ntdll::ZwWaitForSingleObject |
michael@0 | 1061 | raw_context.esp = stack_section.start().Value(); |
michael@0 | 1062 | ASSERT_TRUE(raw_context.esp == frame0_esp.Value()); |
michael@0 | 1063 | raw_context.ebp = frame1_ebp.Value(); |
michael@0 | 1064 | |
michael@0 | 1065 | StackFrameSymbolizer frame_symbolizer(&supplier, &resolver); |
michael@0 | 1066 | StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules, |
michael@0 | 1067 | &frame_symbolizer); |
michael@0 | 1068 | vector<const CodeModule*> modules_without_symbols; |
michael@0 | 1069 | ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols)); |
michael@0 | 1070 | ASSERT_EQ(0U, modules_without_symbols.size()); |
michael@0 | 1071 | frames = call_stack.frames(); |
michael@0 | 1072 | |
michael@0 | 1073 | ASSERT_EQ(4U, frames->size()); |
michael@0 | 1074 | |
michael@0 | 1075 | { // To avoid reusing locals by mistake |
michael@0 | 1076 | StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0)); |
michael@0 | 1077 | EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); |
michael@0 | 1078 | ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); |
michael@0 | 1079 | EXPECT_EQ(0x771ef8c1U, frame0->instruction); |
michael@0 | 1080 | EXPECT_EQ(0x771ef8c1U, frame0->context.eip); |
michael@0 | 1081 | EXPECT_EQ(frame0_esp.Value(), frame0->context.esp); |
michael@0 | 1082 | EXPECT_EQ(frame1_ebp.Value(), frame0->context.ebp); |
michael@0 | 1083 | EXPECT_EQ(&module3, frame0->module); |
michael@0 | 1084 | EXPECT_EQ("ZwWaitForSingleObject", frame0->function_name); |
michael@0 | 1085 | // The STACK WIN record for module3!ZwWaitForSingleObject should have |
michael@0 | 1086 | // produced a fully populated WindowsFrameInfo structure. |
michael@0 | 1087 | ASSERT_TRUE(frame0->windows_frame_info != NULL); |
michael@0 | 1088 | EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame0->windows_frame_info->valid); |
michael@0 | 1089 | EXPECT_EQ(WindowsFrameInfo::STACK_INFO_FPO, |
michael@0 | 1090 | frame0->windows_frame_info->type_); |
michael@0 | 1091 | EXPECT_EQ("", frame0->windows_frame_info->program_string); |
michael@0 | 1092 | EXPECT_FALSE(frame0->windows_frame_info->allocates_base_pointer); |
michael@0 | 1093 | } |
michael@0 | 1094 | |
michael@0 | 1095 | { // To avoid reusing locals by mistake |
michael@0 | 1096 | StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1)); |
michael@0 | 1097 | EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust); |
michael@0 | 1098 | ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP |
michael@0 | 1099 | | StackFrameX86::CONTEXT_VALID_ESP |
michael@0 | 1100 | | StackFrameX86::CONTEXT_VALID_EBP), |
michael@0 | 1101 | frame1->context_validity); |
michael@0 | 1102 | EXPECT_EQ(0x75fa0a91U, frame1->instruction + 1); |
michael@0 | 1103 | EXPECT_EQ(0x75fa0a91U, frame1->context.eip); |
michael@0 | 1104 | EXPECT_EQ(frame1_esp.Value(), frame1->context.esp); |
michael@0 | 1105 | EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp); |
michael@0 | 1106 | EXPECT_EQ(&module4, frame1->module); |
michael@0 | 1107 | EXPECT_EQ("WaitForSingleObjectEx", frame1->function_name); |
michael@0 | 1108 | // The STACK WIN record for module4!WaitForSingleObjectEx should have |
michael@0 | 1109 | // produced a fully populated WindowsFrameInfo structure. |
michael@0 | 1110 | ASSERT_TRUE(frame1->windows_frame_info != NULL); |
michael@0 | 1111 | EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame1->windows_frame_info->valid); |
michael@0 | 1112 | EXPECT_EQ(WindowsFrameInfo::STACK_INFO_FRAME_DATA, |
michael@0 | 1113 | frame1->windows_frame_info->type_); |
michael@0 | 1114 | EXPECT_EQ("$T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L " |
michael@0 | 1115 | "$T0 .cbSavedRegs - = $P $T0 8 + .cbParams + =", |
michael@0 | 1116 | frame1->windows_frame_info->program_string); |
michael@0 | 1117 | EXPECT_FALSE(frame1->windows_frame_info->allocates_base_pointer); |
michael@0 | 1118 | } |
michael@0 | 1119 | } |
michael@0 | 1120 | |
michael@0 | 1121 | // Scan the stack for a better return address and potentially skip frames |
michael@0 | 1122 | // when the calculated return address is not in a known module. |
michael@0 | 1123 | // Note, that the span of this scan is somewhat arbitrarily limited to 30 |
michael@0 | 1124 | // search words (pointers): |
michael@0 | 1125 | // const int kRASearchWords = 30; |
michael@0 | 1126 | // This means that frames can be skipped only when their size is relatively |
michael@0 | 1127 | // small: smaller than kRASearchWords * sizeof(InstructionType) |
michael@0 | 1128 | TEST_F(GetCallerFrame, ReturnAddressIsNotInKnownModule) { |
michael@0 | 1129 | MockCodeModule msvcrt_dll(0x77be0000, 0x58000, "msvcrt.dll", "version1"); |
michael@0 | 1130 | SetModuleSymbols(&msvcrt_dll, // msvcrt.dll |
michael@0 | 1131 | "PUBLIC 38180 0 wcsstr\n" |
michael@0 | 1132 | "STACK WIN 4 38180 61 10 0 8 0 0 0 1 $T0 $ebp = $eip $T0 " |
michael@0 | 1133 | "4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 .cbSavedRegs " |
michael@0 | 1134 | "- = $P $T0 4 + .cbParams + =\n"); |
michael@0 | 1135 | |
michael@0 | 1136 | MockCodeModule kernel32_dll(0x7c800000, 0x103000, "kernel32.dll", "version1"); |
michael@0 | 1137 | SetModuleSymbols(&kernel32_dll, // kernel32.dll |
michael@0 | 1138 | "PUBLIC efda 8 FindNextFileW\n" |
michael@0 | 1139 | "STACK WIN 4 efda 1bb c 0 8 8 3c 0 1 $T0 $ebp = $eip $T0 " |
michael@0 | 1140 | "4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 .cbSavedRegs " |
michael@0 | 1141 | "- = $P $T0 4 + .cbParams + =\n"); |
michael@0 | 1142 | |
michael@0 | 1143 | MockCodeModule chrome_dll(0x1c30000, 0x28C8000, "chrome.dll", "version1"); |
michael@0 | 1144 | SetModuleSymbols(&chrome_dll, // chrome.dll |
michael@0 | 1145 | "FUNC e3cff 4af 0 file_util::FileEnumerator::Next()\n" |
michael@0 | 1146 | "e3cff 1a 711 2505\n" |
michael@0 | 1147 | "STACK WIN 4 e3cff 4af 20 0 4 c 94 0 1 $T1 .raSearch = " |
michael@0 | 1148 | "$T0 $T1 4 - 8 @ = $ebp $T1 4 - ^ = $eip $T1 ^ = $esp " |
michael@0 | 1149 | "$T1 4 + = $20 $T0 152 - ^ = $23 $T0 156 - ^ = $24 " |
michael@0 | 1150 | "$T0 160 - ^ =\n"); |
michael@0 | 1151 | |
michael@0 | 1152 | // Create some modules with some stock debugging information. |
michael@0 | 1153 | MockCodeModules local_modules; |
michael@0 | 1154 | local_modules.Add(&msvcrt_dll); |
michael@0 | 1155 | local_modules.Add(&kernel32_dll); |
michael@0 | 1156 | local_modules.Add(&chrome_dll); |
michael@0 | 1157 | |
michael@0 | 1158 | Label frame0_esp; |
michael@0 | 1159 | Label frame0_ebp; |
michael@0 | 1160 | Label frame1_ebp; |
michael@0 | 1161 | Label frame2_ebp; |
michael@0 | 1162 | Label frame3_ebp; |
michael@0 | 1163 | |
michael@0 | 1164 | stack_section.start() = 0x0932f2d0; |
michael@0 | 1165 | stack_section |
michael@0 | 1166 | .Mark(&frame0_esp) |
michael@0 | 1167 | .D32(0x0764e000) |
michael@0 | 1168 | .D32(0x0764e068) |
michael@0 | 1169 | .Mark(&frame0_ebp) |
michael@0 | 1170 | .D32(frame1_ebp) // Child EBP |
michael@0 | 1171 | .D32(0x001767a0) // return address of frame 0 |
michael@0 | 1172 | // Not in known module |
michael@0 | 1173 | .D32(0x0764e0c6) |
michael@0 | 1174 | .D32(0x001bb1b8) |
michael@0 | 1175 | .D32(0x0764e068) |
michael@0 | 1176 | .D32(0x00000003) |
michael@0 | 1177 | .D32(0x0764e068) |
michael@0 | 1178 | .D32(0x00000003) |
michael@0 | 1179 | .D32(0x07578828) |
michael@0 | 1180 | .D32(0x0764e000) |
michael@0 | 1181 | .D32(0x00000000) |
michael@0 | 1182 | .D32(0x001c0010) |
michael@0 | 1183 | .D32(0x0764e0c6) |
michael@0 | 1184 | .Mark(&frame1_ebp) |
michael@0 | 1185 | .D32(frame2_ebp) // Child EBP |
michael@0 | 1186 | .D32(0x7c80f10f) // return address of frame 1 |
michael@0 | 1187 | // inside kernel32!FindNextFileW |
michael@0 | 1188 | .D32(0x000008f8) |
michael@0 | 1189 | .D32(0x00000000) |
michael@0 | 1190 | .D32(0x00000000) |
michael@0 | 1191 | .D32(0x00000000) |
michael@0 | 1192 | .D32(0x0932f34c) |
michael@0 | 1193 | .D32(0x0764e000) |
michael@0 | 1194 | .D32(0x00001000) |
michael@0 | 1195 | .D32(0x00000000) |
michael@0 | 1196 | .D32(0x00000001) |
michael@0 | 1197 | .D32(0x00000000) |
michael@0 | 1198 | .D32(0x00000000) |
michael@0 | 1199 | .D32(0x0932f6a8) |
michael@0 | 1200 | .D32(0x00000000) |
michael@0 | 1201 | .D32(0x0932f6d8) |
michael@0 | 1202 | .D32(0x00000000) |
michael@0 | 1203 | .D32(0x000000d6) |
michael@0 | 1204 | .D32(0x0764e000) |
michael@0 | 1205 | .D32(0x7ff9a000) |
michael@0 | 1206 | .D32(0x0932f3fc) |
michael@0 | 1207 | .D32(0x00000001) |
michael@0 | 1208 | .D32(0x00000001) |
michael@0 | 1209 | .D32(0x07578828) |
michael@0 | 1210 | .D32(0x0000002e) |
michael@0 | 1211 | .D32(0x0932f340) |
michael@0 | 1212 | .D32(0x0932eef4) |
michael@0 | 1213 | .D32(0x0932ffdc) |
michael@0 | 1214 | .D32(0x7c839ad8) |
michael@0 | 1215 | .D32(0x7c80f0d8) |
michael@0 | 1216 | .D32(0x00000000) |
michael@0 | 1217 | .Mark(&frame2_ebp) |
michael@0 | 1218 | .D32(frame3_ebp) // Child EBP |
michael@0 | 1219 | .D32(0x01d13f91) // return address of frame 2 |
michael@0 | 1220 | // inside chrome_dll!file_util::FileEnumerator::Next |
michael@0 | 1221 | .D32(0x07578828) |
michael@0 | 1222 | .D32(0x0932f6ac) |
michael@0 | 1223 | .D32(0x0932f9c4) |
michael@0 | 1224 | .D32(0x0932f9b4) |
michael@0 | 1225 | .D32(0x00000000) |
michael@0 | 1226 | .D32(0x00000003) |
michael@0 | 1227 | .D32(0x0932f978) |
michael@0 | 1228 | .D32(0x01094330) |
michael@0 | 1229 | .D32(0x00000000) |
michael@0 | 1230 | .D32(0x00000001) |
michael@0 | 1231 | .D32(0x01094330) |
michael@0 | 1232 | .D32(0x00000000) |
michael@0 | 1233 | .D32(0x00000000) |
michael@0 | 1234 | .D32(0x07f30000) |
michael@0 | 1235 | .D32(0x01c3ba17) |
michael@0 | 1236 | .D32(0x08bab840) |
michael@0 | 1237 | .D32(0x07f31580) |
michael@0 | 1238 | .D32(0x00000000) |
michael@0 | 1239 | .D32(0x00000007) |
michael@0 | 1240 | .D32(0x0932f940) |
michael@0 | 1241 | .D32(0x0000002e) |
michael@0 | 1242 | .D32(0x0932f40c) |
michael@0 | 1243 | .D32(0x01d13b53) |
michael@0 | 1244 | .D32(0x0932f958) |
michael@0 | 1245 | .D32(0x00000001) |
michael@0 | 1246 | .D32(0x00000007) |
michael@0 | 1247 | .D32(0x0932f940) |
michael@0 | 1248 | .D32(0x0000002e) |
michael@0 | 1249 | .D32(0x00000000) |
michael@0 | 1250 | .D32(0x0932f6ac) |
michael@0 | 1251 | .D32(0x01e13ef0) |
michael@0 | 1252 | .D32(0x00000001) |
michael@0 | 1253 | .D32(0x00000007) |
michael@0 | 1254 | .D32(0x0932f958) |
michael@0 | 1255 | .D32(0x08bab840) |
michael@0 | 1256 | .D32(0x0932f9b4) |
michael@0 | 1257 | .D32(0x00000000) |
michael@0 | 1258 | .D32(0x0932f9b4) |
michael@0 | 1259 | .D32(0x000000a7) |
michael@0 | 1260 | .D32(0x000000a7) |
michael@0 | 1261 | .D32(0x0932f998) |
michael@0 | 1262 | .D32(0x579627a2) |
michael@0 | 1263 | .Mark(&frame3_ebp) |
michael@0 | 1264 | .D32(0) // saved %ebp (stack end) |
michael@0 | 1265 | .D32(0); // saved %eip (stack end) |
michael@0 | 1266 | |
michael@0 | 1267 | RegionFromSection(); |
michael@0 | 1268 | raw_context.eip = 0x77c181cd; // inside msvcrt!wcsstr |
michael@0 | 1269 | raw_context.esp = frame0_esp.Value(); |
michael@0 | 1270 | raw_context.ebp = frame0_ebp.Value(); |
michael@0 | 1271 | // sanity |
michael@0 | 1272 | ASSERT_TRUE(raw_context.esp == stack_section.start().Value()); |
michael@0 | 1273 | ASSERT_TRUE(raw_context.ebp == stack_section.start().Value() + 8); |
michael@0 | 1274 | |
michael@0 | 1275 | StackFrameSymbolizer frame_symbolizer(&supplier, &resolver); |
michael@0 | 1276 | StackwalkerX86 walker(&system_info, &raw_context, &stack_region, |
michael@0 | 1277 | &local_modules, &frame_symbolizer); |
michael@0 | 1278 | vector<const CodeModule*> modules_without_symbols; |
michael@0 | 1279 | ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols)); |
michael@0 | 1280 | ASSERT_EQ(0U, modules_without_symbols.size()); |
michael@0 | 1281 | frames = call_stack.frames(); |
michael@0 | 1282 | |
michael@0 | 1283 | ASSERT_EQ(3U, frames->size()); |
michael@0 | 1284 | |
michael@0 | 1285 | { // To avoid reusing locals by mistake |
michael@0 | 1286 | StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0)); |
michael@0 | 1287 | EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); |
michael@0 | 1288 | ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); |
michael@0 | 1289 | EXPECT_EQ(0x77c181cdU, frame0->instruction); |
michael@0 | 1290 | EXPECT_EQ(0x77c181cdU, frame0->context.eip); |
michael@0 | 1291 | EXPECT_EQ(frame0_esp.Value(), frame0->context.esp); |
michael@0 | 1292 | EXPECT_EQ(frame0_ebp.Value(), frame0->context.ebp); |
michael@0 | 1293 | EXPECT_EQ(&msvcrt_dll, frame0->module); |
michael@0 | 1294 | EXPECT_EQ("wcsstr", frame0->function_name); |
michael@0 | 1295 | ASSERT_TRUE(frame0->windows_frame_info != NULL); |
michael@0 | 1296 | EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame0->windows_frame_info->valid); |
michael@0 | 1297 | EXPECT_EQ(WindowsFrameInfo::STACK_INFO_FRAME_DATA, |
michael@0 | 1298 | frame0->windows_frame_info->type_); |
michael@0 | 1299 | EXPECT_EQ("$T0 $ebp = $eip $T0 " |
michael@0 | 1300 | "4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 .cbSavedRegs " |
michael@0 | 1301 | "- = $P $T0 4 + .cbParams + =", |
michael@0 | 1302 | frame0->windows_frame_info->program_string); |
michael@0 | 1303 | // It has program string, so allocates_base_pointer is not expected |
michael@0 | 1304 | EXPECT_FALSE(frame0->windows_frame_info->allocates_base_pointer); |
michael@0 | 1305 | } |
michael@0 | 1306 | |
michael@0 | 1307 | { // To avoid reusing locals by mistake |
michael@0 | 1308 | StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1)); |
michael@0 | 1309 | EXPECT_EQ(StackFrame::FRAME_TRUST_CFI_SCAN, frame1->trust); |
michael@0 | 1310 | ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP | |
michael@0 | 1311 | StackFrameX86::CONTEXT_VALID_ESP | |
michael@0 | 1312 | StackFrameX86::CONTEXT_VALID_EBP), |
michael@0 | 1313 | frame1->context_validity); |
michael@0 | 1314 | EXPECT_EQ(0x7c80f10fU, frame1->instruction + 1); |
michael@0 | 1315 | EXPECT_EQ(0x7c80f10fU, frame1->context.eip); |
michael@0 | 1316 | // frame 1 was skipped, so intead of frame1_ebp compare with frame2_ebp. |
michael@0 | 1317 | EXPECT_EQ(frame2_ebp.Value(), frame1->context.ebp); |
michael@0 | 1318 | EXPECT_EQ(&kernel32_dll, frame1->module); |
michael@0 | 1319 | EXPECT_EQ("FindNextFileW", frame1->function_name); |
michael@0 | 1320 | ASSERT_TRUE(frame1->windows_frame_info != NULL); |
michael@0 | 1321 | EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame1->windows_frame_info->valid); |
michael@0 | 1322 | EXPECT_EQ(WindowsFrameInfo::STACK_INFO_FRAME_DATA, |
michael@0 | 1323 | frame1->windows_frame_info->type_); |
michael@0 | 1324 | EXPECT_EQ("$T0 $ebp = $eip $T0 " |
michael@0 | 1325 | "4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 .cbSavedRegs " |
michael@0 | 1326 | "- = $P $T0 4 + .cbParams + =", |
michael@0 | 1327 | frame1->windows_frame_info->program_string); |
michael@0 | 1328 | EXPECT_FALSE(frame1->windows_frame_info->allocates_base_pointer); |
michael@0 | 1329 | } |
michael@0 | 1330 | |
michael@0 | 1331 | { // To avoid reusing locals by mistake |
michael@0 | 1332 | StackFrameX86 *frame2 = static_cast<StackFrameX86 *>(frames->at(2)); |
michael@0 | 1333 | EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame2->trust); |
michael@0 | 1334 | ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP | |
michael@0 | 1335 | StackFrameX86::CONTEXT_VALID_ESP | |
michael@0 | 1336 | StackFrameX86::CONTEXT_VALID_EBP), |
michael@0 | 1337 | frame2->context_validity); |
michael@0 | 1338 | EXPECT_EQ(0x01d13f91U, frame2->instruction + 1); |
michael@0 | 1339 | EXPECT_EQ(0x01d13f91U, frame2->context.eip); |
michael@0 | 1340 | // frame 1 was skipped, so intead of frame2_ebp compare with frame3_ebp. |
michael@0 | 1341 | EXPECT_EQ(frame3_ebp.Value(), frame2->context.ebp); |
michael@0 | 1342 | EXPECT_EQ(&chrome_dll, frame2->module); |
michael@0 | 1343 | EXPECT_EQ("file_util::FileEnumerator::Next()", frame2->function_name); |
michael@0 | 1344 | ASSERT_TRUE(frame2->windows_frame_info != NULL); |
michael@0 | 1345 | EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame2->windows_frame_info->valid); |
michael@0 | 1346 | EXPECT_EQ(WindowsFrameInfo::STACK_INFO_FRAME_DATA, |
michael@0 | 1347 | frame2->windows_frame_info->type_); |
michael@0 | 1348 | EXPECT_EQ("$T1 .raSearch = " |
michael@0 | 1349 | "$T0 $T1 4 - 8 @ = $ebp $T1 4 - ^ = $eip $T1 ^ = $esp " |
michael@0 | 1350 | "$T1 4 + = $20 $T0 152 - ^ = $23 $T0 156 - ^ = $24 " |
michael@0 | 1351 | "$T0 160 - ^ =", |
michael@0 | 1352 | frame2->windows_frame_info->program_string); |
michael@0 | 1353 | EXPECT_FALSE(frame2->windows_frame_info->allocates_base_pointer); |
michael@0 | 1354 | } |
michael@0 | 1355 | } |
michael@0 | 1356 | |
michael@0 | 1357 | struct CFIFixture: public StackwalkerX86Fixture { |
michael@0 | 1358 | CFIFixture() { |
michael@0 | 1359 | // Provide a bunch of STACK CFI records; individual tests walk to the |
michael@0 | 1360 | // caller from every point in this series, expecting to find the same |
michael@0 | 1361 | // set of register values. |
michael@0 | 1362 | SetModuleSymbols(&module1, |
michael@0 | 1363 | // The youngest frame's function. |
michael@0 | 1364 | "FUNC 4000 1000 10 enchiridion\n" |
michael@0 | 1365 | // Initially, just a return address. |
michael@0 | 1366 | "STACK CFI INIT 4000 100 .cfa: $esp 4 + .ra: .cfa 4 - ^\n" |
michael@0 | 1367 | // Push %ebx. |
michael@0 | 1368 | "STACK CFI 4001 .cfa: $esp 8 + $ebx: .cfa 8 - ^\n" |
michael@0 | 1369 | // Move %esi into %ebx. Weird, but permitted. |
michael@0 | 1370 | "STACK CFI 4002 $esi: $ebx\n" |
michael@0 | 1371 | // Allocate frame space, and save %edi. |
michael@0 | 1372 | "STACK CFI 4003 .cfa: $esp 20 + $edi: .cfa 16 - ^\n" |
michael@0 | 1373 | // Put the return address in %edi. |
michael@0 | 1374 | "STACK CFI 4005 .ra: $edi\n" |
michael@0 | 1375 | // Save %ebp, and use it as a frame pointer. |
michael@0 | 1376 | "STACK CFI 4006 .cfa: $ebp 8 + $ebp: .cfa 12 - ^\n" |
michael@0 | 1377 | |
michael@0 | 1378 | // The calling function. |
michael@0 | 1379 | "FUNC 5000 1000 10 epictetus\n" |
michael@0 | 1380 | // Mark it as end of stack. |
michael@0 | 1381 | "STACK CFI INIT 5000 1000 .cfa: $esp .ra 0\n"); |
michael@0 | 1382 | |
michael@0 | 1383 | // Provide some distinctive values for the caller's registers. |
michael@0 | 1384 | expected.esp = 0x80000000; |
michael@0 | 1385 | expected.eip = 0x40005510; |
michael@0 | 1386 | expected.ebp = 0xc0d4aab9; |
michael@0 | 1387 | expected.ebx = 0x60f20ce6; |
michael@0 | 1388 | expected.esi = 0x53d1379d; |
michael@0 | 1389 | expected.edi = 0xafbae234; |
michael@0 | 1390 | |
michael@0 | 1391 | // By default, registers are unchanged. |
michael@0 | 1392 | raw_context = expected; |
michael@0 | 1393 | } |
michael@0 | 1394 | |
michael@0 | 1395 | // Walk the stack, using stack_section as the contents of the stack |
michael@0 | 1396 | // and raw_context as the current register values. (Set |
michael@0 | 1397 | // raw_context.esp to the stack's starting address.) Expect two |
michael@0 | 1398 | // stack frames; in the older frame, expect the callee-saves |
michael@0 | 1399 | // registers to have values matching those in 'expected'. |
michael@0 | 1400 | void CheckWalk() { |
michael@0 | 1401 | RegionFromSection(); |
michael@0 | 1402 | raw_context.esp = stack_section.start().Value(); |
michael@0 | 1403 | |
michael@0 | 1404 | StackFrameSymbolizer frame_symbolizer(&supplier, &resolver); |
michael@0 | 1405 | StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules, |
michael@0 | 1406 | &frame_symbolizer); |
michael@0 | 1407 | vector<const CodeModule*> modules_without_symbols; |
michael@0 | 1408 | ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols)); |
michael@0 | 1409 | ASSERT_EQ(0U, modules_without_symbols.size()); |
michael@0 | 1410 | frames = call_stack.frames(); |
michael@0 | 1411 | ASSERT_EQ(2U, frames->size()); |
michael@0 | 1412 | |
michael@0 | 1413 | { // To avoid reusing locals by mistake |
michael@0 | 1414 | StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0)); |
michael@0 | 1415 | EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); |
michael@0 | 1416 | ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); |
michael@0 | 1417 | EXPECT_EQ("enchiridion", frame0->function_name); |
michael@0 | 1418 | EXPECT_EQ(0x40004000U, frame0->function_base); |
michael@0 | 1419 | ASSERT_TRUE(frame0->windows_frame_info != NULL); |
michael@0 | 1420 | ASSERT_EQ(WindowsFrameInfo::VALID_PARAMETER_SIZE, |
michael@0 | 1421 | frame0->windows_frame_info->valid); |
michael@0 | 1422 | ASSERT_TRUE(frame0->cfi_frame_info != NULL); |
michael@0 | 1423 | } |
michael@0 | 1424 | |
michael@0 | 1425 | { // To avoid reusing locals by mistake |
michael@0 | 1426 | StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1)); |
michael@0 | 1427 | EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust); |
michael@0 | 1428 | ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP | |
michael@0 | 1429 | StackFrameX86::CONTEXT_VALID_ESP | |
michael@0 | 1430 | StackFrameX86::CONTEXT_VALID_EBP | |
michael@0 | 1431 | StackFrameX86::CONTEXT_VALID_EBX | |
michael@0 | 1432 | StackFrameX86::CONTEXT_VALID_ESI | |
michael@0 | 1433 | StackFrameX86::CONTEXT_VALID_EDI), |
michael@0 | 1434 | frame1->context_validity); |
michael@0 | 1435 | EXPECT_EQ(expected.eip, frame1->context.eip); |
michael@0 | 1436 | EXPECT_EQ(expected.esp, frame1->context.esp); |
michael@0 | 1437 | EXPECT_EQ(expected.ebp, frame1->context.ebp); |
michael@0 | 1438 | EXPECT_EQ(expected.ebx, frame1->context.ebx); |
michael@0 | 1439 | EXPECT_EQ(expected.esi, frame1->context.esi); |
michael@0 | 1440 | EXPECT_EQ(expected.edi, frame1->context.edi); |
michael@0 | 1441 | EXPECT_EQ("epictetus", frame1->function_name); |
michael@0 | 1442 | } |
michael@0 | 1443 | } |
michael@0 | 1444 | |
michael@0 | 1445 | // The values the stack walker should find for the caller's registers. |
michael@0 | 1446 | MDRawContextX86 expected; |
michael@0 | 1447 | }; |
michael@0 | 1448 | |
michael@0 | 1449 | class CFI: public CFIFixture, public Test { }; |
michael@0 | 1450 | |
michael@0 | 1451 | TEST_F(CFI, At4000) { |
michael@0 | 1452 | Label frame1_esp = expected.esp; |
michael@0 | 1453 | stack_section |
michael@0 | 1454 | .D32(0x40005510) // return address |
michael@0 | 1455 | .Mark(&frame1_esp); // This effectively sets stack_section.start(). |
michael@0 | 1456 | raw_context.eip = 0x40004000; |
michael@0 | 1457 | CheckWalk(); |
michael@0 | 1458 | } |
michael@0 | 1459 | |
michael@0 | 1460 | TEST_F(CFI, At4001) { |
michael@0 | 1461 | Label frame1_esp = expected.esp; |
michael@0 | 1462 | stack_section |
michael@0 | 1463 | .D32(0x60f20ce6) // saved %ebx |
michael@0 | 1464 | .D32(0x40005510) // return address |
michael@0 | 1465 | .Mark(&frame1_esp); // This effectively sets stack_section.start(). |
michael@0 | 1466 | raw_context.eip = 0x40004001; |
michael@0 | 1467 | raw_context.ebx = 0x91aa9a8b; // callee's %ebx value |
michael@0 | 1468 | CheckWalk(); |
michael@0 | 1469 | } |
michael@0 | 1470 | |
michael@0 | 1471 | TEST_F(CFI, At4002) { |
michael@0 | 1472 | Label frame1_esp = expected.esp; |
michael@0 | 1473 | stack_section |
michael@0 | 1474 | .D32(0x60f20ce6) // saved %ebx |
michael@0 | 1475 | .D32(0x40005510) // return address |
michael@0 | 1476 | .Mark(&frame1_esp); // This effectively sets stack_section.start(). |
michael@0 | 1477 | raw_context.eip = 0x40004002; |
michael@0 | 1478 | raw_context.ebx = 0x53d1379d; // saved %esi |
michael@0 | 1479 | raw_context.esi = 0xa5c790ed; // callee's %esi value |
michael@0 | 1480 | CheckWalk(); |
michael@0 | 1481 | } |
michael@0 | 1482 | |
michael@0 | 1483 | TEST_F(CFI, At4003) { |
michael@0 | 1484 | Label frame1_esp = expected.esp; |
michael@0 | 1485 | stack_section |
michael@0 | 1486 | .D32(0x56ec3db7) // garbage |
michael@0 | 1487 | .D32(0xafbae234) // saved %edi |
michael@0 | 1488 | .D32(0x53d67131) // garbage |
michael@0 | 1489 | .D32(0x60f20ce6) // saved %ebx |
michael@0 | 1490 | .D32(0x40005510) // return address |
michael@0 | 1491 | .Mark(&frame1_esp); // This effectively sets stack_section.start(). |
michael@0 | 1492 | raw_context.eip = 0x40004003; |
michael@0 | 1493 | raw_context.ebx = 0x53d1379d; // saved %esi |
michael@0 | 1494 | raw_context.esi = 0xa97f229d; // callee's %esi |
michael@0 | 1495 | raw_context.edi = 0xb05cc997; // callee's %edi |
michael@0 | 1496 | CheckWalk(); |
michael@0 | 1497 | } |
michael@0 | 1498 | |
michael@0 | 1499 | // The results here should be the same as those at module offset |
michael@0 | 1500 | // 0x4003. |
michael@0 | 1501 | TEST_F(CFI, At4004) { |
michael@0 | 1502 | Label frame1_esp = expected.esp; |
michael@0 | 1503 | stack_section |
michael@0 | 1504 | .D32(0xe29782c2) // garbage |
michael@0 | 1505 | .D32(0xafbae234) // saved %edi |
michael@0 | 1506 | .D32(0x5ba29ce9) // garbage |
michael@0 | 1507 | .D32(0x60f20ce6) // saved %ebx |
michael@0 | 1508 | .D32(0x40005510) // return address |
michael@0 | 1509 | .Mark(&frame1_esp); // This effectively sets stack_section.start(). |
michael@0 | 1510 | raw_context.eip = 0x40004004; |
michael@0 | 1511 | raw_context.ebx = 0x53d1379d; // saved %esi |
michael@0 | 1512 | raw_context.esi = 0x0fb7dc4e; // callee's %esi |
michael@0 | 1513 | raw_context.edi = 0x993b4280; // callee's %edi |
michael@0 | 1514 | CheckWalk(); |
michael@0 | 1515 | } |
michael@0 | 1516 | |
michael@0 | 1517 | TEST_F(CFI, At4005) { |
michael@0 | 1518 | Label frame1_esp = expected.esp; |
michael@0 | 1519 | stack_section |
michael@0 | 1520 | .D32(0xe29782c2) // garbage |
michael@0 | 1521 | .D32(0xafbae234) // saved %edi |
michael@0 | 1522 | .D32(0x5ba29ce9) // garbage |
michael@0 | 1523 | .D32(0x60f20ce6) // saved %ebx |
michael@0 | 1524 | .D32(0x8036cc02) // garbage |
michael@0 | 1525 | .Mark(&frame1_esp); // This effectively sets stack_section.start(). |
michael@0 | 1526 | raw_context.eip = 0x40004005; |
michael@0 | 1527 | raw_context.ebx = 0x53d1379d; // saved %esi |
michael@0 | 1528 | raw_context.esi = 0x0fb7dc4e; // callee's %esi |
michael@0 | 1529 | raw_context.edi = 0x40005510; // return address |
michael@0 | 1530 | CheckWalk(); |
michael@0 | 1531 | } |
michael@0 | 1532 | |
michael@0 | 1533 | TEST_F(CFI, At4006) { |
michael@0 | 1534 | Label frame0_ebp; |
michael@0 | 1535 | Label frame1_esp = expected.esp; |
michael@0 | 1536 | stack_section |
michael@0 | 1537 | .D32(0xdcdd25cd) // garbage |
michael@0 | 1538 | .D32(0xafbae234) // saved %edi |
michael@0 | 1539 | .D32(0xc0d4aab9) // saved %ebp |
michael@0 | 1540 | .Mark(&frame0_ebp) // frame pointer points here |
michael@0 | 1541 | .D32(0x60f20ce6) // saved %ebx |
michael@0 | 1542 | .D32(0x8036cc02) // garbage |
michael@0 | 1543 | .Mark(&frame1_esp); // This effectively sets stack_section.start(). |
michael@0 | 1544 | raw_context.eip = 0x40004006; |
michael@0 | 1545 | raw_context.ebp = frame0_ebp.Value(); |
michael@0 | 1546 | raw_context.ebx = 0x53d1379d; // saved %esi |
michael@0 | 1547 | raw_context.esi = 0x743833c9; // callee's %esi |
michael@0 | 1548 | raw_context.edi = 0x40005510; // return address |
michael@0 | 1549 | CheckWalk(); |
michael@0 | 1550 | } |
michael@0 | 1551 |