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
This commit is contained in:
rmcilroy@chromium.org 2014-04-11 16:09:12 +00:00
parent a2245d6744
commit 6594ac922c
5 changed files with 275 additions and 0 deletions

View file

@ -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 <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!"

View file

@ -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 <asm/sigcontext.h>
#endif
#include <sys/ucontext.h>
#include "breakpad_googletest_includes.h"
#include "common/android/ucontext_constants.h"
template <int left, int right>
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<left, right> 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<size_t>(MCONTEXT_FPC_CSR),
offsetof(ucontext_t,uc_mcontext.fpc_csr));
#elif defined(__x86_64__)
COMPILE_ASSERT_EQ(static_cast<size_t>(MCONTEXT_GREGS_OFFSET),
offsetof(ucontext_t,uc_mcontext.gregs),
mcontext_gregs_offset);
#define CHECK_REG(x) \
COMPILE_ASSERT_EQ(static_cast<size_t>(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<size_t>(MCONTEXT_GREGS_OFFSET),
offsetof(ucontext_t,uc_mcontext.gregs));

View file

@ -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

View file

@ -143,6 +143,28 @@ struct user_fpregs_struct {
unsigned int fir;
};
#elif defined(__x86_64__)
#include <sys/types.h>
#include_next <sys/user.h>
// 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

View file

@ -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