// Copyright 2014 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. #if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) #if defined(__i386__) || defined(__x86_64__) // namespace crashpad { // void CaptureContext(x86_thread_state_t* x86_thread_state); // } // namespace crashpad #define CAPTURECONTEXT_SYMBOL __ZN8crashpad14CaptureContextEP16x86_thread_state #elif defined(__aarch64__) // namespace crashpad { // void CaptureContext(arm_unified_thread_state_t* arm_unified_thread_state); // } // namespace crashpad #define CAPTURECONTEXT_SYMBOL \ __ZN8crashpad14CaptureContextEP24arm_unified_thread_state #endif .section __TEXT,__text,regular,pure_instructions .private_extern CAPTURECONTEXT_SYMBOL .globl CAPTURECONTEXT_SYMBOL #if defined(__i386__) || defined(__x86_64__) .p2align 4, 0x90 #elif defined(__aarch64__) .p2align 2 #endif CAPTURECONTEXT_SYMBOL: #if defined(__i386__) .cfi_startproc pushl %ebp .cfi_def_cfa_offset 8 .cfi_offset %ebp, -8 movl %esp, %ebp .cfi_def_cfa_register %ebp // Note that 16-byte stack alignment is not maintained because this function // does not call out to any other. // pushfl first, because some instructions (but probably none used here) // affect %eflags. %eflags will be in -4(%ebp). pushfl // Save the original value of %eax, and use %eax to hold the x86_thread_state* // argument. The original value of %eax will be in -8(%ebp). pushl %eax movl 8(%ebp), %eax // Initialize the header identifying the x86_thread_state_t structure as // carrying an x86_thread_state32_t (flavor x86_THREAD_STATE32) of size // x86_THREAD_STATE32_COUNT 32-bit values. movl $1, (%eax) // x86_thread_state->tsh.flavor movl $16, 4(%eax) // x86_thread_state->tsh.count // General-purpose registers whose values haven’t changed can be captured // directly. movl %ebx, 12(%eax) // x86_thread_state->uts.ts32.__ebx movl %ecx, 16(%eax) // x86_thread_state->uts.ts32.__ecx movl %edx, 20(%eax) // x86_thread_state->uts.ts32.__edx movl %edi, 24(%eax) // x86_thread_state->uts.ts32.__edi movl %esi, 28(%eax) // x86_thread_state->uts.ts32.__esi // Now that the original value of %edx has been saved, it can be repurposed to // hold other registers’ values. // The original %eax was saved on the stack above. movl -8(%ebp), %edx movl %edx, 8(%eax) // x86_thread_state->uts.ts32.__eax // The original %ebp was saved on the stack in this function’s prologue. movl (%ebp), %edx movl %edx, 32(%eax) // x86_thread_state->uts.ts32.__ebp // %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. leal 8(%ebp), %edx movl %edx, 36(%eax) // x86_thread_state->uts.ts32.__esp // The original %eflags was saved on the stack above. movl -4(%ebp), %edx movl %edx, 44(%eax) // x86_thread_state->uts.ts32.__eflags // %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. movl 4(%ebp), %edx movl %edx, 48(%eax) // x86_thread_state->uts.ts32.__eip // The segment registers are 16 bits wide, but x86_thread_state declares them // as unsigned 32-bit values, so zero the top half. xorl %edx, %edx movw %ss, %dx movl %edx, 40(%eax) // x86_thread_state->uts.ts32.__ss movw %cs, %dx movl %edx, 52(%eax) // x86_thread_state->uts.ts32.__cs movw %ds, %dx movl %edx, 56(%eax) // x86_thread_state->uts.ts32.__ds movw %es, %dx movl %edx, 60(%eax) // x86_thread_state->uts.ts32.__es movw %fs, %dx movl %edx, 64(%eax) // x86_thread_state->uts.ts32.__fs movw %gs, %dx movl %edx, 68(%eax) // x86_thread_state->uts.ts32.__gs // 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. movl 20(%eax), %edx // x86_thread_state->uts.ts32.__edx popl %eax popfl popl %ebp ret .cfi_endproc #elif defined(__x86_64__) .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset %rbp, -16 movq %rsp, %rbp .cfi_def_cfa_register %rbp // Note that 16-byte stack alignment is not maintained because this function // does not call out to any other. // pushfq first, because some instructions (but probably none used here) // affect %rflags. %rflags will be in -8(%rbp). pushfq // Initialize the header identifying the x86_thread_state_t structure as // carrying an x86_thread_state64_t (flavor x86_THREAD_STATE64) of size // x86_THREAD_STATE64_COUNT 32-bit values. movl $4, (%rdi) // x86_thread_state->tsh.flavor movl $42, 4(%rdi) // x86_thread_state->tsh.count // General-purpose registers whose values haven’t changed can be captured // directly. movq %rax, 8(%rdi) // x86_thread_state->uts.ts64.__rax movq %rbx, 16(%rdi) // x86_thread_state->uts.ts64.__rbx movq %rcx, 24(%rdi) // x86_thread_state->uts.ts64.__rcx movq %rdx, 32(%rdi) // x86_thread_state->uts.ts64.__rdx movq %rsi, 48(%rdi) // x86_thread_state->uts.ts64.__rsi movq %r8, 72(%rdi) // x86_thread_state->uts.ts64.__r8 movq %r9, 80(%rdi) // x86_thread_state->uts.ts64.__r9 movq %r10, 88(%rdi) // x86_thread_state->uts.ts64.__r10 movq %r11, 96(%rdi) // x86_thread_state->uts.ts64.__r11 movq %r12, 104(%rdi) // x86_thread_state->uts.ts64.__r12 movq %r13, 112(%rdi) // x86_thread_state->uts.ts64.__r13 movq %r14, 120(%rdi) // x86_thread_state->uts.ts64.__r14 movq %r15, 128(%rdi) // x86_thread_state->uts.ts64.__r15 // Because of the calling convention, there’s no way to recover the value of // the caller’s %rdi as it existed prior to calling this function. This // function captures a snapshot of the register state at its return, which // involves %rdi containing a pointer to its first argument. Callers that // require the value of %rdi prior to calling this function should obtain it // separately. For example: // uint64_t rdi; // asm("movq %%rdi, %0" : "=m"(rdi)); movq %rdi, 40(%rdi) // x86_thread_state->uts.ts64.__rdi // Now that the original value of %rax has been saved, it can be repurposed to // hold other registers’ values. // The original %rbp was saved on the stack in this function’s prologue. movq (%rbp), %rax movq %rax, 56(%rdi) // x86_thread_state->uts.ts64.__rbp // %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. leaq 16(%rbp), %rax movq %rax, 64(%rdi) // x86_thread_state->uts.ts64.__rsp // %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. movq 8(%rbp), %rax movq %rax, 136(%rdi) // x86_thread_state->uts.ts64.__rip // The original %rflags was saved on the stack above. movq -8(%rbp), %rax movq %rax, 144(%rdi) // x86_thread_state->uts.ts64.__rflags // The segment registers are 16 bits wide, but x86_thread_state declares them // as unsigned 64-bit values, so zero the top portion. xorq %rax, %rax movw %cs, %ax movq %rax, 152(%rdi) // x86_thread_state->uts.ts64.__cs movw %fs, %ax movq %rax, 160(%rdi) // x86_thread_state->uts.ts64.__fs movw %gs, %ax movq %rax, 168(%rdi) // x86_thread_state->uts.ts64.__gs // 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. movq 8(%rdi), %rax popfq popq %rbp ret .cfi_endproc #elif defined(__aarch64__) .cfi_startproc // Save general-purpose registers in arm_unified_thread_state->ts_64.__x[0]. // The original x0 can't be recovered. stp x0, x1, [x0, #0x8] stp x2, x3, [x0, #0x18] stp x4, x5, [x0, #0x28] stp x6, x7, [x0, #0x38] stp x8, x9, [x0, #0x48] stp x10, x11, [x0, #0x58] stp x12, x13, [x0, #0x68] stp x14, x15, [x0, #0x78] stp x16, x17, [x0, #0x88] stp x18, x19, [x0, #0x98] stp x20, x21, [x0, #0xa8] stp x22, x23, [x0, #0xb8] stp x24, x25, [x0, #0xc8] stp x26, x27, [x0, #0xd8] // Save the last general-purpose register (x28) and the frame pointer (x29). stp x28, x29, [x0, #0xe8] // __x[28] and __fp // Save the link register (x30) and the stack pointer (using x1 as a scratch // register) mov x1, sp stp x30, x1, [x0, #0xf8] // __lr and __sp // The link register (x30) holds the return address for this function. // __cpsr should hold current program status register, but nzcv are the only // bits we know about (saved using x1 as a scratch register). The 64-bit x1 // covers both the 32-bit __cpsr (which receives the nzcv bits) and __pad // (which will be zeroed). mrs x1, nzcv stp x30, x1, [x0, #0x108] // __pc and __cpsr and __pad // Initialize the header identifying the arm_unified_thread_state structure as // carrying an arm_thread_state64_t (flavor ARM_THREAD_STATE64) of size // ARM_THREAD_STATE64_COUNT 32-bit values. mov x1, #6 movk x1, #68, lsl #32 str x1, [x0, #0x0] // arm_thread_state->ash.flavor and count // Restore x1 from the saved context. ldr x1, [x0, #0x10] // TODO(justincohen): Consider saving floating-point registers into // arm_neon_state64_t as second parameter, or as a a second function call // after all of the general-purpose state is captured, or as a new struct that // has both arm_unified_state_t and arm_neon_state64_t members. That may be // better than a second parameter (which occupies another register) and better // than a second function call. ret .cfi_endproc #endif .subsections_via_symbols #endif