aboutsummaryrefslogtreecommitdiff
path: root/src/processor/stackwalker_x86.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/processor/stackwalker_x86.cc')
-rw-r--r--src/processor/stackwalker_x86.cc103
1 files changed, 103 insertions, 0 deletions
diff --git a/src/processor/stackwalker_x86.cc b/src/processor/stackwalker_x86.cc
new file mode 100644
index 00000000..d3360caf
--- /dev/null
+++ b/src/processor/stackwalker_x86.cc
@@ -0,0 +1,103 @@
+// Copyright (C) 2006 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// stackwalker_x86.cc: x86-specific stackwalker.
+//
+// See stackwalker_x86.h for documentation.
+//
+// Author: Mark Mentovai
+
+
+#include "processor/stackwalker_x86.h"
+#include "processor/minidump.h"
+
+
+namespace google_airbag {
+
+
+StackwalkerX86::StackwalkerX86(MinidumpContext* context,
+ MemoryRegion* memory,
+ MinidumpModuleList* modules)
+ : Stackwalker(memory, modules)
+ , last_frame_pointer_(0) {
+ if (memory_->GetBase() + memory_->GetSize() - 1 > 0xffffffff) {
+ // The x86 is a 32-bit CPU, the limits of the supplied stack are invalid.
+ // Mark memory_ = NULL, which will cause stackwalking to fail.
+ memory_ = NULL;
+ }
+
+ // TODO(mmentovai): verify that |context| is x86 when Minidump supports
+ // other CPU types.
+ context_ = context->context();
+}
+
+
+bool StackwalkerX86::GetContextFrame(StackFrame* frame) {
+ if (!context_ || !memory_ || !frame)
+ return false;
+
+ // The frame and instruction pointers are stored directly in registers,
+ // so pull them straight out of the CPU context structure.
+ frame->frame_pointer = last_frame_pointer_ = context_->ebp;
+ frame->instruction = context_->eip;
+
+ return true;
+}
+
+
+bool StackwalkerX86::GetCallerFrame(StackFrame* frame) {
+ if (!memory_ || !frame)
+ return false;
+
+ // The frame and instruction pointers for previous frames are saved on the
+ // stack. The typical x86 calling convention, when frame pointers are
+ // present, is for the calling procedure to use CALL, which pushes the
+ // return address onto the stack and sets the instruction pointer (%eip)
+ // to the entry point of the called routine. The called routine's then
+ // PUSHes the calling routine's frame pointer (%ebp) onto the stack before
+ // copying the stack pointer (%esp) to the frame pointer (%ebp). Therefore,
+ // the calling procedure's frame pointer is always available by
+ // dereferencing the called procedure's frame pointer, and the return
+ // address is always available at the memory location immediately above
+ // the address pointed to by the called procedure's frame pointer.
+
+ // If there is no frame pointer, determining the layout of the stack is
+ // considerably more difficult, requiring debugging information. This
+ // stackwalker doesn't attempt to solve that problem (at this point).
+
+ // Don't pass frame.frame_pointer or frame.instruction directly
+ // ReadMemory, because their types are too wide (64-bit), and we
+ // specifically want to read 32-bit quantities for both.
+ u_int32_t frame_pointer;
+ if (!memory_->GetMemoryAtAddress(last_frame_pointer_, &frame_pointer))
+ return false;
+
+ // A caller frame must reside higher in memory than its callee frames.
+ // Anything else is an error, or an indication that we've reached the
+ // end of the stack.
+ if (frame_pointer <= last_frame_pointer_)
+ return false;
+
+ u_int32_t instruction;
+ if (!memory_->GetMemoryAtAddress(last_frame_pointer_ + 4, &instruction))
+ return false;
+
+ frame->frame_pointer = last_frame_pointer_ = frame_pointer;
+ frame->instruction = instruction;
+
+ return true;
+}
+
+
+} // namespace google_airbag