Chromium Code Reviews| 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 |