From 8eb7111814953cb64ec0569b91ea99804b2d5b85 Mon Sep 17 00:00:00 2001 From: "ted.mielczarek" Date: Wed, 31 Oct 2007 19:20:31 +0000 Subject: [PATCH] Issue 196 - Breakpad processor support for x86-64. r=mento git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@227 4c0a9323-5329-0410-9bdc-e9ce6186880e --- Makefile.am | 5 + Makefile.in | 23 +- src/google_breakpad/common/minidump_format.h | 154 +++++ src/google_breakpad/processor/minidump.h | 15 +- .../processor/stack_frame_cpu.h | 26 + src/processor/minidump.cc | 585 ++++++++++++------ src/processor/minidump_processor.cc | 10 +- src/processor/minidump_stackwalk.cc | 12 +- src/processor/stackwalker.cc | 8 + src/processor/stackwalker_amd64.cc | 131 ++++ src/processor/stackwalker_amd64.h | 80 +++ 11 files changed, 833 insertions(+), 216 deletions(-) create mode 100644 src/processor/stackwalker_amd64.cc create mode 100644 src/processor/stackwalker_amd64.h diff --git a/Makefile.am b/Makefile.am index 2edfd94e..f6e331d1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -93,6 +93,8 @@ src_libbreakpad_la_SOURCES = \ src/processor/simple_symbol_supplier.h \ src/processor/stack_frame_info.h \ src/processor/stackwalker.cc \ + src/processor/stackwalker_amd64.cc \ + src/processor/stackwalker_amd64.h \ src/processor/stackwalker_ppc.cc \ src/processor/stackwalker_ppc.h \ src/processor/stackwalker_sparc.cc \ @@ -161,6 +163,7 @@ src_processor_minidump_processor_unittest_LDADD = \ src/processor/pathname_stripper.lo \ src/processor/process_state.lo \ src/processor/stackwalker.lo \ + src/processor/stackwalker_amd64.lo \ src/processor/stackwalker_ppc.lo \ src/processor/stackwalker_sparc.lo \ src/processor/stackwalker_x86.lo @@ -192,6 +195,7 @@ src_processor_stackwalker_selftest_LDADD = \ src/processor/minidump.lo \ src/processor/pathname_stripper.lo \ src/processor/stackwalker.lo \ + src/processor/stackwalker_amd64.lo \ src/processor/stackwalker_ppc.lo \ src/processor/stackwalker_sparc.lo \ src/processor/stackwalker_x86.lo @@ -221,6 +225,7 @@ src_processor_minidump_stackwalk_LDADD = \ src/processor/process_state.lo \ src/processor/simple_symbol_supplier.lo \ src/processor/stackwalker.lo \ + src/processor/stackwalker_amd64.lo \ src/processor/stackwalker_ppc.lo \ src/processor/stackwalker_sparc.lo \ src/processor/stackwalker_x86.lo diff --git a/Makefile.in b/Makefile.in index f4a81182..a7ec92ae 100644 --- a/Makefile.in +++ b/Makefile.in @@ -112,7 +112,9 @@ am_src_libbreakpad_la_OBJECTS = src/processor/basic_code_modules.lo \ src/processor/pathname_stripper.lo \ src/processor/process_state.lo \ src/processor/simple_symbol_supplier.lo \ - src/processor/stackwalker.lo src/processor/stackwalker_ppc.lo \ + src/processor/stackwalker.lo \ + src/processor/stackwalker_amd64.lo \ + src/processor/stackwalker_ppc.lo \ src/processor/stackwalker_sparc.lo \ src/processor/stackwalker_x86.lo src_libbreakpad_la_OBJECTS = $(am_src_libbreakpad_la_OBJECTS) @@ -156,6 +158,7 @@ src_processor_minidump_processor_unittest_DEPENDENCIES = \ src/processor/minidump_processor.lo src/processor/minidump.lo \ src/processor/pathname_stripper.lo \ src/processor/process_state.lo src/processor/stackwalker.lo \ + src/processor/stackwalker_amd64.lo \ src/processor/stackwalker_ppc.lo \ src/processor/stackwalker_sparc.lo \ src/processor/stackwalker_x86.lo @@ -171,7 +174,9 @@ src_processor_minidump_stackwalk_DEPENDENCIES = \ src/processor/pathname_stripper.lo \ src/processor/process_state.lo \ src/processor/simple_symbol_supplier.lo \ - src/processor/stackwalker.lo src/processor/stackwalker_ppc.lo \ + src/processor/stackwalker.lo \ + src/processor/stackwalker_amd64.lo \ + src/processor/stackwalker_ppc.lo \ src/processor/stackwalker_sparc.lo \ src/processor/stackwalker_x86.lo am_src_processor_pathname_stripper_unittest_OBJECTS = \ @@ -201,7 +206,9 @@ src_processor_stackwalker_selftest_DEPENDENCIES = \ src/processor/basic_source_line_resolver.lo \ src/processor/call_stack.lo src/processor/logging.lo \ src/processor/minidump.lo src/processor/pathname_stripper.lo \ - src/processor/stackwalker.lo src/processor/stackwalker_ppc.lo \ + src/processor/stackwalker.lo \ + src/processor/stackwalker_amd64.lo \ + src/processor/stackwalker_ppc.lo \ src/processor/stackwalker_sparc.lo \ src/processor/stackwalker_x86.lo SCRIPTS = $(noinst_SCRIPTS) @@ -425,6 +432,8 @@ src_libbreakpad_la_SOURCES = \ src/processor/simple_symbol_supplier.h \ src/processor/stack_frame_info.h \ src/processor/stackwalker.cc \ + src/processor/stackwalker_amd64.cc \ + src/processor/stackwalker_amd64.h \ src/processor/stackwalker_ppc.cc \ src/processor/stackwalker_ppc.h \ src/processor/stackwalker_sparc.cc \ @@ -474,6 +483,7 @@ src_processor_minidump_processor_unittest_LDADD = \ src/processor/pathname_stripper.lo \ src/processor/process_state.lo \ src/processor/stackwalker.lo \ + src/processor/stackwalker_amd64.lo \ src/processor/stackwalker_ppc.lo \ src/processor/stackwalker_sparc.lo \ src/processor/stackwalker_x86.lo @@ -509,6 +519,7 @@ src_processor_stackwalker_selftest_LDADD = \ src/processor/minidump.lo \ src/processor/pathname_stripper.lo \ src/processor/stackwalker.lo \ + src/processor/stackwalker_amd64.lo \ src/processor/stackwalker_ppc.lo \ src/processor/stackwalker_sparc.lo \ src/processor/stackwalker_x86.lo @@ -537,6 +548,7 @@ src_processor_minidump_stackwalk_LDADD = \ src/processor/process_state.lo \ src/processor/simple_symbol_supplier.lo \ src/processor/stackwalker.lo \ + src/processor/stackwalker_amd64.lo \ src/processor/stackwalker_ppc.lo \ src/processor/stackwalker_sparc.lo \ src/processor/stackwalker_x86.lo @@ -781,6 +793,8 @@ src/processor/simple_symbol_supplier.lo: \ src/processor/$(DEPDIR)/$(am__dirstamp) src/processor/stackwalker.lo: src/processor/$(am__dirstamp) \ src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/stackwalker_amd64.lo: src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) src/processor/stackwalker_ppc.lo: src/processor/$(am__dirstamp) \ src/processor/$(DEPDIR)/$(am__dirstamp) src/processor/stackwalker_sparc.lo: src/processor/$(am__dirstamp) \ @@ -925,6 +939,8 @@ mostlyclean-compile: -rm -f src/processor/simple_symbol_supplier.lo -rm -f src/processor/stackwalker.$(OBJEXT) -rm -f src/processor/stackwalker.lo + -rm -f src/processor/stackwalker_amd64.$(OBJEXT) + -rm -f src/processor/stackwalker_amd64.lo -rm -f src/processor/stackwalker_ppc.$(OBJEXT) -rm -f src/processor/stackwalker_ppc.lo -rm -f src/processor/stackwalker_selftest.$(OBJEXT) @@ -955,6 +971,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/range_map_unittest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/simple_symbol_supplier.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_amd64.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_ppc.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_selftest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_sparc.Plo@am__quote@ diff --git a/src/google_breakpad/common/minidump_format.h b/src/google_breakpad/common/minidump_format.h index cb6811e0..229bb9b2 100644 --- a/src/google_breakpad/common/minidump_format.h +++ b/src/google_breakpad/common/minidump_format.h @@ -215,6 +215,160 @@ typedef struct { #define MD_CONTEXT_CPU_MASK 0xffffffc0 +/* + * AMD64 support, see WINNT.H + */ + +typedef struct { + u_int16_t control_word; + u_int16_t status_word; + u_int8_t tag_word; + u_int8_t reserved1; + u_int16_t error_opcode; + u_int32_t error_offset; + u_int16_t error_selector; + u_int16_t reserved2; + u_int32_t data_offset; + u_int16_t data_selector; + u_int16_t reserved3; + u_int32_t mx_csr; + u_int32_t mx_csr_mask; + u_int128_t float_registers[8]; + u_int128_t xmm_registers[16]; + u_int8_t reserved4[96]; +} MDXmmSaveArea32AMD64; /* XMM_SAVE_AREA32 */ + +#define MD_CONTEXT_AMD64_VR_COUNT 26 + +typedef struct { + /* + * Register parameter home addresses. + */ + u_int64_t p1_home; + u_int64_t p2_home; + u_int64_t p3_home; + u_int64_t p4_home; + u_int64_t p5_home; + u_int64_t p6_home; + + /* The next field determines the layout of the structure, and which parts + * of it are populated */ + u_int32_t context_flags; + u_int32_t mx_csr; + + /* The next register is included with MD_CONTEXT_AMD64_CONTROL */ + u_int16_t cs; + + /* The next 4 registers are included with MD_CONTEXT_AMD64_SEGMENTS */ + u_int16_t ds; + u_int16_t es; + u_int16_t fs; + u_int16_t gs; + + /* The next 2 registers are included with MD_CONTEXT_AMD64_CONTROL */ + u_int16_t ss; + u_int32_t eflags; + + /* The next 6 registers are included with MD_CONTEXT_AMD64_DEBUG_REGISTERS */ + u_int64_t dr0; + u_int64_t dr1; + u_int64_t dr2; + u_int64_t dr3; + u_int64_t dr6; + u_int64_t dr7; + + /* The next 4 registers are included with MD_CONTEXT_AMD64_INTEGER */ + u_int64_t rax; + u_int64_t rcx; + u_int64_t rdx; + u_int64_t rbx; + + /* The next register is included with MD_CONTEXT_AMD64_CONTROL */ + u_int64_t rsp; + + /* The next 11 registers are included with MD_CONTEXT_AMD64_INTEGER */ + u_int64_t rbp; + u_int64_t rsi; + u_int64_t rdi; + u_int64_t r8; + u_int64_t r9; + u_int64_t r10; + u_int64_t r11; + u_int64_t r12; + u_int64_t r13; + u_int64_t r14; + u_int64_t r15; + + /* The next register is included with MD_CONTEXT_AMD64_CONTROL */ + u_int64_t rip; + + /* The next set of registers are included with + * MD_CONTEXT_AMD64_FLOATING_POINT + */ + union { + MDXmmSaveArea32AMD64 flt_save; + struct { + u_int128_t header[2]; + u_int128_t legacy[8]; + u_int128_t xmm0; + u_int128_t xmm1; + u_int128_t xmm2; + u_int128_t xmm3; + u_int128_t xmm4; + u_int128_t xmm5; + u_int128_t xmm6; + u_int128_t xmm7; + u_int128_t xmm8; + u_int128_t xmm9; + u_int128_t xmm10; + u_int128_t xmm11; + u_int128_t xmm12; + u_int128_t xmm13; + u_int128_t xmm14; + u_int128_t xmm15; + } sse_registers; + }; + + u_int128_t vector_register[MD_CONTEXT_AMD64_VR_COUNT]; + u_int64_t vector_control; + + /* The next 5 registers are included with MD_CONTEXT_AMD64_DEBUG_REGISTERS */ + u_int64_t debug_control; + u_int64_t last_branch_to_rip; + u_int64_t last_branch_from_rip; + u_int64_t last_exception_to_rip; + u_int64_t last_exception_from_rip; + +} MDRawContextAMD64; /* CONTEXT */ + +/* For (MDRawContextAMD64).context_flags. These values indicate the type of + * context stored in the structure. The high 26 bits identify the CPU, the + * low 6 bits identify the type of context saved. */ +#define MD_CONTEXT_AMD64_CONTROL (MD_CONTEXT_AMD64 | 0x00000001) + /* CONTEXT_CONTROL */ +#define MD_CONTEXT_AMD64_INTEGER (MD_CONTEXT_AMD64 | 0x00000002) + /* CONTEXT_INTEGER */ +#define MD_CONTEXT_AMD64_SEGMENTS (MD_CONTEXT_AMD64 | 0x00000004) + /* CONTEXT_SEGMENTS */ +#define MD_CONTEXT_AMD64_FLOATING_POINT (MD_CONTEXT_AMD64 | 0x00000008) + /* CONTEXT_FLOATING_POINT */ +#define MD_CONTEXT_AMD64_DEBUG_REGISTERS (MD_CONTEXT_AMD64 | 0x00000010) + /* CONTEXT_DEBUG_REGISTERS */ +/* WinNT.h refers to CONTEXT_MMX_REGISTERS but doesn't appear to define it + * I think it really means CONTEXT_FLOATING_POINT. + */ + +#define MD_CONTEXT_AMD64_FULL (MD_CONTEXT_AMD64_CONTROL | \ + MD_CONTEXT_AMD64_INTEGER | \ + MD_CONTEXT_AMD64_FLOATING_POINT) + /* CONTEXT_FULL */ + +#define MD_CONTEXT_AMD64_ALL (MD_CONTEXT_AMD64_FULL | \ + MD_CONTEXT_AMD64_SEGMENTS | \ + MD_CONTEXT_X86_DEBUG_REGISTERS) + /* CONTEXT_ALL */ + + /* * SPARC support, see (solaris)sys/procfs_isa.h also */ diff --git a/src/google_breakpad/processor/minidump.h b/src/google_breakpad/processor/minidump.h index 474382b9..4e900fb1 100644 --- a/src/google_breakpad/processor/minidump.h +++ b/src/google_breakpad/processor/minidump.h @@ -175,8 +175,9 @@ class MinidumpContext : public MinidumpStream { // Returns raw CPU-specific context data for the named CPU type. If the // context data does not match the CPU type or does not exist, returns // NULL. - const MDRawContextX86* GetContextX86() const; - const MDRawContextPPC* GetContextPPC() const; + const MDRawContextX86* GetContextX86() const; + const MDRawContextPPC* GetContextPPC() const; + const MDRawContextAMD64* GetContextAMD64() const; const MDRawContextSPARC* GetContextSPARC() const; // Print a human-readable representation of the object to stdout. @@ -200,11 +201,15 @@ class MinidumpContext : public MinidumpStream { // not contain a system info stream. bool CheckAgainstSystemInfo(u_int32_t context_cpu_type); + // Store this separately because of the weirdo AMD64 context + u_int32_t context_flags_; + // The CPU-specific context structure. union { - MDRawContextBase* base; - MDRawContextX86* x86; - MDRawContextPPC* ppc; + MDRawContextBase* base; + MDRawContextX86* x86; + MDRawContextPPC* ppc; + MDRawContextAMD64* amd64; // on Solaris SPARC, sparc is defined as a numeric constant, // so variables can NOT be named as sparc MDRawContextSPARC* ctx_sparc; diff --git a/src/google_breakpad/processor/stack_frame_cpu.h b/src/google_breakpad/processor/stack_frame_cpu.h index 567c1b7e..a8840278 100644 --- a/src/google_breakpad/processor/stack_frame_cpu.h +++ b/src/google_breakpad/processor/stack_frame_cpu.h @@ -98,6 +98,32 @@ struct StackFramePPC : public StackFrame { int context_validity; }; +struct StackFrameAMD64 : public StackFrame { + // ContextValidity has one entry for each relevant hardware pointer register + // (%rip and %rsp) and one entry for each nonvolatile (callee-save) register. + //FIXME: validate this list + enum ContextValidity { + CONTEXT_VALID_NONE = 0, + CONTEXT_VALID_RIP = 1 << 0, + CONTEXT_VALID_RSP = 1 << 1, + CONTEXT_VALID_RBP = 1 << 2, + CONTEXT_VALID_ALL = -1 + }; + + StackFrameAMD64() : context(), context_validity(CONTEXT_VALID_NONE) {} + + // Register state. This is only fully valid for the topmost frame in a + // stack. In other frames, the values of nonvolatile registers may be + // present, given sufficient debugging information. Refer to + // context_validity. + MDRawContextAMD64 context; + + // context_validity is actually ContextValidity, but int is used because + // the OR operator doesn't work well with enumerated types. This indicates + // which fields in context are valid. + int context_validity; +}; + struct StackFrameSPARC : public StackFrame { // to be confirmed enum ContextValidity { diff --git a/src/processor/minidump.cc b/src/processor/minidump.cc index 3cce5ed7..d08ad12f 100644 --- a/src/processor/minidump.cc +++ b/src/processor/minidump.cc @@ -271,6 +271,7 @@ MinidumpStream::MinidumpStream(Minidump* minidump) MinidumpContext::MinidumpContext(Minidump* minidump) : MinidumpStream(minidump), + context_flags_(0), context_() { } @@ -286,233 +287,334 @@ bool MinidumpContext::Read(u_int32_t expected_size) { FreeContext(); // First, figure out what type of CPU this context structure is for. - u_int32_t context_flags; - if (!minidump_->ReadBytes(&context_flags, sizeof(context_flags))) { - BPLOG(ERROR) << "MinidumpContext could not read context flags"; - return false; - } - if (minidump_->swap()) - Swap(&context_flags); + // For some reason, the AMD64 Context doesn't have context_flags + // at the beginning of the structure, so special case it here. + if (expected_size == sizeof(MDRawContextAMD64)) { + BPLOG(INFO) << "MinidumpContext: looks like AMD64 context"; - u_int32_t cpu_type = context_flags & MD_CONTEXT_CPU_MASK; - - // Allocate the context structure for the correct CPU and fill it. The - // casts are slightly unorthodox, but it seems better to do that than to - // maintain a separate pointer for each type of CPU context structure - // when only one of them will be used. - switch (cpu_type) { - case MD_CONTEXT_X86: { - if (expected_size != sizeof(MDRawContextX86)) { - BPLOG(ERROR) << "MinidumpContext x86 size mismatch, " << - expected_size << " != " << sizeof(MDRawContextX86); - return false; - } - - scoped_ptr context_x86(new MDRawContextX86()); - - // Set the context_flags member, which has already been read, and - // read the rest of the structure beginning with the first member - // after context_flags. - context_x86->context_flags = context_flags; - - size_t flags_size = sizeof(context_x86->context_flags); - u_int8_t* context_after_flags = - reinterpret_cast(context_x86.get()) + flags_size; - if (!minidump_->ReadBytes(context_after_flags, - sizeof(MDRawContextX86) - flags_size)) { - BPLOG(ERROR) << "MinidumpContext could not read x86 context"; - return false; - } - - // Do this after reading the entire MDRawContext structure because - // GetSystemInfo may seek minidump to a new position. - if (!CheckAgainstSystemInfo(cpu_type)) { - BPLOG(ERROR) << "MinidumpContext x86 does not match system info"; - return false; - } - - if (minidump_->swap()) { - // context_x86->context_flags was already swapped. - Swap(&context_x86->dr0); - Swap(&context_x86->dr1); - Swap(&context_x86->dr2); - Swap(&context_x86->dr3); - Swap(&context_x86->dr6); - Swap(&context_x86->dr7); - Swap(&context_x86->float_save.control_word); - Swap(&context_x86->float_save.status_word); - Swap(&context_x86->float_save.tag_word); - Swap(&context_x86->float_save.error_offset); - Swap(&context_x86->float_save.error_selector); - Swap(&context_x86->float_save.data_offset); - Swap(&context_x86->float_save.data_selector); - // context_x86->float_save.register_area[] contains 8-bit quantities - // and does not need to be swapped. - Swap(&context_x86->float_save.cr0_npx_state); - Swap(&context_x86->gs); - Swap(&context_x86->fs); - Swap(&context_x86->es); - Swap(&context_x86->ds); - Swap(&context_x86->edi); - Swap(&context_x86->esi); - Swap(&context_x86->ebx); - Swap(&context_x86->edx); - Swap(&context_x86->ecx); - Swap(&context_x86->eax); - Swap(&context_x86->ebp); - Swap(&context_x86->eip); - Swap(&context_x86->cs); - Swap(&context_x86->eflags); - Swap(&context_x86->esp); - Swap(&context_x86->ss); - // context_x86->extended_registers[] contains 8-bit quantities and - // does not need to be swapped. - } - - context_.x86 = context_x86.release(); - - break; + scoped_ptr context_amd64(new MDRawContextAMD64()); + if (!minidump_->ReadBytes(context_amd64.get(), + sizeof(MDRawContextAMD64))) { + BPLOG(ERROR) << "MinidumpContext could not read amd64 context"; + return false; } - case MD_CONTEXT_PPC: { - if (expected_size != sizeof(MDRawContextPPC)) { - BPLOG(ERROR) << "MinidumpContext ppc size mismatch, " << - expected_size << " != " << sizeof(MDRawContextPPC); - return false; - } + if (minidump_->swap()) + Swap(&context_amd64->context_flags); - scoped_ptr context_ppc(new MDRawContextPPC()); + u_int32_t cpu_type = context_amd64->context_flags & MD_CONTEXT_CPU_MASK; - // Set the context_flags member, which has already been read, and - // read the rest of the structure beginning with the first member - // after context_flags. - context_ppc->context_flags = context_flags; + if (cpu_type != MD_CONTEXT_AMD64) { + //TODO: fall through to switch below? + // need a Tell method to be able to SeekSet back to beginning + // http://code.google.com/p/google-breakpad/issues/detail?id=224 + BPLOG(ERROR) << "MinidumpContext not actually amd64 context"; + return false; + } - size_t flags_size = sizeof(context_ppc->context_flags); - u_int8_t* context_after_flags = - reinterpret_cast(context_ppc.get()) + flags_size; - if (!minidump_->ReadBytes(context_after_flags, - sizeof(MDRawContextPPC) - flags_size)) { - BPLOG(ERROR) << "MinidumpContext could not read ppc context"; - return false; - } + // Do this after reading the entire MDRawContext structure because + // GetSystemInfo may seek minidump to a new position. + if (!CheckAgainstSystemInfo(cpu_type)) { + BPLOG(ERROR) << "MinidumpContext amd64 does not match system info"; + return false; + } - // Do this after reading the entire MDRawContext structure because - // GetSystemInfo may seek minidump to a new position. - if (!CheckAgainstSystemInfo(cpu_type)) { - BPLOG(ERROR) << "MinidumpContext ppc does not match system info"; - return false; - } + // Normalize the 128-bit types in the dump. + // Since this is AMD64, by definition, the values are little-endian. + for (unsigned int vr_index = 0; + vr_index < MD_CONTEXT_AMD64_VR_COUNT; + ++vr_index) + Normalize128(&context_amd64->vector_register[vr_index], false); + + if (minidump_->swap()) { + Swap(&context_amd64->p1_home); + Swap(&context_amd64->p2_home); + Swap(&context_amd64->p3_home); + Swap(&context_amd64->p4_home); + Swap(&context_amd64->p5_home); + Swap(&context_amd64->p6_home); + // context_flags is already swapped + Swap(&context_amd64->mx_csr); + Swap(&context_amd64->cs); + Swap(&context_amd64->ds); + Swap(&context_amd64->es); + Swap(&context_amd64->fs); + Swap(&context_amd64->ss); + Swap(&context_amd64->eflags); + Swap(&context_amd64->dr0); + Swap(&context_amd64->dr1); + Swap(&context_amd64->dr2); + Swap(&context_amd64->dr3); + Swap(&context_amd64->dr6); + Swap(&context_amd64->dr7); + Swap(&context_amd64->rax); + Swap(&context_amd64->rcx); + Swap(&context_amd64->rdx); + Swap(&context_amd64->rbx); + Swap(&context_amd64->rsp); + Swap(&context_amd64->rbp); + Swap(&context_amd64->rsi); + Swap(&context_amd64->rdi); + Swap(&context_amd64->r8); + Swap(&context_amd64->r9); + Swap(&context_amd64->r10); + Swap(&context_amd64->r11); + Swap(&context_amd64->r12); + Swap(&context_amd64->r13); + Swap(&context_amd64->r14); + Swap(&context_amd64->r15); + Swap(&context_amd64->rip); + //FIXME: I'm not sure what actually determines + // which member of the union {flt_save, sse_registers} + // is valid. We're not currently using either, + // but it would be good to have them swapped properly. - // Normalize the 128-bit types in the dump. - // Since this is PowerPC, by definition, the values are big-endian. for (unsigned int vr_index = 0; - vr_index < MD_VECTORSAVEAREA_PPC_VR_COUNT; - ++vr_index) { - Normalize128(&context_ppc->vector_save.save_vr[vr_index], true); + vr_index < MD_CONTEXT_AMD64_VR_COUNT; + ++vr_index) + Swap(&context_amd64->vector_register[vr_index]); + Swap(&context_amd64->vector_control); + Swap(&context_amd64->debug_control); + Swap(&context_amd64->last_branch_to_rip); + Swap(&context_amd64->last_branch_from_rip); + Swap(&context_amd64->last_exception_to_rip); + Swap(&context_amd64->last_exception_from_rip); + } + + context_flags_ = context_amd64->context_flags; + + context_.amd64 = context_amd64.release(); + } + else { + u_int32_t context_flags; + if (!minidump_->ReadBytes(&context_flags, sizeof(context_flags))) { + BPLOG(ERROR) << "MinidumpContext could not read context flags"; + return false; + } + if (minidump_->swap()) + Swap(&context_flags); + + u_int32_t cpu_type = context_flags & MD_CONTEXT_CPU_MASK; + + // Allocate the context structure for the correct CPU and fill it. The + // casts are slightly unorthodox, but it seems better to do that than to + // maintain a separate pointer for each type of CPU context structure + // when only one of them will be used. + switch (cpu_type) { + case MD_CONTEXT_X86: { + if (expected_size != sizeof(MDRawContextX86)) { + BPLOG(ERROR) << "MinidumpContext x86 size mismatch, " << + expected_size << " != " << sizeof(MDRawContextX86); + return false; + } + + scoped_ptr context_x86(new MDRawContextX86()); + + // Set the context_flags member, which has already been read, and + // read the rest of the structure beginning with the first member + // after context_flags. + context_x86->context_flags = context_flags; + + size_t flags_size = sizeof(context_x86->context_flags); + u_int8_t* context_after_flags = + reinterpret_cast(context_x86.get()) + flags_size; + if (!minidump_->ReadBytes(context_after_flags, + sizeof(MDRawContextX86) - flags_size)) { + BPLOG(ERROR) << "MinidumpContext could not read x86 context"; + return false; + } + + // Do this after reading the entire MDRawContext structure because + // GetSystemInfo may seek minidump to a new position. + if (!CheckAgainstSystemInfo(cpu_type)) { + BPLOG(ERROR) << "MinidumpContext x86 does not match system info"; + return false; + } + + if (minidump_->swap()) { + // context_x86->context_flags was already swapped. + Swap(&context_x86->dr0); + Swap(&context_x86->dr1); + Swap(&context_x86->dr2); + Swap(&context_x86->dr3); + Swap(&context_x86->dr6); + Swap(&context_x86->dr7); + Swap(&context_x86->float_save.control_word); + Swap(&context_x86->float_save.status_word); + Swap(&context_x86->float_save.tag_word); + Swap(&context_x86->float_save.error_offset); + Swap(&context_x86->float_save.error_selector); + Swap(&context_x86->float_save.data_offset); + Swap(&context_x86->float_save.data_selector); + // context_x86->float_save.register_area[] contains 8-bit quantities + // and does not need to be swapped. + Swap(&context_x86->float_save.cr0_npx_state); + Swap(&context_x86->gs); + Swap(&context_x86->fs); + Swap(&context_x86->es); + Swap(&context_x86->ds); + Swap(&context_x86->edi); + Swap(&context_x86->esi); + Swap(&context_x86->ebx); + Swap(&context_x86->edx); + Swap(&context_x86->ecx); + Swap(&context_x86->eax); + Swap(&context_x86->ebp); + Swap(&context_x86->eip); + Swap(&context_x86->cs); + Swap(&context_x86->eflags); + Swap(&context_x86->esp); + Swap(&context_x86->ss); + // context_x86->extended_registers[] contains 8-bit quantities and + // does not need to be swapped. + } + + context_.x86 = context_x86.release(); + + break; } - if (minidump_->swap()) { - // context_ppc->context_flags was already swapped. - Swap(&context_ppc->srr0); - Swap(&context_ppc->srr1); - for (unsigned int gpr_index = 0; - gpr_index < MD_CONTEXT_PPC_GPR_COUNT; - ++gpr_index) { - Swap(&context_ppc->gpr[gpr_index]); + case MD_CONTEXT_PPC: { + if (expected_size != sizeof(MDRawContextPPC)) { + BPLOG(ERROR) << "MinidumpContext ppc size mismatch, " << + expected_size << " != " << sizeof(MDRawContextPPC); + return false; } - Swap(&context_ppc->cr); - Swap(&context_ppc->xer); - Swap(&context_ppc->lr); - Swap(&context_ppc->ctr); - Swap(&context_ppc->mq); - Swap(&context_ppc->vrsave); - for (unsigned int fpr_index = 0; - fpr_index < MD_FLOATINGSAVEAREA_PPC_FPR_COUNT; - ++fpr_index) { - Swap(&context_ppc->float_save.fpregs[fpr_index]); + + scoped_ptr context_ppc(new MDRawContextPPC()); + + // Set the context_flags member, which has already been read, and + // read the rest of the structure beginning with the first member + // after context_flags. + context_ppc->context_flags = context_flags; + + size_t flags_size = sizeof(context_ppc->context_flags); + u_int8_t* context_after_flags = + reinterpret_cast(context_ppc.get()) + flags_size; + if (!minidump_->ReadBytes(context_after_flags, + sizeof(MDRawContextPPC) - flags_size)) { + BPLOG(ERROR) << "MinidumpContext could not read ppc context"; + return false; } - // Don't swap context_ppc->float_save.fpscr_pad because it is only - // used for padding. - Swap(&context_ppc->float_save.fpscr); + + // Do this after reading the entire MDRawContext structure because + // GetSystemInfo may seek minidump to a new position. + if (!CheckAgainstSystemInfo(cpu_type)) { + BPLOG(ERROR) << "MinidumpContext ppc does not match system info"; + return false; + } + + // Normalize the 128-bit types in the dump. + // Since this is PowerPC, by definition, the values are big-endian. for (unsigned int vr_index = 0; vr_index < MD_VECTORSAVEAREA_PPC_VR_COUNT; ++vr_index) { - Swap(&context_ppc->vector_save.save_vr[vr_index]); + Normalize128(&context_ppc->vector_save.save_vr[vr_index], true); } - Swap(&context_ppc->vector_save.save_vscr); - // Don't swap the padding fields in vector_save. - Swap(&context_ppc->vector_save.save_vrvalid); - } - context_.ppc = context_ppc.release(); - - break; - } - - case MD_CONTEXT_SPARC: { - if (expected_size != sizeof(MDRawContextSPARC)) { - BPLOG(ERROR) << "MinidumpContext sparc size mismatch, " << - expected_size << " != " << sizeof(MDRawContextSPARC); - return false; - } - - scoped_ptr context_sparc(new MDRawContextSPARC()); - - // Set the context_flags member, which has already been read, and - // read the rest of the structure beginning with the first member - // after context_flags. - context_sparc->context_flags = context_flags; - - size_t flags_size = sizeof(context_sparc->context_flags); - u_int8_t* context_after_flags = - reinterpret_cast(context_sparc.get()) + flags_size; - if (!minidump_->ReadBytes(context_after_flags, - sizeof(MDRawContextSPARC) - flags_size)) { - BPLOG(ERROR) << "MinidumpContext could not read sparc context"; - return false; - } - - // Do this after reading the entire MDRawContext structure because - // GetSystemInfo may seek minidump to a new position. - if (!CheckAgainstSystemInfo(cpu_type)) { - BPLOG(ERROR) << "MinidumpContext sparc does not match system info"; - return false; - } - - if (minidump_->swap()) { - // context_sparc->context_flags was already swapped. - for (unsigned int gpr_index = 0; - gpr_index < MD_CONTEXT_SPARC_GPR_COUNT; - ++gpr_index) { - Swap(&context_sparc->g_r[gpr_index]); + if (minidump_->swap()) { + // context_ppc->context_flags was already swapped. + Swap(&context_ppc->srr0); + Swap(&context_ppc->srr1); + for (unsigned int gpr_index = 0; + gpr_index < MD_CONTEXT_PPC_GPR_COUNT; + ++gpr_index) { + Swap(&context_ppc->gpr[gpr_index]); + } + Swap(&context_ppc->cr); + Swap(&context_ppc->xer); + Swap(&context_ppc->lr); + Swap(&context_ppc->ctr); + Swap(&context_ppc->mq); + Swap(&context_ppc->vrsave); + for (unsigned int fpr_index = 0; + fpr_index < MD_FLOATINGSAVEAREA_PPC_FPR_COUNT; + ++fpr_index) { + Swap(&context_ppc->float_save.fpregs[fpr_index]); + } + // Don't swap context_ppc->float_save.fpscr_pad because it is only + // used for padding. + Swap(&context_ppc->float_save.fpscr); + for (unsigned int vr_index = 0; + vr_index < MD_VECTORSAVEAREA_PPC_VR_COUNT; + ++vr_index) { + Swap(&context_ppc->vector_save.save_vr[vr_index]); + } + Swap(&context_ppc->vector_save.save_vscr); + // Don't swap the padding fields in vector_save. + Swap(&context_ppc->vector_save.save_vrvalid); } - Swap(&context_sparc->ccr); - Swap(&context_sparc->pc); - Swap(&context_sparc->npc); - Swap(&context_sparc->y); - Swap(&context_sparc->asi); - Swap(&context_sparc->fprs); - for (unsigned int fpr_index = 0; - fpr_index < MD_FLOATINGSAVEAREA_SPARC_FPR_COUNT; - ++fpr_index) { - Swap(&context_sparc->float_save.regs[fpr_index]); - } - Swap(&context_sparc->float_save.filler); - Swap(&context_sparc->float_save.fsr); + + context_.ppc = context_ppc.release(); + + break; } - context_.ctx_sparc = context_sparc.release(); - break; - } + case MD_CONTEXT_SPARC: { + if (expected_size != sizeof(MDRawContextSPARC)) { + BPLOG(ERROR) << "MinidumpContext sparc size mismatch, " << + expected_size << " != " << sizeof(MDRawContextSPARC); + return false; + } - default: { - // Unknown context type - BPLOG(ERROR) << "MinidumpContext unknown context type " << - HexString(cpu_type); - return false; - break; + scoped_ptr context_sparc(new MDRawContextSPARC()); + + // Set the context_flags member, which has already been read, and + // read the rest of the structure beginning with the first member + // after context_flags. + context_sparc->context_flags = context_flags; + + size_t flags_size = sizeof(context_sparc->context_flags); + u_int8_t* context_after_flags = + reinterpret_cast(context_sparc.get()) + flags_size; + if (!minidump_->ReadBytes(context_after_flags, + sizeof(MDRawContextSPARC) - flags_size)) { + BPLOG(ERROR) << "MinidumpContext could not read sparc context"; + return false; + } + + // Do this after reading the entire MDRawContext structure because + // GetSystemInfo may seek minidump to a new position. + if (!CheckAgainstSystemInfo(cpu_type)) { + BPLOG(ERROR) << "MinidumpContext sparc does not match system info"; + return false; + } + + if (minidump_->swap()) { + // context_sparc->context_flags was already swapped. + for (unsigned int gpr_index = 0; + gpr_index < MD_CONTEXT_SPARC_GPR_COUNT; + ++gpr_index) { + Swap(&context_sparc->g_r[gpr_index]); + } + Swap(&context_sparc->ccr); + Swap(&context_sparc->pc); + Swap(&context_sparc->npc); + Swap(&context_sparc->y); + Swap(&context_sparc->asi); + Swap(&context_sparc->fprs); + for (unsigned int fpr_index = 0; + fpr_index < MD_FLOATINGSAVEAREA_SPARC_FPR_COUNT; + ++fpr_index) { + Swap(&context_sparc->float_save.regs[fpr_index]); + } + Swap(&context_sparc->float_save.filler); + Swap(&context_sparc->float_save.fsr); + } + context_.ctx_sparc = context_sparc.release(); + + break; + } + + default: { + // Unknown context type + BPLOG(ERROR) << "MinidumpContext unknown context type " << + HexString(cpu_type); + return false; + break; + } } + context_flags_ = context_flags; } valid_ = true; @@ -527,7 +629,7 @@ u_int32_t MinidumpContext::GetContextCPU() const { return 0; } - return context_.base->context_flags & MD_CONTEXT_CPU_MASK; + return context_flags_ & MD_CONTEXT_CPU_MASK; } @@ -550,6 +652,15 @@ const MDRawContextPPC* MinidumpContext::GetContextPPC() const { return context_.ppc; } +const MDRawContextAMD64* MinidumpContext::GetContextAMD64() const { + if (GetContextCPU() != MD_CONTEXT_AMD64) { + BPLOG(ERROR) << "MinidumpContext cannot get amd64 context"; + return NULL; + } + + return context_.amd64; +} + const MDRawContextSPARC* MinidumpContext::GetContextSPARC() const { if (GetContextCPU() != MD_CONTEXT_SPARC) { BPLOG(ERROR) << "MinidumpContext cannot get sparc context"; @@ -569,6 +680,10 @@ void MinidumpContext::FreeContext() { delete context_.ppc; break; + case MD_CONTEXT_AMD64: + delete context_.amd64; + break; + case MD_CONTEXT_SPARC: delete context_.ctx_sparc; break; @@ -580,6 +695,7 @@ void MinidumpContext::FreeContext() { break; } + context_flags_ = 0; context_.base = NULL; } @@ -621,6 +737,11 @@ bool MinidumpContext::CheckAgainstSystemInfo(u_int32_t context_cpu_type) { return_value = true; break; + case MD_CONTEXT_AMD64: + if (system_info_cpu_type == MD_CPU_ARCHITECTURE_AMD64) + return_value = true; + break; + case MD_CONTEXT_SPARC: if (system_info_cpu_type == MD_CPU_ARCHITECTURE_SPARC) return_value = true; @@ -745,6 +866,60 @@ void MinidumpContext::Print() { break; } + case MD_CONTEXT_AMD64: { + const MDRawContextAMD64* context_amd64 = GetContextAMD64(); + printf("MDRawContextAMD64\n"); + printf(" p1_home = 0x%llx\n", + context_amd64->p1_home); + printf(" p2_home = 0x%llx\n", + context_amd64->p2_home); + printf(" p3_home = 0x%llx\n", + context_amd64->p3_home); + printf(" p4_home = 0x%llx\n", + context_amd64->p4_home); + printf(" p5_home = 0x%llx\n", + context_amd64->p5_home); + printf(" p6_home = 0x%llx\n", + context_amd64->p6_home); + printf(" context_flags = 0x%x\n", + context_amd64->context_flags); + printf(" mx_csr = 0x%x\n", + context_amd64->mx_csr); + printf(" cs = 0x%x\n", context_amd64->cs); + printf(" ds = 0x%x\n", context_amd64->ds); + printf(" es = 0x%x\n", context_amd64->es); + printf(" fs = 0x%x\n", context_amd64->fs); + printf(" gs = 0x%x\n", context_amd64->gs); + printf(" ss = 0x%x\n", context_amd64->ss); + printf(" eflags = 0x%x\n", context_amd64->eflags); + printf(" dr0 = 0x%llx\n", context_amd64->dr0); + printf(" dr1 = 0x%llx\n", context_amd64->dr1); + printf(" dr2 = 0x%llx\n", context_amd64->dr2); + printf(" dr3 = 0x%llx\n", context_amd64->dr3); + printf(" dr6 = 0x%llx\n", context_amd64->dr6); + printf(" dr7 = 0x%llx\n", context_amd64->dr7); + printf(" rax = 0x%llx\n", context_amd64->rax); + printf(" rcx = 0x%llx\n", context_amd64->rcx); + printf(" rdx = 0x%llx\n", context_amd64->rdx); + printf(" rbx = 0x%llx\n", context_amd64->rbx); + printf(" rsp = 0x%llx\n", context_amd64->rsp); + printf(" rbp = 0x%llx\n", context_amd64->rbp); + printf(" rsi = 0x%llx\n", context_amd64->rsi); + printf(" rdi = 0x%llx\n", context_amd64->rdi); + printf(" r8 = 0x%llx\n", context_amd64->r8); + printf(" r9 = 0x%llx\n", context_amd64->r9); + printf(" r10 = 0x%llx\n", context_amd64->r10); + printf(" r11 = 0x%llx\n", context_amd64->r11); + printf(" r12 = 0x%llx\n", context_amd64->r12); + printf(" r13 = 0x%llx\n", context_amd64->r13); + printf(" r14 = 0x%llx\n", context_amd64->r14); + printf(" r15 = 0x%llx\n", context_amd64->r15); + printf(" rip = 0x%llx\n", context_amd64->rip); + //TODO: print xmm, vector, debug registers + printf("\n"); + break; + } + case MD_CONTEXT_SPARC: { const MDRawContextSPARC* context_sparc = GetContextSPARC(); printf("MDRawContextSPARC\n"); diff --git a/src/processor/minidump_processor.cc b/src/processor/minidump_processor.cc index 54e6b545..3c609170 100644 --- a/src/processor/minidump_processor.cc +++ b/src/processor/minidump_processor.cc @@ -258,8 +258,14 @@ bool MinidumpProcessor::GetCPUInfo(Minidump *dump, SystemInfo *info) { return false; switch (raw_system_info->processor_architecture) { - case MD_CPU_ARCHITECTURE_X86: { - info->cpu = "x86"; + case MD_CPU_ARCHITECTURE_X86: + case MD_CPU_ARCHITECTURE_AMD64: { + if (raw_system_info->processor_architecture == + MD_CPU_ARCHITECTURE_X86) + info->cpu = "x86"; + else + info->cpu = "amd64"; + const string *cpu_vendor = system_info->GetCPUVendor(); if (cpu_vendor) { info->cpu_info = *cpu_vendor; diff --git a/src/processor/minidump_stackwalk.cc b/src/processor/minidump_stackwalk.cc index 547914db..785d6512 100644 --- a/src/processor/minidump_stackwalk.cc +++ b/src/processor/minidump_stackwalk.cc @@ -68,6 +68,7 @@ using google_breakpad::StackFrame; using google_breakpad::StackFramePPC; using google_breakpad::StackFrameSPARC; using google_breakpad::StackFrameX86; +using google_breakpad::StackFrameAMD64; // Separator character for machine readable output. static const char kOutputSeparator = '|'; @@ -165,6 +166,16 @@ static void PrintStack(const CallStack *stack, const string &cpu) { sequence = PrintRegister("srr0", frame_ppc->context.srr0, sequence); if (frame_ppc->context_validity & StackFramePPC::CONTEXT_VALID_GPR1) sequence = PrintRegister("r1", frame_ppc->context.gpr[1], sequence); + } else if (cpu == "amd64") { + const StackFrameAMD64 *frame_amd64 = + reinterpret_cast(frame); + + if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RIP) + sequence = PrintRegister("rip", frame_amd64->context.rip, sequence); + if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RSP) + sequence = PrintRegister("rsp", frame_amd64->context.rsp, sequence); + if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RBP) + sequence = PrintRegister("rbp", frame_amd64->context.rbp, sequence); } else if (cpu == "sparc") { const StackFrameSPARC *frame_sparc = reinterpret_cast(frame); @@ -176,7 +187,6 @@ static void PrintStack(const CallStack *stack, const string &cpu) { if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_PC) sequence = PrintRegister("pc", frame_sparc->context.pc, sequence); } - printf("\n"); } } diff --git a/src/processor/stackwalker.cc b/src/processor/stackwalker.cc index 502f07d4..cdb0ce52 100644 --- a/src/processor/stackwalker.cc +++ b/src/processor/stackwalker.cc @@ -51,6 +51,7 @@ #include "processor/stackwalker_ppc.h" #include "processor/stackwalker_sparc.h" #include "processor/stackwalker_x86.h" +#include "processor/stackwalker_amd64.h" namespace google_breakpad { @@ -164,6 +165,13 @@ Stackwalker* Stackwalker::StackwalkerForCPU( memory, modules, supplier, resolver); break; + + case MD_CONTEXT_AMD64: + cpu_stackwalker = new StackwalkerAMD64(system_info, + context->GetContextAMD64(), + memory, modules, supplier, + resolver); + break; case MD_CONTEXT_SPARC: cpu_stackwalker = new StackwalkerSPARC(system_info, diff --git a/src/processor/stackwalker_amd64.cc b/src/processor/stackwalker_amd64.cc new file mode 100644 index 00000000..7a413963 --- /dev/null +++ b/src/processor/stackwalker_amd64.cc @@ -0,0 +1,131 @@ +// Copyright (c) 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// stackwalker_amd64.cc: amd64-specific stackwalker. +// +// See stackwalker_amd64.h for documentation. +// +// Author: Mark Mentovai, Ted Mielczarek + + +#include "processor/stackwalker_amd64.h" +#include "google_breakpad/processor/call_stack.h" +#include "google_breakpad/processor/memory_region.h" +#include "google_breakpad/processor/stack_frame_cpu.h" +#include "processor/logging.h" + +namespace google_breakpad { + + +StackwalkerAMD64::StackwalkerAMD64(const SystemInfo *system_info, + const MDRawContextAMD64 *context, + MemoryRegion *memory, + const CodeModules *modules, + SymbolSupplier *supplier, + SourceLineResolverInterface *resolver) + : Stackwalker(system_info, memory, modules, supplier, resolver), + context_(context) { +} + + +StackFrame* StackwalkerAMD64::GetContextFrame() { + if (!context_ || !memory_) { + BPLOG(ERROR) << "Can't get context frame without context or memory"; + return NULL; + } + + StackFrameAMD64 *frame = new StackFrameAMD64(); + + // The instruction pointer is stored directly in a register, so pull it + // straight out of the CPU context structure. + frame->context = *context_; + frame->context_validity = StackFrameAMD64::CONTEXT_VALID_ALL; + frame->instruction = frame->context.rip; + + return frame; +} + + +StackFrame* StackwalkerAMD64::GetCallerFrame( + const CallStack *stack, + const vector< linked_ptr > &stack_frame_info) { + if (!memory_ || !stack) { + BPLOG(ERROR) << "Can't get caller frame without memory or stack"; + return NULL; + } + + StackFrameAMD64 *last_frame = static_cast( + stack->frames()->back()); + + //FIXME: this pretty much doesn't work at all due to FPO + // being enabled by default. + // Brain-dead stackwalking: + // %rip_new = *(%rbp_old + 8) + // %rsp_new = %rbp_old + 16 + // %rbp_new = *(%rbp_old) + + // 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. + u_int64_t stack_pointer = last_frame->context.rbp + 16; + if (stack_pointer <= last_frame->context.rsp) { + return NULL; + } + + u_int64_t instruction; + if (!memory_->GetMemoryAtAddress(last_frame->context.rbp + 8, + &instruction) || + instruction <= 1) { + return NULL; + } + + u_int64_t stack_base; + if (!memory_->GetMemoryAtAddress(last_frame->context.rbp, + &stack_base) || + stack_base <= 1) { + return NULL; + } + + StackFrameAMD64 *frame = new StackFrameAMD64(); + + frame->context = last_frame->context; + frame->context.rip = instruction; + frame->context.rsp = stack_pointer; + frame->context.rbp = stack_base; + frame->context_validity = StackFrameAMD64::CONTEXT_VALID_RIP | + StackFrameAMD64::CONTEXT_VALID_RSP | + StackFrameAMD64::CONTEXT_VALID_RBP; + + frame->instruction = frame->context.rip - 1; + + return frame; +} + + +} // namespace google_breakpad diff --git a/src/processor/stackwalker_amd64.h b/src/processor/stackwalker_amd64.h new file mode 100644 index 00000000..a3e11358 --- /dev/null +++ b/src/processor/stackwalker_amd64.h @@ -0,0 +1,80 @@ +// Copyright (c) 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// stackwalker_amd64.h: amd64-specific stackwalker. +// +// Provides stack frames given amd64 register context and a memory region +// corresponding to a amd64 stack. +// +// Author: Mark Mentovai, Ted Mielczarek + + +#ifndef PROCESSOR_STACKWALKER_AMD64_H__ +#define PROCESSOR_STACKWALKER_AMD64_H__ + + +#include "google_breakpad/common/breakpad_types.h" +#include "google_breakpad/common/minidump_format.h" +#include "google_breakpad/processor/stackwalker.h" + +namespace google_breakpad { + +class CodeModules; + +class StackwalkerAMD64 : public Stackwalker { + public: + // context is a amd64 context object that gives access to amd64-specific + // register state corresponding to the innermost called frame to be + // included in the stack. The other arguments are passed directly through + // to the base Stackwalker constructor. + StackwalkerAMD64(const SystemInfo *system_info, + const MDRawContextAMD64 *context, + MemoryRegion *memory, + const CodeModules *modules, + SymbolSupplier *supplier, + SourceLineResolverInterface *resolver); + + private: + // Implementation of Stackwalker, using amd64 context (stack pointer in %rsp, + // stack base in %rbp) and stack conventions (saved stack pointer at 0(%rbp)) + virtual StackFrame* GetContextFrame(); + virtual StackFrame* GetCallerFrame( + const CallStack *stack, + const vector< linked_ptr > &stack_frame_info); + + // Stores the CPU context corresponding to the innermost stack frame to + // be returned by GetContextFrame. + const MDRawContextAMD64 *context_; +}; + + +} // namespace google_breakpad + + +#endif // PROCESSOR_STACKWALKER_AMD64_H__