; 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 __MINGW32__
CAPTURECONTEXT_SYMBOL equ _ZN8crashpad14CaptureContextEP8_CONTEXT
elseifdef _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
