; 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 ; The CONTEXT structure definitions that follow are based on those in . ; 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://crashpad.chromium.org/bug/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) - 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://crashpad.chromium.org/bug/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