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..a65b36d8389ede72ca8a65ab9ea2cecd8316ca37 |
--- /dev/null |
+++ b/util/win/capture_context.asm |
@@ -0,0 +1,339 @@ |
+; 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 |
+ |
+; 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 dword ptr [ebx+156], edi ; context->Edi |
scottmg
2015/09/29 21:58:40
Do you think it would be more readable to define
Mark Mentovai
2015/09/30 15:33:22
scottmg wrote:
|
+ mov dword ptr [ebx+160], esi ; context->Esi |
+ mov dword ptr [ebx+168], edx ; context->Edx |
+ mov dword ptr [ebx+172], ecx ; context->Ecx |
+ mov dword ptr [ebx+176], eax ; context->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 dword ptr [ebx+164], edx ; context->Ebx |
+ |
+ ; The original ebp was saved on the stack in this function’s prologue. |
+ mov edx, dword ptr [ebp] |
+ mov dword ptr [ebx+180], edx ; context->Ebp |
+ |
+ ; 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 dword ptr [ebx+184], edx ; context->Eip |
+ |
+ ; The original eflags was saved on the stack above. |
+ mov edx, dword ptr [ebp-4] |
+ mov dword ptr [ebx+192], edx ; context->EFlags |
+ |
+ ; 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 dword ptr [ebx+196], edx ; context->Esp |
+ |
+ ; 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 dword ptr [ebx+140], edx ; context->SegGs |
+ mov dx, fs |
+ mov dword ptr [ebx+144], edx ; context->SegFs |
+ mov dx, es |
+ mov dword ptr [ebx+148], edx ; context->SegEs |
+ mov dx, ds |
+ mov dword ptr [ebx+152], edx ; context->SegDs |
+ mov dx, cs |
+ mov dword ptr [ebx+188], edx ; context->SegCs |
+ mov dx, ss |
+ mov dword ptr [ebx+200], edx ; context->SegSs |
+ |
+ ; 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 CONTEXT_i386 | CONTEXT_CONTROL |
+ ; | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT, and zero the |
+ ; ExtendedRegisters area. |
+ mov dword ptr [ebx], 1000fh ; context->ContextFlags |
+ lea edi, [ebx+204] ; context->ExtendedRegisters |
+ xor eax, eax |
+ mov ecx, 128 |
+ rep stosd |
+ jmp $FXSaveDone |
+ |
+$FXSave: |
+ ; fxsave is supported. Set ContextFlags to CONTEXT_i386 | CONTEXT_CONTROL | |
+ ; CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT | |
+ ; CONTEXT_EXTENDED_REGISTERS. |
+ mov dword ptr [ebx], 1002fh ; context->ContextFlags |
+ |
+ ; 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, 512 |
+ |
+ ; 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, 128 |
+ rep stosd |
+ |
+ fxsave [esp] |
+ |
+ ; Copy the temporary fxsave area into the CONTEXT structure. |
+ lea edi, [ebx+204] ; context->ExtendedRegisters |
+ mov esi, esp |
+ mov ecx, 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+28] ; context->FloatSave |
+ frstor [ebx+28] ; context->FloatSave |
+ |
+ mov dword ptr [ebx+136], 0 ; context->FloatSave.Spare0 |
scottmg
2015/09/29 21:58:40
Add " / .Cr0NpxState" to comment (for WOW64)
Mark Mentovai
2015/09/30 15:33:22
scottmg wrote:
|
+ |
+ ; 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 dword ptr [ebx+4], 0 ; context->Dr0 |
+ mov dword ptr [ebx+8], 0 ; context->Dr1 |
+ mov dword ptr [ebx+12], 0 ; context->Dr2 |
+ mov dword ptr [ebx+16], 0 ; context->Dr3 |
+ mov dword ptr [ebx+20], 0 ; context->Dr6 |
+ mov dword ptr [ebx+24], 0 ; context->Dr7 |
+ |
+ ; 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, dword ptr [ebx+156] ; context->Edi |
+ mov esi, dword ptr [ebx+160] ; context->Esi |
+ mov edx, dword ptr [ebx+168] ; context->Edx |
+ mov ecx, dword ptr [ebx+172] ; context->Ecx |
+ mov eax, dword ptr [ebx+176] ; context->Eax |
+ pop ebx |
+ popfd |
+ |
+ pop ebp |
+ |
+ ret |
+ |
+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 |
+ |
+ ; Set ContextFlags to CONTEXT_AMD64 | CONTEXT_CONTROL | CONTEXT_INTEGER | |
+ ; CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT. |
+ mov dword ptr [rcx+48], 10000fh ; context->ContextFlags |
+ |
+ ; General-purpose registers whose values haven’t changed can be captured |
+ ; directly. |
+ mov qword ptr [rcx+120], rax |
+ mov qword ptr [rcx+136], rdx |
+ mov qword ptr [rcx+144], rbx |
+ mov qword ptr [rcx+168], rsi |
+ mov qword ptr [rcx+176], rdi |
+ mov qword ptr [rcx+184], r8 |
+ mov qword ptr [rcx+192], r9 |
+ mov qword ptr [rcx+200], r10 |
+ mov qword ptr [rcx+208], r11 |
+ mov qword ptr [rcx+216], r12 |
+ mov qword ptr [rcx+224], r13 |
+ mov qword ptr [rcx+232], r14 |
+ mov qword ptr [rcx+240], 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 qword ptr [rcx+128], 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->FloatSave.MxCsr, saved by fxsave |
+ ; below. |
+ stmxcsr dword ptr [rcx+52] ; context->MxCsr |
+ |
+ ; Segment registers. |
+ mov ax, cs |
+ mov word ptr [rcx+56], ax ; context->SegCs |
+ mov ax, ds |
+ mov word ptr [rcx+58], ax ; context->SegDs |
+ mov ax, es |
+ mov word ptr [rcx+60], ax ; context->SegEs |
+ mov ax, fs |
+ mov word ptr [rcx+62], ax ; context->SegFs |
+ mov ax, gs |
+ mov word ptr [rcx+64], ax ; context->SegGs |
+ mov ax, ss |
+ mov word ptr [rcx+66], ax ; context->SegSs |
+ |
+ ; 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 dword ptr [rcx+68], eax ; context->EFlags |
+ |
+ ; 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 qword ptr [rcx+152], rax ; context->Rsp |
+ |
+ ; The original rbp was saved on the stack in this function’s prologue. |
+ mov rax, qword ptr [rbp] |
+ mov qword ptr [rcx+160], rax ; context->Rbp |
+ |
+ ; 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 qword ptr [rcx+248], rax ; context->Rip |
+ |
+ ; 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. Also, zero out the unused VectorRegister and VectorControl fields, |
+ ; and the debug control register fields. |
+ mov rbx, rcx |
+ cld |
+ lea rdi, [rcx+256] ; context->FltSave |
+ xor rax, rax |
+ mov rcx, 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. |
+ lea rax, [rcx+256] ; context->FltSave |
+ fxsave [rax] |
+ |
+ ; 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 qword ptr [rcx], 0 ; context->P1Home |
+ mov qword ptr [rcx+8], 0 ; context->P2Home |
+ mov qword ptr [rcx+16], 0 ; context->P3Home |
+ mov qword ptr [rcx+24], 0 ; context->P4Home |
+ mov qword ptr [rcx+32], 0 ; context->P5Home |
+ mov qword ptr [rcx+40], 0 ; context->P6Home |
+ |
+ ; 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 qword ptr [rcx+72], 0 ; context->Dr0 |
+ mov qword ptr [rcx+80], 0 ; context->Dr1 |
+ mov qword ptr [rcx+88], 0 ; context->Dr2 |
+ mov qword ptr [rcx+96], 0 ; context->Dr3 |
+ mov qword ptr [rcx+104], 0 ; context->Dr6 |
+ mov qword ptr [rcx+112], 0 ; context->Dr7 |
+ |
+ ; 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, qword ptr [rcx+120] ; context->Rax |
+ mov rbx, qword ptr [rcx+144] ; context->Rbx |
+ mov rdi, qword ptr [rcx+176] ; context->Rdi |
+ popfq |
+ |
+ pop rbp |
+ |
+ ret |
+ |
+endif |
+ |
+CAPTURECONTEXT_SYMBOL endp |
+_TEXT ends |
+end |