blob: 528dba7a97f621ba9f6cccc3722ff286fbf8ac23 [file] [log] [blame]
// Copyright (c) 2012, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// A minimalistic implementation of getcontext() to be used by
// Google Breakpad when getcontext() is not available in libc.
#include "common/linux/ucontext_constants.h"
/* int getcontext (ucontext_t* ucp) */
#if defined(__arm__)
.text
.global breakpad_getcontext
.hidden breakpad_getcontext
.type breakpad_getcontext, #function
.align 0
.fnstart
breakpad_getcontext:
/* First, save r4-r11 */
add r1, r0, #(MCONTEXT_GREGS_OFFSET + 4*4)
stm r1, {r4-r11}
/* r12 is a scratch register, don't save it */
/* Save sp and lr explicitly. */
/* - sp can't be stored with stmia in Thumb-2 */
/* - STM instructions that store sp and pc are deprecated in ARM */
str sp, [r0, #(MCONTEXT_GREGS_OFFSET + 13*4)]
str lr, [r0, #(MCONTEXT_GREGS_OFFSET + 14*4)]
/* Save the caller's address in 'pc' */
str lr, [r0, #(MCONTEXT_GREGS_OFFSET + 15*4)]
/* Save ucontext_t* pointer across next call */
mov r4, r0
/* Call sigprocmask(SIG_BLOCK, NULL, &(ucontext->uc_sigmask)) */
mov r0, #0 /* SIG_BLOCK */
mov r1, #0 /* NULL */
add r2, r4, #UCONTEXT_SIGMASK_OFFSET
bl sigprocmask(PLT)
/* Intentionally do not save the FPU state here. This is because on
* Linux/ARM, one should instead use ptrace(PTRACE_GETFPREGS) or
* ptrace(PTRACE_GETVFPREGS) to get it.
*
* Note that a real implementation of getcontext() would need to save
* this here to allow setcontext()/swapcontext() to work correctly.
*/
/* Restore the values of r4 and lr */
mov r0, r4
ldr lr, [r0, #(MCONTEXT_GREGS_OFFSET + 14*4)]
ldr r4, [r0, #(MCONTEXT_GREGS_OFFSET + 4*4)]
/* Return 0 */
mov r0, #0
bx lr
.fnend
.size breakpad_getcontext, . - breakpad_getcontext
#elif defined(__aarch64__)
#define _NSIG 64
#define __NR_rt_sigprocmask 135
.text
.global breakpad_getcontext
.hidden breakpad_getcontext
.type breakpad_getcontext, #function
.align 4
.cfi_startproc
breakpad_getcontext:
/* The saved context will return to the getcontext() call point
with a return value of 0 */
str xzr, [x0, MCONTEXT_GREGS_OFFSET + 0 * REGISTER_SIZE]
stp x18, x19, [x0, MCONTEXT_GREGS_OFFSET + 18 * REGISTER_SIZE]
stp x20, x21, [x0, MCONTEXT_GREGS_OFFSET + 20 * REGISTER_SIZE]
stp x22, x23, [x0, MCONTEXT_GREGS_OFFSET + 22 * REGISTER_SIZE]
stp x24, x25, [x0, MCONTEXT_GREGS_OFFSET + 24 * REGISTER_SIZE]
stp x26, x27, [x0, MCONTEXT_GREGS_OFFSET + 26 * REGISTER_SIZE]
stp x28, x29, [x0, MCONTEXT_GREGS_OFFSET + 28 * REGISTER_SIZE]
str x30, [x0, MCONTEXT_GREGS_OFFSET + 30 * REGISTER_SIZE]
/* Place LR into the saved PC, this will ensure that when
switching to this saved context with setcontext() control
will pass back to the caller of getcontext(), we have
already arranged to return the appropriate return value in x0
above. */
str x30, [x0, MCONTEXT_PC_OFFSET]
/* Save the current SP */
mov x2, sp
str x2, [x0, MCONTEXT_SP_OFFSET]
/* Initialize the pstate. */
str xzr, [x0, MCONTEXT_PSTATE_OFFSET]
/* Figure out where to place the first context extension
block. */
add x2, x0, #MCONTEXT_EXTENSION_OFFSET
/* Write the context extension fpsimd header. */
mov w3, #(FPSIMD_MAGIC & 0xffff)
movk w3, #(FPSIMD_MAGIC >> 16), lsl #16
str w3, [x2, #FPSIMD_CONTEXT_MAGIC_OFFSET]
mov w3, #FPSIMD_CONTEXT_SIZE
str w3, [x2, #FPSIMD_CONTEXT_SIZE_OFFSET]
/* Fill in the FP SIMD context. */
add x3, x2, #(FPSIMD_CONTEXT_VREGS_OFFSET + 8 * SIMD_REGISTER_SIZE)
stp d8, d9, [x3], #(2 * SIMD_REGISTER_SIZE)
stp d10, d11, [x3], #(2 * SIMD_REGISTER_SIZE)
stp d12, d13, [x3], #(2 * SIMD_REGISTER_SIZE)
stp d14, d15, [x3], #(2 * SIMD_REGISTER_SIZE)
add x3, x2, FPSIMD_CONTEXT_FPSR_OFFSET
mrs x4, fpsr
str w4, [x3]
mrs x4, fpcr
str w4, [x3, FPSIMD_CONTEXT_FPCR_OFFSET - FPSIMD_CONTEXT_FPSR_OFFSET]
/* Write the termination context extension header. */
add x2, x2, #FPSIMD_CONTEXT_SIZE
str xzr, [x2, #FPSIMD_CONTEXT_MAGIC_OFFSET]
str xzr, [x2, #FPSIMD_CONTEXT_SIZE_OFFSET]
/* Grab the signal mask */
/* rt_sigprocmask (SIG_BLOCK, NULL, &ucp->uc_sigmask, _NSIG8) */
add x2, x0, #UCONTEXT_SIGMASK_OFFSET
mov x0, #0 /* SIG_BLOCK */
mov x1, #0 /* NULL */
mov x3, #(_NSIG / 8)
mov x8, #__NR_rt_sigprocmask
svc 0
/* Return x0 for success */
mov x0, 0
ret
.cfi_endproc
.size breakpad_getcontext, . - breakpad_getcontext
#elif defined(__i386__)
.text
.global breakpad_getcontext
.hidden breakpad_getcontext
.align 4
.type breakpad_getcontext, @function
breakpad_getcontext:
movl 4(%esp), %eax /* eax = uc */
/* Save register values */
movl %ecx, MCONTEXT_ECX_OFFSET(%eax)
movl %edx, MCONTEXT_EDX_OFFSET(%eax)
movl %ebx, MCONTEXT_EBX_OFFSET(%eax)
movl %edi, MCONTEXT_EDI_OFFSET(%eax)
movl %esi, MCONTEXT_ESI_OFFSET(%eax)
movl %ebp, MCONTEXT_EBP_OFFSET(%eax)
movl (%esp), %edx /* return address */
lea 4(%esp), %ecx /* exclude return address from stack */
mov %edx, MCONTEXT_EIP_OFFSET(%eax)
mov %ecx, MCONTEXT_ESP_OFFSET(%eax)
xorl %ecx, %ecx
movw %fs, %cx
mov %ecx, MCONTEXT_FS_OFFSET(%eax)
movl $0, MCONTEXT_EAX_OFFSET(%eax)
/* Save floating point state to fpregstate, then update
* the fpregs pointer to point to it */
leal UCONTEXT_FPREGS_MEM_OFFSET(%eax), %ecx
fnstenv (%ecx)
fldenv (%ecx)
mov %ecx, UCONTEXT_FPREGS_OFFSET(%eax)
/* Save signal mask: sigprocmask(SIGBLOCK, NULL, &uc->uc_sigmask) */
leal UCONTEXT_SIGMASK_OFFSET(%eax), %edx
xorl %ecx, %ecx
push %edx /* &uc->uc_sigmask */
push %ecx /* NULL */
push %ecx /* SIGBLOCK == 0 on i386 */
call sigprocmask@PLT
addl $12, %esp
movl $0, %eax
ret
.size breakpad_getcontext, . - breakpad_getcontext
#elif defined(__mips__)
// This implementation is inspired by implementation of getcontext in glibc.
#include <asm-mips/asm.h>
#include <asm-mips/regdef.h>
#if _MIPS_SIM == _ABIO32
#include <asm-mips/fpregdef.h>
#endif
// from asm-mips/asm.h
#if _MIPS_SIM == _ABIO32
#define ALSZ 7
#define ALMASK ~7
#define SZREG 4
#else // _MIPS_SIM != _ABIO32
#define ALSZ 15
#define ALMASK ~15
#define SZREG 8
#endif
#include <asm/unistd.h> // for __NR_rt_sigprocmask
#define _NSIG8 128 / 8
#define SIG_BLOCK 1
.text
LOCALS_NUM = 1 // save gp on stack
FRAME_SIZE = ((LOCALS_NUM * SZREG) + ALSZ) & ALMASK
GP_FRAME_OFFSET = FRAME_SIZE - (1 * SZREG)
MCONTEXT_REG_SIZE = 8
#if _MIPS_SIM == _ABIO32
NESTED (breakpad_getcontext, FRAME_SIZE, ra)
.mask 0x00000000, 0
.fmask 0x00000000, 0
.set noreorder
.cpload t9
.set reorder
move a2, sp
#define _SP a2
addiu sp, -FRAME_SIZE
.cprestore GP_FRAME_OFFSET
sw s0, (16 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sw s1, (17 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sw s2, (18 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sw s3, (19 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sw s4, (20 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sw s5, (21 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sw s6, (22 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sw s7, (23 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sw _SP, (29 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sw fp, (30 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sw ra, (31 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sw ra, MCONTEXT_PC_OFFSET(a0)
#ifdef __mips_hard_float
s.d fs0, (20 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
s.d fs1, (22 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
s.d fs2, (24 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
s.d fs3, (26 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
s.d fs4, (28 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
s.d fs5, (30 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
cfc1 v1, fcr31
sw v1, MCONTEXT_FPC_CSR(a0)
#endif // __mips_hard_float
/* rt_sigprocmask (SIG_BLOCK, NULL, &ucp->uc_sigmask, _NSIG8) */
li a3, _NSIG8
addu a2, a0, UCONTEXT_SIGMASK_OFFSET
move a1, zero
li a0, SIG_BLOCK
li v0, __NR_rt_sigprocmask
syscall
addiu sp, FRAME_SIZE
jr ra
END (breakpad_getcontext)
#else
#ifndef NESTED
/*
* NESTED - declare nested routine entry point
*/
#define NESTED(symbol, framesize, rpc) \
.globl symbol; \
.align 2; \
.type symbol,@function; \
.ent symbol,0; \
symbol: .frame sp, framesize, rpc;
#endif
/*
* END - mark end of function
*/
#ifndef END
# define END(function) \
.end function; \
.size function,.-function
#endif
/* int getcontext (ucontext_t* ucp) */
NESTED (breakpad_getcontext, FRAME_SIZE, ra)
.mask 0x10000000, 0
.fmask 0x00000000, 0
move a2, sp
#define _SP a2
move a3, gp
#define _GP a3
daddiu sp, -FRAME_SIZE
.cpsetup $25, GP_FRAME_OFFSET, breakpad_getcontext
/* Store a magic flag. */
li v1, 1
sd v1, (0 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0) /* zero */
sd s0, (16 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sd s1, (17 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sd s2, (18 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sd s3, (19 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sd s4, (20 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sd s5, (21 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sd s6, (22 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sd s7, (23 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sd _GP, (28 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sd _SP, (29 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sd s8, (30 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sd ra, (31 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sd ra, MCONTEXT_PC_OFFSET(a0)
#ifdef __mips_hard_float
s.d $f24, (24 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
s.d $f25, (25 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
s.d $f26, (26 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
s.d $f27, (27 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
s.d $f28, (28 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
s.d $f29, (29 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
s.d $f30, (30 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
s.d $f31, (31 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
cfc1 v1, $31
sw v1, MCONTEXT_FPC_CSR(a0)
#endif /* __mips_hard_float */
/* rt_sigprocmask (SIG_BLOCK, NULL, &ucp->uc_sigmask, _NSIG8) */
li a3, _NSIG8
daddu a2, a0, UCONTEXT_SIGMASK_OFFSET
move a1, zero
li a0, SIG_BLOCK
li v0, __NR_rt_sigprocmask
syscall
.cpreturn
daddiu sp, FRAME_SIZE
move v0, zero
jr ra
END (breakpad_getcontext)
#endif // _MIPS_SIM == _ABIO32
#elif defined(__x86_64__)
/* The x64 implementation of breakpad_getcontext was derived in part
from the implementation of libunwind which requires the following
notice. */
/* libunwind - a platform-independent unwind library
Copyright (C) 2008 Google, Inc
Contributed by Paul Pluzhnikov <ppluzhnikov@google.com>
Copyright (C) 2010 Konstantin Belousov <kib@freebsd.org>
This file is part of libunwind.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
.text
.global breakpad_getcontext
.hidden breakpad_getcontext
.align 4
.type breakpad_getcontext, @function
breakpad_getcontext:
.cfi_startproc
/* Callee saved: RBX, RBP, R12-R15 */
movq %r12, MCONTEXT_GREGS_R12(%rdi)
movq %r13, MCONTEXT_GREGS_R13(%rdi)
movq %r14, MCONTEXT_GREGS_R14(%rdi)
movq %r15, MCONTEXT_GREGS_R15(%rdi)
movq %rbp, MCONTEXT_GREGS_RBP(%rdi)
movq %rbx, MCONTEXT_GREGS_RBX(%rdi)
/* Save argument registers (not strictly needed, but setcontext
restores them, so don't restore garbage). */
movq %r8, MCONTEXT_GREGS_R8(%rdi)
movq %r9, MCONTEXT_GREGS_R9(%rdi)
movq %rdi, MCONTEXT_GREGS_RDI(%rdi)
movq %rsi, MCONTEXT_GREGS_RSI(%rdi)
movq %rdx, MCONTEXT_GREGS_RDX(%rdi)
movq %rax, MCONTEXT_GREGS_RAX(%rdi)
movq %rcx, MCONTEXT_GREGS_RCX(%rdi)
/* Save fp state (not needed, except for setcontext not
restoring garbage). */
leaq MCONTEXT_FPREGS_MEM(%rdi),%r8
movq %r8, MCONTEXT_FPREGS_PTR(%rdi)
fnstenv (%r8)
stmxcsr FPREGS_OFFSET_MXCSR(%r8)
leaq 8(%rsp), %rax /* exclude this call. */
movq %rax, MCONTEXT_GREGS_RSP(%rdi)
movq 0(%rsp), %rax
movq %rax, MCONTEXT_GREGS_RIP(%rdi)
/* Save signal mask: sigprocmask(SIGBLOCK, NULL, &uc->uc_sigmask) */
leaq UCONTEXT_SIGMASK_OFFSET(%rdi), %rdx // arg3
xorq %rsi, %rsi // arg2 NULL
xorq %rdi, %rdi // arg1 SIGBLOCK == 0
call sigprocmask@PLT
/* Always return 0 for success, even if sigprocmask failed. */
xorl %eax, %eax
ret
.cfi_endproc
.size breakpad_getcontext, . - breakpad_getcontext
#else
#error "This file has not been ported for your CPU!"
#endif