// 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 #include #if _MIPS_SIM == _ABIO32 #include #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 // 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 Copyright (C) 2010 Konstantin Belousov 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