// Copyright 2018 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.
#include "util/misc/arm64_pac_bti.S"
// namespace crashpad {
// void CaptureContext(ucontext_t* context);
// } // namespace crashpad
// The type name for a ucontext_t varies by libc implementation and version.
// Bionic and glibc 2.25 typedef ucontext_t from struct ucontext. glibc 2.26+
// typedef ucontext_t from struct ucontext_t. Alias the symbol names to maintain
// compatibility with both possibilities.
#define CAPTURECONTEXT_SYMBOL _ZN8crashpad14CaptureContextEP10ucontext_t
#define CAPTURECONTEXT_SYMBOL2 _ZN8crashpad14CaptureContextEP8ucontext
.text
.globl CAPTURECONTEXT_SYMBOL
.globl CAPTURECONTEXT_SYMBOL2
#if defined(__i386__) || defined(__x86_64__)
.balign 16, 0x90
#elif defined(__arm__) || defined(__aarch64__)
.balign 4, 0x0
.type CAPTURECONTEXT_SYMBOL, %function
.type CAPTURECONTEXT_SYMBOL2, %function
#elif defined(__mips__)
.balign 4, 0x0
#elif defined(__riscv)
.balign 4, 0x0
#endif
CAPTURECONTEXT_SYMBOL:
CAPTURECONTEXT_SYMBOL2:
CRASHPAD_AARCH64_VALID_CALL_TARGET
#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 ucontext_t*
// argument. The original value of %eax will be in -8(%ebp).
pushl %eax
movl 8(%ebp), %eax
// Save the original value of %ecx, and use %ecx as a scratch register.
pushl %ecx
// The segment registers are 16 bits wide, but mcontext_t declares them
// as unsigned 32-bit values, so zero the top half.
xorl %ecx, %ecx
movw %gs, %cx
movl %ecx, 0x14(%eax) // context->uc_mcontext.xgs
movw %fs, %cx
movl %ecx, 0x18(%eax) // context->uc_mcontext.xfs
movw %es, %cx
movl %ecx, 0x1c(%eax) // context->uc_mcontext.xes
movw %ds, %cx
movl %ecx, 0x20(%eax) // context->uc_mcontext.xds
// General-purpose registers whose values haven’t changed can be captured
// directly.
movl %edi, 0x24(%eax) // context->uc_mcontext.edi
movl %esi, 0x28(%eax) // context->uc_mcontext.esi
// The original %ebp was saved on the stack in this function’s prologue.
movl (%ebp), %ecx
movl %ecx, 0x2c(%eax) // context->uc_mcontext.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), %ecx
movl %ecx, 0x30(%eax) // context->uc_mcontext.esp
// More general-purpose registers
movl %ebx, 0x34(%eax) // context->uc_mcontext.ebx
movl %edx, 0x38(%eax) // context->uc_mcontext.edx
// The original %ecx was saved on the stack above.
movl -12(%ebp), %ecx
movl %ecx, 0x3c(%eax) // context->uc_mcontext.ecx
// The original %eax was saved on the stack above.
movl -8(%ebp), %ecx
movl %ecx, 0x40(%eax) // context->uc_mcontext.eax
// trapno and err are unused so zero them out.
xorl %ecx, %ecx
movl %ecx, 0x44(%eax) // context->uc_mcontext.trapno
movl %ecx, 0x48(%eax) // context->uc_mcontext.err
// %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), %ecx
movl %ecx, 0x4c(%eax) // context->uc_mcontext.eip
// More segment registers
xorl %ecx, %ecx
movw %cs, %cx
movl %ecx, 0x50(%eax) // context->uc_mcontext.xcs
// The original %eflags was saved on the stack above.
movl -4(%ebp), %ecx
movl %ecx, 0x54(%eax) // context->uc_mcontext.eflags
// uesp is unused so zero it out.
xorl %ecx, %ecx
movl %ecx, 0x58(%eax) // context->uc_mcontext.uesp
// The last segment register.
movw %ss, %cx
movl %ecx, 0x5c(%eax) // context->uc_mcontext.xss
// TODO(jperaza): save floating-point registers.
xorl %ecx, %ecx
movl %ecx, 0x60(%eax) // context->uc_mcontext.fpregs
// 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.
popl %ecx
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
// General-purpose registers whose values haven’t changed can be captured
// directly.
movq %r8, 0x28(%rdi) // context->uc_mcontext.r8
movq %r9, 0x30(%rdi) // context->uc_mcontext.r9
movq %r10, 0x38(%rdi) // context->uc_mcontext.r10
movq %r11, 0x40(%rdi) // context->uc_mcontext.r11
movq %r12, 0x48(%rdi) // context->uc_mcontext.r12
movq %r13, 0x50(%rdi) // context->uc_mcontext.r13
movq %r14, 0x58(%rdi) // context->uc_mcontext.r14
movq %r15, 0x60(%rdi) // context->uc_mcontext.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, 0x68(%rdi) // context->uc_mcontext.rdi
movq %rsi, 0x70(%rdi) // context->uc_mcontext.rsi
// Use %r8 as a scratch register now that it has been saved.
// The original %rbp was saved on the stack in this function’s prologue.
movq (%rbp), %r8
movq %r8, 0x78(%rdi) // context->uc_mcontext.rbp
// Save the remaining general-purpose registers.
movq %rbx, 0x80(%rdi) // context->uc_mcontext.rbx
movq %rdx, 0x88(%rdi) // context->uc_mcontext.rdx
movq %rax, 0x90(%rdi) // context->uc_mcontext.rax
movq %rcx, 0x98(%rdi) // context->uc_mcontext.rcx
// %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), %r8
movq %r8, 0xa0(%rdi) // context->uc_mcontext.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), %r8
movq %r8, 0xa8(%rdi) // context->uc_mcontext.rip
// The original %rflags was saved on the stack above.
movq -8(%rbp), %r8
movq %r8, 0xb0(%rdi) // context->uc_mcontext.eflags
// Save the segment registers
movw %cs, 0xb8(%rdi) // context->uc_mcontext.cs
movw %gs, 0xba(%rdi) // context->uc_mcontext.gs
movw %fs, 0xbc(%rdi) // context->uc_mcontext.fs
xorw %ax, %ax
movw %ax, 0xbe(%rdi) // context->uc_mcontext.padding
// Zero out the remainder of the unused pseudo-registers
xorq %r8, %r8
movq %r8, 0xc0(%rdi) // context->uc_mcontext.err
movq %r8, 0xc8(%rdi) // context->uc_mcontext.trapno
movq %r8, 0xd0(%rdi) // context->uc_mcontext.oldmask
movq %r8, 0xd8(%rdi) // context->uc_mcontext.cr2
// TODO(jperaza): save floating-point registers.
movq %r8, 0xe0(%rdi) // context->uc_mcontext.fpregs
// 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 0x90(%rdi), %rax
movq 0x28(%rdi), %r8
popfq
popq %rbp
ret
.cfi_endproc
#elif defined(__arm__)
// The original r0 can't be recovered.
str r0, [r0, #0x20]
// Now advance r0 to point to the register array.
add r0, r0, #0x24
// Save registers r1-r12 at context->uc_mcontext.regs[i].
stm r0, {r1-r12}
// Restore r0.
sub r0, r0, #0x24
// Save SP/r13.
str SP, [r0, #0x54] // context->uc_mcontext.sp
// The original LR can't be recovered.
str LR, [r0, #0x58] // context->uc_mcontext.lr
// The link register holds the return address for this function.
str LR, [r0, #0x5c] // context->uc_mcontext.pc
// Use r1 as a scratch register.
// CPSR is a deprecated synonym for APSR.
mrs r1, APSR
str r1, [r0, #0x60] // context->uc_mcontext.cpsr
// Zero out unused fields.
mov r1, #0x0
str r1, [r0, #0x14] // context->uc_mcontext.trap_no
str r1, [r0, #0x18] // context->uc_mcontext.error_code
str r1, [r0, #0x1c] // context->uc_mcontext.oldmask
str r1, [r0, #0x64] // context->uc_mcontext.fault_address
// Restore r1.
ldr r1, [r0, #0x24]
// TODO(https://crashpad.chromium.org/bug/300): save floating-point registers.
mov PC, LR
#elif defined(__aarch64__)
// Zero out fault_address, which is unused.
str xzr, [x0, #0xb0] // context->uc_mcontext.fault_address
// Save general purpose registers in context->uc_mcontext.regs[i].
// The original x0 can't be recovered.
stp x0, x1, [x0, #0xb8]
stp x2, x3, [x0, #0xc8]
stp x4, x5, [x0, #0xd8]
stp x6, x7, [x0, #0xe8]
stp x8, x9, [x0, #0xf8]
stp x10, x11, [x0, #0x108]
stp x12, x13, [x0, #0x118]
stp x14, x15, [x0, #0x128]
stp x16, x17, [x0, #0x138]
stp x18, x19, [x0, #0x148]
stp x20, x21, [x0, #0x158]
stp x22, x23, [x0, #0x168]
stp x24, x25, [x0, #0x178]
stp x26, x27, [x0, #0x188]
stp x28, x29, [x0, #0x198]
// The original LR can't be recovered, therefore no need to sign x30 with PAC.
str x30, [x0, #0x1a8]
// Use x1 as a scratch register.
mov x1, SP
str x1, [x0, #0x1b0] // context->uc_mcontext.sp
// The link register holds the return address for this function and won't be
// recovered, therefore no need to sign x30 with PAC.
str x30, [x0, #0x1b8] // context->uc_mcontext.pc
// pstate should hold SPSR but NZCV are the only bits we know about.
mrs x1, NZCV
str x1, [x0, #0x1c0] // context->uc_mcontext.pstate
// Restore x1 from the saved context.
ldr x1, [x0, #0xc0]
// TODO(https://crashpad.chromium.org/bug/300): save floating-point registers.
ret
#elif defined(__mips__)
.set noat
#if _MIPS_SIM == _ABIO32
#define STORE sw
#define MCONTEXT_FPREG_SIZE 4
#define MCONTEXT_PC_OFFSET 32
#else
#define STORE sd
#define MCONTEXT_FPREG_SIZE 8
#define MCONTEXT_PC_OFFSET 616
#endif
#define MCONTEXT_REG_SIZE 8
#define MCONTEXT_GREGS_OFFSET 40
#define MCONTEXT_FPREGS_OFFSET 296
// Value of register 0 is always 0.
// Registers 26 and 27 are reserved for kernel, and shouldn't be used.
STORE $1, (1 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $2, (2 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $3, (3 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $4, (4 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $5, (5 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $6, (6 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $7, (7 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $8, (8 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $9, (9 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $10, (10 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $11, (11 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $12, (12 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $13, (13 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $14, (14 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $15, (15 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $16, (16 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $17, (17 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $18, (18 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $19, (19 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $20, (20 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $21, (21 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $22, (22 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $23, (23 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $24, (24 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $25, (25 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $28, (28 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $29, (29 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $30, (30 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $31, (31 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $31, (MCONTEXT_PC_OFFSET)($a0)
#ifdef __mips_hard_float
s.d $f0, (0 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f2, (2 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f4, (4 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f6, (6 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f8, (8 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f10, (10 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f12, (12 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f14, (14 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f16, (16 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f18, (18 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f20, (20 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f22, (22 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f24, (24 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f26, (26 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f28, (28 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f30, (30 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
#if _MIPS_SIM != _ABIO32
s.d $f1, (1 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f3, (3 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f5, (5 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f7, (7 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f9, (9 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f11, (11 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f13, (13 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f15, (15 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f17, (17 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f19, (19 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f21, (21 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f23, (23 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f25, (25 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f27, (27 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f29, (29 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f31, (31 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
#endif // _MIPS_SIM != _ABIO32
#endif // __mips_hard_float
jr $ra
.set at
#elif defined(__riscv)
#define MCONTEXT_GREGS_OFFSET 176
// x1/ra is the return address. Store it as the pc.
// The original x10/a0 can't be recovered.
sd x1, (0 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x1, (1 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x2, (2 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x3, (3 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x4, (4 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x5, (5 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x6, (6 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x7, (7 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x8, (8 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x9, (9 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x10, (10 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x11, (11 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x12, (12 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x13, (13 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x14, (14 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x15, (15 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x16, (16 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x17, (17 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x18, (18 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x19, (19 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x20, (20 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x21, (21 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x22, (22 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x23, (23 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x24, (24 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x25, (25 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x26, (26 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x27, (27 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x28, (28 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x29, (29 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x30, (30 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x31, (31 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
#define MCONTEXT_FPREGS_OFFSET MCONTEXT_GREGS_OFFSET + 32*8
// Use x31/t6 as scratch register.
frcsr x31
sw x31, (32 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f0, (0 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f1, (1 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f2, (2 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f3, (3 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f4, (4 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f5, (5 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f6, (6 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f7, (7 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f8, (8 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f9, (9 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f10, (10 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f11, (11 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f12, (12 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f13, (13 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f14, (14 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f15, (15 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f16, (16 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f17, (17 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f18, (18 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f19, (19 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f20, (20 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f21, (21 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f22, (22 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f23, (23 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f24, (24 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f25, (25 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f26, (26 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f27, (27 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f28, (28 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f29, (29 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f30, (30 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f31, (31 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
ret
#endif // __i386__