crashpad/util/misc/capture_context_win.asm
Mark Mentovai 6278690abe Update copyright boilerplate, 2022 edition (Crashpad)
sed -i '' -E -e 's/Copyright (.+) The Crashpad Authors\. All rights reserved\.$/Copyright \1 The Crashpad Authors/' $(git grep -El 'Copyright (.+) The Crashpad Authors\. All rights reserved\.$')

Bug: chromium:1098010
Change-Id: I8d6138469ddbe3d281a5d83f64cf918ec2491611
Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/3878262
Reviewed-by: Joshua Peraza <jperaza@chromium.org>
Commit-Queue: Mark Mentovai <mark@chromium.org>
2022-09-06 23:54:07 +00:00

526 lines
14 KiB
NASM
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

; Copyright 2015 The Crashpad Authors
;
; 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 <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 havent 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 functions prologue.
mov edx, dword ptr [ebp]
mov [ebx.CONTEXT].c_Ebp, edx
; eip cant 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 functions prologue, but the callers esp is 8
; more than this value: 4 for the original ebp saved on the stack in this
; functions 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 cant be read from user code, so zero them out in the
; CONTEXT structure. context->ContextFlags doesnt 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
; functions 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 havent 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, theres no way to recover the value of
; the callers 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 functions prologue, but the callers rsp is
; 16 more than this value: 8 for the original rbp saved on the stack in this
; functions 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 functions prologue.
mov rax, qword ptr [rbp]
mov [rcx.CONTEXT].c_Rbp, rax
; rip cant 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 arent 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 cant be read from user code, so zero them out in the
; CONTEXT structure. context->ContextFlags doesnt 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
; functions 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