crashpad/util/misc/capture_context_linux.S
Thomas Gales 4f5dd67229 [riscv] Add RISC-V Linux support
Only RV64GC is supported.

Bug: fuchsia:127655

Tested: `python build/run_tests.py` on RISC-V emulator
Tested: Created minidump via self-induced crash on RISC-V emulator,
ran through Breakpad stackwalker

Change-Id: I713797cd623b0a758269048e01696cbce502ca6c
Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/4581050
Reviewed-by: Joshua Peraza <jperaza@chromium.org>
2023-06-12 21:13:24 +00:00

514 lines
19 KiB
ArmAsm
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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 havent 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 functions prologue.
movl (%ebp), %ecx
movl %ecx, 0x2c(%eax) // context->uc_mcontext.ebp
// %esp was saved in %ebp in this functions prologue, but the callers %esp
// is 8 more than this value: 4 for the original %ebp saved on the stack in
// this functions 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 cant 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
// functions 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 havent 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, theres no way to recover the value of
// the callers %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 functions 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 functions prologue, but the callers %rsp
// is 16 more than this value: 8 for the original %rbp saved on the stack in
// this functions 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 cant 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
// functions 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__