From 6594ac922cc1937a237b6ed9ef05b7b952bcd82e Mon Sep 17 00:00:00 2001 From: "rmcilroy@chromium.org" Date: Fri, 11 Apr 2014 16:09:12 +0000 Subject: Add x64 version of getcontext. Assembly code is derived in part from code in libunwind. Code tested on desktop linux (Android testing pending emulation support). BUG=346626 R=dannyb@google.com, thestig@chromium.org Review URL: https://breakpad.appspot.com/1454002 git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@1311 4c0a9323-5329-0410-9bdc-e9ce6186880e --- src/common/android/breakpad_getcontext.S | 81 ++++++++++++++++++++++ src/common/android/breakpad_getcontext_unittest.cc | 79 +++++++++++++++++++++ src/common/android/include/sys/ucontext.h | 67 ++++++++++++++++++ src/common/android/include/sys/user.h | 22 ++++++ src/common/android/ucontext_constants.h | 26 +++++++ 5 files changed, 275 insertions(+) (limited to 'src') diff --git a/src/common/android/breakpad_getcontext.S b/src/common/android/breakpad_getcontext.S index 86ef8daf..79fe88df 100644 --- a/src/common/android/breakpad_getcontext.S +++ b/src/common/android/breakpad_getcontext.S @@ -305,6 +305,87 @@ NESTED (breakpad_getcontext, FRAME_SIZE, ra) END (breakpad_getcontext) +#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!" diff --git a/src/common/android/breakpad_getcontext_unittest.cc b/src/common/android/breakpad_getcontext_unittest.cc index 14d7927a..2c550bf2 100644 --- a/src/common/android/breakpad_getcontext_unittest.cc +++ b/src/common/android/breakpad_getcontext_unittest.cc @@ -27,11 +27,26 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#if defined(__x86_64__) +#include +#endif + #include #include "breakpad_googletest_includes.h" #include "common/android/ucontext_constants.h" +template +struct CompileAssertEquals { + // a compilation error here indicates left and right are not equal. + char left_too_large[right - left]; + // a compilation error here indicates left and right are not equal. + char right_too_large[left - right]; +}; + +#define COMPILE_ASSERT_EQ(left, right, tag) \ + CompileAssertEquals tag; + TEST(AndroidUContext, GRegsOffset) { #if defined(__arm__) // There is no gregs[] array on ARM, so compare to the offset of @@ -95,6 +110,70 @@ TEST(AndroidUContext, GRegsOffset) { ASSERT_EQ(static_cast(MCONTEXT_FPC_CSR), offsetof(ucontext_t,uc_mcontext.fpc_csr)); +#elif defined(__x86_64__) + + COMPILE_ASSERT_EQ(static_cast(MCONTEXT_GREGS_OFFSET), + offsetof(ucontext_t,uc_mcontext.gregs), + mcontext_gregs_offset); +#define CHECK_REG(x) \ + COMPILE_ASSERT_EQ(static_cast(MCONTEXT_GREGS_##x), \ + offsetof(ucontext_t,uc_mcontext.gregs[REG_##x]), reg_##x) + CHECK_REG(R8); + CHECK_REG(R9); + CHECK_REG(R10); + CHECK_REG(R11); + CHECK_REG(R12); + CHECK_REG(R13); + CHECK_REG(R14); + CHECK_REG(R15); + CHECK_REG(RDI); + CHECK_REG(RSI); + CHECK_REG(RBP); + CHECK_REG(RBX); + CHECK_REG(RDX); + CHECK_REG(RAX); + CHECK_REG(RCX); + CHECK_REG(RSP); + CHECK_REG(RIP); + + // sigcontext is an analog to mcontext_t. The layout should be the same. + COMPILE_ASSERT_EQ(offsetof(mcontext_t,fpregs), + offsetof(sigcontext,fpstate), sigcontext_fpstate); + // Check that _fpstate from asm/sigcontext.h is essentially the same + // as _libc_fpstate. + COMPILE_ASSERT_EQ(sizeof(_libc_fpstate), sizeof(_fpstate), + sigcontext_fpstate_size); + COMPILE_ASSERT_EQ(offsetof(_libc_fpstate,cwd),offsetof(_fpstate,cwd), + sigcontext_fpstate_cwd); + COMPILE_ASSERT_EQ(offsetof(_libc_fpstate,swd),offsetof(_fpstate,swd), + sigcontext_fpstate_swd); + COMPILE_ASSERT_EQ(offsetof(_libc_fpstate,ftw),offsetof(_fpstate,twd), + sigcontext_fpstate_twd); + COMPILE_ASSERT_EQ(offsetof(_libc_fpstate,fop),offsetof(_fpstate,fop), + sigcontext_fpstate_fop); + COMPILE_ASSERT_EQ(offsetof(_libc_fpstate,rip),offsetof(_fpstate,rip), + sigcontext_fpstate_rip); + COMPILE_ASSERT_EQ(offsetof(_libc_fpstate,rdp),offsetof(_fpstate,rdp), + sigcontext_fpstate_rdp); + COMPILE_ASSERT_EQ(offsetof(_libc_fpstate,mxcsr),offsetof(_fpstate,mxcsr), + sigcontext_fpstate_mxcsr); + COMPILE_ASSERT_EQ(offsetof(_libc_fpstate,mxcr_mask), + offsetof(_fpstate,mxcsr_mask), + sigcontext_fpstate_mxcsr_mask); + COMPILE_ASSERT_EQ(offsetof(_libc_fpstate,_st), offsetof(_fpstate,st_space), + sigcontext_fpstate_stspace); + COMPILE_ASSERT_EQ(offsetof(_libc_fpstate,_xmm), offsetof(_fpstate,xmm_space), + sigcontext_fpstate_xmm_space); + + COMPILE_ASSERT_EQ(MCONTEXT_FPREGS_PTR, + offsetof(ucontext_t,uc_mcontext.fpregs), + mcontext_fpregs_ptr); + COMPILE_ASSERT_EQ(MCONTEXT_FPREGS_MEM, offsetof(ucontext_t,__fpregs_mem), + mcontext_fpregs_mem); + COMPILE_ASSERT_EQ(FPREGS_OFFSET_MXCSR, offsetof(_libc_fpstate,mxcsr), + fpregs_offset_mxcsr); + COMPILE_ASSERT_EQ(UCONTEXT_SIGMASK_OFFSET, offsetof(ucontext_t, uc_sigmask), + ucontext_sigmask); #else ASSERT_EQ(static_cast(MCONTEXT_GREGS_OFFSET), offsetof(ucontext_t,uc_mcontext.gregs)); diff --git a/src/common/android/include/sys/ucontext.h b/src/common/android/include/sys/ucontext.h index bfc22c44..df7019c4 100644 --- a/src/common/android/include/sys/ucontext.h +++ b/src/common/android/include/sys/ucontext.h @@ -175,6 +175,73 @@ typedef struct ucontext { // Other fields are not used by Google Breakpad. Don't define them. } ucontext_t; +#elif defined(__x86_64__) +enum { + REG_R8 = 0, + REG_R9, + REG_R10, + REG_R11, + REG_R12, + REG_R13, + REG_R14, + REG_R15, + REG_RDI, + REG_RSI, + REG_RBP, + REG_RBX, + REG_RDX, + REG_RAX, + REG_RCX, + REG_RSP, + REG_RIP, + REG_EFL, + REG_CSGSFS, + REG_ERR, + REG_TRAPNO, + REG_OLDMASK, + REG_CR2, + NGREG +}; + +// This struct is essentially the same as _fpstate in asm/sigcontext.h +// except that the individual field names are chosen here to match the +// ones used in breakpad for other x86_64 platforms. +struct _libc_fpstate { + /* 64-bit FXSAVE format. */ + uint16_t cwd; + uint16_t swd; + uint16_t ftw; + uint16_t fop; + uint64_t rip; + uint64_t rdp; + uint32_t mxcsr; + uint32_t mxcr_mask; + uint32_t _st[32]; // 128 bytes for the ST/MM registers 0-7 + uint32_t _xmm[64]; // 256 bytes for the XMM registers 0-7 + uint32_t padding[24]; // 96 bytes +}; + +typedef long greg_t; +typedef greg_t gregset_t[NGREG]; + +typedef struct _libc_fpstate* fpregset_t; + +typedef struct { + gregset_t gregs; + fpregset_t fpregs; + uint64_t __reserved1[8]; +} mcontext_t; + +typedef struct ucontext { + unsigned long uc_flags; + struct ucontext* uc_link; + stack_t uc_stack; + mcontext_t uc_mcontext; + sigset_t uc_sigmask; + uint64_t __padding[15]; + _libc_fpstate __fpregs_mem; +} ucontext_t; + #else # error "Unsupported Android CPU ABI!" #endif diff --git a/src/common/android/include/sys/user.h b/src/common/android/include/sys/user.h index 257cd803..7cafd2c9 100644 --- a/src/common/android/include/sys/user.h +++ b/src/common/android/include/sys/user.h @@ -143,6 +143,28 @@ struct user_fpregs_struct { unsigned int fir; }; +#elif defined(__x86_64__) +#include +#include_next + +// This struct is essentially the same as user_i387_struct in sys/user.h +// except that the struct name and individual field names are chosen here +// to match the ones used in breakpad for other x86_64 platforms. + +struct user_fpregs_struct { + __u16 cwd; + __u16 swd; + __u16 ftw; + __u16 fop; + __u64 rip; + __u64 rdp; + __u32 mxcsr; + __u32 mxcr_mask; + __u32 st_space[32]; /* 8*16 bytes for each FP-reg = 128 bytes */ + __u32 xmm_space[64]; /* 16*16 bytes for each XMM-reg = 256 bytes */ + __u32 padding[24]; +}; + #else # error "Unsupported Android CPU ABI" #endif diff --git a/src/common/android/ucontext_constants.h b/src/common/android/ucontext_constants.h index 251718c3..92871b43 100644 --- a/src/common/android/ucontext_constants.h +++ b/src/common/android/ucontext_constants.h @@ -103,6 +103,32 @@ #define MCONTEXT_FPC_CSR 556 #define UCONTEXT_SIGMASK_OFFSET 616 +#elif defined(__x86_64__) + +#define MCONTEXT_GREGS_OFFSET 40 +#define UCONTEXT_SIGMASK_OFFSET 296 + +#define MCONTEXT_GREGS_R8 40 +#define MCONTEXT_GREGS_R9 48 +#define MCONTEXT_GREGS_R10 56 +#define MCONTEXT_GREGS_R11 64 +#define MCONTEXT_GREGS_R12 72 +#define MCONTEXT_GREGS_R13 80 +#define MCONTEXT_GREGS_R14 88 +#define MCONTEXT_GREGS_R15 96 +#define MCONTEXT_GREGS_RDI 104 +#define MCONTEXT_GREGS_RSI 112 +#define MCONTEXT_GREGS_RBP 120 +#define MCONTEXT_GREGS_RBX 128 +#define MCONTEXT_GREGS_RDX 136 +#define MCONTEXT_GREGS_RAX 144 +#define MCONTEXT_GREGS_RCX 152 +#define MCONTEXT_GREGS_RSP 160 +#define MCONTEXT_GREGS_RIP 168 +#define MCONTEXT_FPREGS_PTR 224 +#define MCONTEXT_FPREGS_MEM 424 +#define FPREGS_OFFSET_MXCSR 24 + #else #error "This header has not been ported for your CPU" #endif -- cgit v1.2.1