Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(742)

Unified Diff: util/win/capture_context.asm

Issue 1377963002: win: Add and use a custom CaptureContext() implementation (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad@master
Patch Set: Address review feeback (2) Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « util/win/capture_context.h ('k') | util/win/capture_context_test.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: util/win/capture_context.asm
diff --git a/util/win/capture_context.asm b/util/win/capture_context.asm
new file mode 100644
index 0000000000000000000000000000000000000000..933f8828957cef97ef7c1330978f0dcc08897bff
--- /dev/null
+++ b/util/win/capture_context.asm
@@ -0,0 +1,532 @@
+; Copyright 2015 The Crashpad Authors. All rights reserved.
+;
+; 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.
+
+; Detect ml64 assembling for x86_64 by checking for rax.
+ifdef rax
+_M_X64 equ 1
+else
+_M_IX86 equ 1
+endif
+
+ifdef _M_IX86
+.586
+.XMM
+.model flat
+endif
+
+offsetof macro structure, field
+ exitm <structure.&field>
+endm
+
+; The CONTEXT structure definitions that follow are based on those in <winnt.h>.
+; Field names are prefixed (as in c_Rax) to avoid colliding with the predefined
+; register names (such as Rax).
+
+ifdef _M_IX86
+
+CONTEXT_i386 equ 10000h
+CONTEXT_CONTROL equ CONTEXT_i386 or 1h
+CONTEXT_INTEGER equ CONTEXT_i386 or 2h
+CONTEXT_SEGMENTS equ CONTEXT_i386 or 4h
+CONTEXT_FLOATING_POINT equ CONTEXT_i386 or 8h
+CONTEXT_DEBUG_REGISTERS equ CONTEXT_i386 or 10h
+CONTEXT_EXTENDED_REGISTERS equ CONTEXT_i386 or 20h
+CONTEXT_XSTATE equ CONTEXT_i386 or 40h
+
+MAXIMUM_SUPPORTED_EXTENSION equ 512
+
+CONTEXT struct
+ c_ContextFlags dword ?
+
+ c_Dr0 dword ?
+ c_Dr1 dword ?
+ c_Dr2 dword ?
+ c_Dr3 dword ?
+ c_Dr6 dword ?
+ c_Dr7 dword ?
+
+ struct c_FloatSave
+ f_ControlWord dword ?
+ f_StatusWord dword ?
+ f_TagWord dword ?
+ f_ErrorOffset dword ?
+ f_ErrorSelector dword ?
+ f_DataOffset dword ?
+ f_DataSelector dword ?
+ f_RegisterArea byte 80 dup(?)
+
+ union
+ f_Spare0 dword ? ; As in FLOATING_SAVE_AREA.
+ f_Cr0NpxState dword ? ; As in WOW64_FLOATING_SAVE_AREA.
+ ends
+ ends
+
+ c_SegGs dword ?
+ c_SegFs dword ?
+ c_SegEs dword ?
+ c_SegDs dword ?
+
+ c_Edi dword ?
+ c_Esi dword ?
+ c_Ebx dword ?
+ c_Edx dword ?
+ c_Ecx dword ?
+ c_Eax dword ?
+
+ c_Ebp dword ?
+
+ c_Eip dword ?
+ c_SegCs dword ?
+
+ c_EFlags dword ?
+
+ c_Esp dword ?
+ c_SegSs dword ?
+
+ c_ExtendedRegisters byte MAXIMUM_SUPPORTED_EXTENSION dup(?)
+CONTEXT ends
+
+elseifdef _M_X64
+
+M128A struct 16
+ m_Low qword ?
+ m_High qword ?
+M128A ends
+
+CONTEXT_AMD64 equ 100000h
+CONTEXT_CONTROL equ CONTEXT_AMD64 or 1h
+CONTEXT_INTEGER equ CONTEXT_AMD64 or 2h
+CONTEXT_SEGMENTS equ CONTEXT_AMD64 or 4h
+CONTEXT_FLOATING_POINT equ CONTEXT_AMD64 or 8h
+CONTEXT_DEBUG_REGISTERS equ CONTEXT_AMD64 or 10h
+CONTEXT_XSTATE equ CONTEXT_AMD64 or 40h
+
+CONTEXT struct 16
+ c_P1Home qword ?
+ c_P2Home qword ?
+ c_P3Home qword ?
+ c_P4Home qword ?
+ c_P5Home qword ?
+ c_P6Home qword ?
+
+ c_ContextFlags dword ?
+ c_MxCsr dword ?
+
+ c_SegCs word ?
+ c_SegDs word ?
+ c_SegEs word ?
+ c_SegFs word ?
+ c_SegGs word ?
+ c_SegSs word ?
+
+ c_EFlags dword ?
+
+ c_Dr0 qword ?
+ c_Dr1 qword ?
+ c_Dr2 qword ?
+ c_Dr3 qword ?
+ c_Dr6 qword ?
+ c_Dr7 qword ?
+
+ c_Rax qword ?
+ c_Rcx qword ?
+ c_Rdx qword ?
+ c_Rbx qword ?
+ c_Rsp qword ?
+ c_Rbp qword ?
+ c_Rsi qword ?
+ c_Rdi qword ?
+ c_R8 qword ?
+ c_R9 qword ?
+ c_R10 qword ?
+ c_R11 qword ?
+ c_R12 qword ?
+ c_R13 qword ?
+ c_R14 qword ?
+ c_R15 qword ?
+
+ c_Rip qword ?
+
+ union
+ struct c_FltSave
+ f_ControlWord word ?
+ f_StatusWord word ?
+ f_TagWord byte ?
+ f_Reserved1 byte ?
+ f_ErrorOpcode word ?
+ f_ErrorOffset dword ?
+ f_ErrorSelector word ?
+ f_Reserved2 word ?
+ f_DataOffset dword ?
+ f_DataSelector word ?
+ f_Reserved3 word ?
+ f_MxCsr dword ?
+ f_MxCsr_Mask dword ?
+ f_FloatRegisters M128A 8 dup(<?>)
+ f_XmmRegisters M128A 16 dup(<?>)
+ f_Reserved4 byte 96 dup(?)
+ ends
+ struct
+ fx_Header M128A 2 dup(<?>)
+ fx_Legacy M128A 8 dup(<?>)
+ fx_Xmm0 M128A <?>
+ fx_Xmm1 M128A <?>
+ fx_Xmm2 M128A <?>
+ fx_Xmm3 M128A <?>
+ fx_Xmm4 M128A <?>
+ fx_Xmm5 M128A <?>
+ fx_Xmm6 M128A <?>
+ fx_Xmm7 M128A <?>
+ fx_Xmm8 M128A <?>
+ fx_Xmm9 M128A <?>
+ fx_Xmm10 M128A <?>
+ fx_Xmm11 M128A <?>
+ fx_Xmm12 M128A <?>
+ fx_Xmm13 M128A <?>
+ fx_Xmm14 M128A <?>
+ fx_Xmm15 M128A <?>
+ ends
+ ends
+
+ c_VectorRegister M128A 26 dup(<?>)
+ c_VectorControl qword ?
+
+ c_DebugControl qword ?
+ c_LastBranchToRip qword ?
+ c_LastBranchFromRip qword ?
+ c_LastExceptionToRip qword ?
+ c_LastExceptionFromRip qword ?
+CONTEXT ends
+
+endif
+
+; namespace crashpad {
+; void CaptureContext(CONTEXT* context)
+; } // namespace crashpad
+ifdef _M_IX86
+CAPTURECONTEXT_SYMBOL equ ?CaptureContext@crashpad@@YAXPAU_CONTEXT@@@Z
+elseifdef _M_X64
+CAPTURECONTEXT_SYMBOL equ ?CaptureContext@crashpad@@YAXPEAU_CONTEXT@@@Z
+endif
+
+_TEXT segment
+public CAPTURECONTEXT_SYMBOL
+
+ifdef _M_IX86
+
+CAPTURECONTEXT_SYMBOL proc
+
+ push ebp
+ mov ebp, esp
+
+ ; pushfd first, because some instructions affect eflags. eflags will be in
+ ; [ebp-4].
+ pushfd
+
+ ; Save the original value of ebx, and use ebx to hold the CONTEXT* argument.
+ ; The original value of ebx will be in [ebp-8].
+ push ebx
+ mov ebx, [ebp+8]
+
+ ; General-purpose registers whose values haven’t changed can be captured
+ ; directly.
+ mov [ebx.CONTEXT].c_Edi, edi
+ mov [ebx.CONTEXT].c_Esi, esi
+ mov [ebx.CONTEXT].c_Edx, edx
+ mov [ebx.CONTEXT].c_Ecx, ecx
+ mov [ebx.CONTEXT].c_Eax, eax
+
+ ; Now that the original value of edx has been saved, it can be repurposed to
+ ; hold other registers’ values.
+
+ ; The original ebx was saved on the stack above.
+ mov edx, dword ptr [ebp-8]
+ mov [ebx.CONTEXT].c_Ebx, edx
+
+ ; The original ebp was saved on the stack in this function’s prologue.
+ mov edx, dword ptr [ebp]
+ mov [ebx.CONTEXT].c_Ebp, edx
+
+ ; eip can’t be accessed directly, but the return address saved on the stack
+ ; by the call instruction that reached this function can be used.
+ mov edx, dword ptr [ebp+4]
+ mov [ebx.CONTEXT].c_Eip, edx
+
+ ; The original eflags was saved on the stack above.
+ mov edx, dword ptr [ebp-4]
+ mov [ebx.CONTEXT].c_EFlags, edx
+
+ ; esp was saved in ebp in this function’s prologue, but the caller’s esp is 8
+ ; more than this value: 4 for the original ebp saved on the stack in this
+ ; function’s prologue, and 4 for the return address saved on the stack by the
+ ; call instruction that reached this function.
+ lea edx, [ebp+8]
+ mov [ebx.CONTEXT].c_Esp, edx
+
+ ; The segment registers are 16 bits wide, but CONTEXT declares them as
+ ; unsigned 32-bit values, so zero the top half.
+ xor edx, edx
+ mov dx, gs
+ mov [ebx.CONTEXT].c_SegGs, edx
+ mov dx, fs
+ mov [ebx.CONTEXT].c_SegFs, edx
+ mov dx, es
+ mov [ebx.CONTEXT].c_SegEs, edx
+ mov dx, ds
+ mov [ebx.CONTEXT].c_SegDs, edx
+ mov dx, cs
+ mov [ebx.CONTEXT].c_SegCs, edx
+ mov dx, ss
+ mov [ebx.CONTEXT].c_SegSs, edx
+
+ ; Prepare for the string move that will populate the ExtendedRegisters area,
+ ; or the string store that will zero it.
+ cld
+
+ ; Use cpuid 1 to check whether fxsave is supported. If it is, perform it
+ ; before fnsave because fxsave is a less-destructive operation.
+ mov esi, ebx
+ mov eax, 1
+ cpuid
+ mov ebx, esi
+
+ test edx, 01000000 ; FXSR
+ jnz $FXSave
+
+ ; fxsave is not supported. Set ContextFlags to not include
+ ; CONTEXT_EXTENDED_REGISTERS, and zero the ExtendedRegisters area.
+ mov [ebx.CONTEXT].c_ContextFlags, CONTEXT_i386 or \
+ CONTEXT_CONTROL or \
+ CONTEXT_INTEGER or \
+ CONTEXT_SEGMENTS or \
+ CONTEXT_FLOATING_POINT
+ lea edi, [ebx.CONTEXT].c_ExtendedRegisters
+ xor eax, eax
+ mov ecx, MAXIMUM_SUPPORTED_EXTENSION / sizeof(dword) ; 128
+ rep stosd
+ jmp $FXSaveDone
+
+$FXSave:
+ ; fxsave is supported. Set ContextFlags to include CONTEXT_EXTENDED_REGISTERS.
+ mov [ebx.CONTEXT].c_ContextFlags, CONTEXT_i386 or \
+ CONTEXT_CONTROL or \
+ CONTEXT_INTEGER or \
+ CONTEXT_SEGMENTS or \
+ CONTEXT_FLOATING_POINT or \
+ CONTEXT_EXTENDED_REGISTERS
+
+ ; fxsave requires a 16 byte-aligned destination memory area. Nothing
+ ; guarantees the alignment of a CONTEXT structure, so create a temporary
+ ; aligned fxsave destination on the stack.
+ and esp, 0fffffff0h
+ sub esp, MAXIMUM_SUPPORTED_EXTENSION
+
+ ; Zero out the temporary fxsave area before performing the fxsave. Some of the
+ ; fxsave area may not be written by fxsave, and some is definitely not written
+ ; by fxsave.
+ mov edi, esp
+ xor eax, eax
+ mov ecx, MAXIMUM_SUPPORTED_EXTENSION / sizeof(dword) ; 128
+ rep stosd
+
+ fxsave [esp]
+
+ ; Copy the temporary fxsave area into the CONTEXT structure.
+ lea edi, [ebx.CONTEXT].c_ExtendedRegisters
+ mov esi, esp
+ mov ecx, MAXIMUM_SUPPORTED_EXTENSION / sizeof(dword) ; 128
+ rep movsd
+
+ ; Free the stack space used for the temporary fxsave area.
+ lea esp, [ebp-8]
+
+ ; TODO(mark): AVX/xsave support.
+ ; https://code.google.com/p/crashpad/issues/detail?id=58
+
+$FXSaveDone:
+ ; fnsave reinitializes the FPU with an implicit finit operation, so use frstor
+ ; to restore the original state.
+ fnsave [ebx.CONTEXT].c_FloatSave
+ frstor [ebx.CONTEXT].c_FloatSave
+
+ ; cr0 is inaccessible from user code, and this field would not be used anyway.
+ mov [ebx.CONTEXT].c_FloatSave.f_Cr0NpxState, 0
+
+ ; The debug registers can’t be read from user code, so zero them out in the
+ ; CONTEXT structure. context->ContextFlags doesn’t indicate that they are
+ ; present.
+ mov [ebx.CONTEXT].c_Dr0, 0
+ mov [ebx.CONTEXT].c_Dr1, 0
+ mov [ebx.CONTEXT].c_Dr2, 0
+ mov [ebx.CONTEXT].c_Dr3, 0
+ mov [ebx.CONTEXT].c_Dr6, 0
+ mov [ebx.CONTEXT].c_Dr7, 0
+
+ ; Clean up by restoring clobbered registers, even those considered volatile
+ ; by the ABI, so that the captured context represents the state at this
+ ; function’s exit.
+ mov edi, [ebx.CONTEXT].c_Edi
+ mov esi, [ebx.CONTEXT].c_Esi
+ mov edx, [ebx.CONTEXT].c_Edx
+ mov ecx, [ebx.CONTEXT].c_Ecx
+ mov eax, [ebx.CONTEXT].c_Eax
+ pop ebx
+ popfd
+
+ pop ebp
+
+ ret
+
+CAPTURECONTEXT_SYMBOL endp
+
+elseifdef _M_X64
+
+CAPTURECONTEXT_SYMBOL proc frame
+
+ push rbp
+ .pushreg rbp
+ mov rbp, rsp
+ .setframe rbp, 0
+
+ ; Note that 16-byte stack alignment is not maintained because this function
+ ; does not call out to any other.
+
+ ; pushfq first, because some instructions affect rflags. rflags will be in
+ ; [rbp-8].
+ pushfq
+ .allocstack 8
+ .endprolog
+
+ mov [rcx.CONTEXT].c_ContextFlags, CONTEXT_AMD64 or \
+ CONTEXT_CONTROL or \
+ CONTEXT_INTEGER or \
+ CONTEXT_SEGMENTS or \
+ CONTEXT_FLOATING_POINT
+
+ ; General-purpose registers whose values haven’t changed can be captured
+ ; directly.
+ mov [rcx.CONTEXT].c_Rax, rax
+ mov [rcx.CONTEXT].c_Rdx, rdx
+ mov [rcx.CONTEXT].c_Rbx, rbx
+ mov [rcx.CONTEXT].c_Rsi, rsi
+ mov [rcx.CONTEXT].c_Rdi, rdi
+ mov [rcx.CONTEXT].c_R8, r8
+ mov [rcx.CONTEXT].c_R9, r9
+ mov [rcx.CONTEXT].c_R10, r10
+ mov [rcx.CONTEXT].c_R11, r11
+ mov [rcx.CONTEXT].c_R12, r12
+ mov [rcx.CONTEXT].c_R13, r13
+ mov [rcx.CONTEXT].c_R14, r14
+ mov [rcx.CONTEXT].c_R15, r15
+
+ ; Because of the calling convention, there’s no way to recover the value of
+ ; the caller’s rcx as it existed prior to calling this function. This
+ ; function captures a snapshot of the register state at its return, which
+ ; involves rcx containing a pointer to its first argument.
+ mov [rcx.CONTEXT].c_Rcx, rcx
+
+ ; Now that the original value of rax has been saved, it can be repurposed to
+ ; hold other registers’ values.
+
+ ; Save mxcsr. This is duplicated in context->FltSave.MxCsr, saved by fxsave
+ ; below.
+ stmxcsr [rcx.CONTEXT].c_MxCsr
+
+ ; Segment registers.
+ mov [rcx.CONTEXT].c_SegCs, cs
+ mov [rcx.CONTEXT].c_SegDs, ds
+ mov [rcx.CONTEXT].c_SegEs, es
+ mov [rcx.CONTEXT].c_SegFs, fs
+ mov [rcx.CONTEXT].c_SegGs, gs
+ mov [rcx.CONTEXT].c_SegSs, ss
+
+ ; The original rflags was saved on the stack above. Note that the CONTEXT
+ ; structure only stores eflags, the low 32 bits. The high 32 bits in rflags
+ ; are reserved.
+ mov rax, qword ptr [rbp-8]
+ mov [rcx.CONTEXT].c_EFlags, eax
+
+ ; rsp was saved in rbp in this function’s prologue, but the caller’s rsp is
+ ; 16 more than this value: 8 for the original rbp saved on the stack in this
+ ; function’s prologue, and 8 for the return address saved on the stack by the
+ ; call instruction that reached this function.
+ lea rax, [rbp+16]
+ mov [rcx.CONTEXT].c_Rsp, rax
+
+ ; The original rbp was saved on the stack in this function’s prologue.
+ mov rax, qword ptr [rbp]
+ mov [rcx.CONTEXT].c_Rbp, rax
+
+ ; rip can’t be accessed directly, but the return address saved on the stack by
+ ; the call instruction that reached this function can be used.
+ mov rax, qword ptr [rbp+8]
+ mov [rcx.CONTEXT].c_Rip, rax
+
+ ; Zero out the fxsave area before performing the fxsave. Some of the fxsave
+ ; area may not be written by fxsave, and some is definitely not written by
+ ; fxsave. This also zeroes out the rest of the CONTEXT structure to its end,
+ ; including the unused VectorRegister and VectorControl fields, and the debug
+ ; control register fields.
+ mov rbx, rcx
+ cld
+ lea rdi, [rcx.CONTEXT].c_FltSave
+ xor rax, rax
+ mov rcx, (sizeof(CONTEXT) - offsetof(CONTEXT, c_FltSave)) / \
+ sizeof(qword) ; 122
+ rep stosq
+ mov rcx, rbx
+
+ ; Save the floating point (including SSE) state. The CONTEXT structure is
+ ; declared as 16-byte-aligned, which is correct for this operation.
+ fxsave [rcx.CONTEXT].c_FltSave
+
+ ; TODO(mark): AVX/xsave support.
+ ; https://code.google.com/p/crashpad/issues/detail?id=58
+
+ ; The register parameter home address fields aren’t used, so zero them out.
+ mov [rcx.CONTEXT].c_P1Home, 0
+ mov [rcx.CONTEXT].c_P2Home, 0
+ mov [rcx.CONTEXT].c_P3Home, 0
+ mov [rcx.CONTEXT].c_P4Home, 0
+ mov [rcx.CONTEXT].c_P5Home, 0
+ mov [rcx.CONTEXT].c_P6Home, 0
+
+ ; The debug registers can’t be read from user code, so zero them out in the
+ ; CONTEXT structure. context->ContextFlags doesn’t indicate that they are
+ ; present.
+ mov [rcx.CONTEXT].c_Dr0, 0
+ mov [rcx.CONTEXT].c_Dr1, 0
+ mov [rcx.CONTEXT].c_Dr2, 0
+ mov [rcx.CONTEXT].c_Dr3, 0
+ mov [rcx.CONTEXT].c_Dr6, 0
+ mov [rcx.CONTEXT].c_Dr7, 0
+
+ ; Clean up by restoring clobbered registers, even those considered volatile by
+ ; the ABI, so that the captured context represents the state at this
+ ; function’s exit.
+ mov rax, [rcx.CONTEXT].c_Rax
+ mov rbx, [rcx.CONTEXT].c_Rbx
+ mov rdi, [rcx.CONTEXT].c_Rdi
+ popfq
+
+ pop rbp
+
+ ret
+
+CAPTURECONTEXT_SYMBOL endp
+
+endif
+
+_TEXT ends
+end
« no previous file with comments | « util/win/capture_context.h ('k') | util/win/capture_context_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698