aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordigit@chromium.org <digit@chromium.org@4c0a9323-5329-0410-9bdc-e9ce6186880e>2012-08-31 18:38:29 +0000
committerdigit@chromium.org <digit@chromium.org@4c0a9323-5329-0410-9bdc-e9ce6186880e>2012-08-31 18:38:29 +0000
commit7e3c538af1bdee7f5a4c04e11715488f1d4efd15 (patch)
treef1e36060e1fe50ce09eba0855d225478e623a8d8 /src
parentFix 'make check' for Android (diff)
downloadbreakpad-7e3c538af1bdee7f5a4c04e11715488f1d4efd15.tar.xz
Add custom getcontext() implementation for Android.
This adds a minimalistic implementation of getcontext() for Android/ARM and Android/x86. The provided code is in assembly and only implements the bare minimum required by Breakpad to get the current processor state. Note that: - The FPU state is not saved to the ucontext_t on ARM. (that's actually the main difference with a normal getcontext() implementation). This is normal. On Linux/ARM, such state must be obtained with PTRACE_GETVFPREGS instead. This will be implemented in a future patch. - On x86, only the 'regular' FPU state is saved, to mimic the GLibc/i386 implementation. The state of SSE/SSE2/etc registers is not part of the upstream getcontext() implementation. Review URL: https://breakpad.appspot.com/444002 git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@1024 4c0a9323-5329-0410-9bdc-e9ce6186880e
Diffstat (limited to 'src')
-rw-r--r--src/client/linux/handler/exception_handler.cc12
-rw-r--r--src/common/android/breakpad_getcontext.S145
-rw-r--r--src/common/android/breakpad_getcontext_unittest.cc76
-rw-r--r--src/common/android/include/sys/ucontext.h14
-rw-r--r--src/common/android/include/ucontext.h25
-rw-r--r--src/common/android/ucontext_constants.h85
6 files changed, 342 insertions, 15 deletions
diff --git a/src/client/linux/handler/exception_handler.cc b/src/client/linux/handler/exception_handler.cc
index 4a5932c1..27ac1544 100644
--- a/src/client/linux/handler/exception_handler.cc
+++ b/src/client/linux/handler/exception_handler.cc
@@ -324,16 +324,11 @@ bool ExceptionHandler::HandleSignal(int sig, siginfo_t* info, void* uc) {
// This is a public interface to HandleSignal that allows the client to
// generate a crash dump. This function may run in a compromised context.
bool ExceptionHandler::SimulateSignalDelivery(int sig) {
-#ifdef __ANDROID__
- // Android doesn't provide getcontext().
- return false;
-#else
siginfo_t siginfo;
my_memset(&siginfo, 0, sizeof(siginfo_t));
struct ucontext context;
getcontext(&context);
return HandleSignal(sig, &siginfo, &context);
-#endif
}
// This function may run in a compromised context: see the top of the file.
@@ -457,7 +452,6 @@ bool ExceptionHandler::WriteMinidump(const string& dump_path,
}
bool ExceptionHandler::WriteMinidump() {
-#if !defined(__ARM_EABI__) && !defined(__ANDROID__)
if (!IsOutOfProcess() && !minidump_descriptor_.IsFD()) {
// Update the path of the minidump so that this can be called multiple times
// and new files are created for each minidump. This is done before the
@@ -478,14 +472,14 @@ bool ExceptionHandler::WriteMinidump() {
int getcontext_result = getcontext(&context.context);
if (getcontext_result)
return false;
+#if !defined(__ARM_EABI__)
+ // FPU state is not part of ARM EABI ucontext_t.
memcpy(&context.float_state, context.context.uc_mcontext.fpregs,
sizeof(context.float_state));
+#endif
context.tid = sys_gettid();
return GenerateDump(&context);
-#else
- return false;
-#endif // !defined(__ARM_EABI__) && !defined(__ANDROID__)
}
void ExceptionHandler::AddMappingInfo(const string& name,
diff --git a/src/common/android/breakpad_getcontext.S b/src/common/android/breakpad_getcontext.S
new file mode 100644
index 00000000..13ccd46b
--- /dev/null
+++ b/src/common/android/breakpad_getcontext.S
@@ -0,0 +1,145 @@
+// 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 on Android.
+
+#include "common/android/ucontext_constants.h"
+
+/* int getcontext (ucontext_t *ucp) */
+
+#ifdef __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 explicitely. */
+ /* - 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 accross 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(__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
+
+#else
+#error "This file has not been ported for your CPU!"
+#endif
diff --git a/src/common/android/breakpad_getcontext_unittest.cc b/src/common/android/breakpad_getcontext_unittest.cc
new file mode 100644
index 00000000..004390c3
--- /dev/null
+++ b/src/common/android/breakpad_getcontext_unittest.cc
@@ -0,0 +1,76 @@
+// 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.
+
+#include <sys/ucontext.h>
+
+#include "breakpad_googletest_includes.h"
+#include "common/android/ucontext_constants.h"
+
+TEST(AndroidUContext, GRegsOffset) {
+#ifdef __arm__
+ // There is no gregs[] array on ARM, so compare to the offset of
+ // first register fields, since they're stored in order.
+ ASSERT_EQ(MCONTEXT_GREGS_OFFSET, offsetof(ucontext_t,uc_mcontext.arm_r0));
+#elif defined(__i386__)
+ ASSERT_EQ(MCONTEXT_GREGS_OFFSET, offsetof(ucontext_t,uc_mcontext.gregs));
+#define CHECK_REG(x) \
+ ASSERT_EQ(MCONTEXT_##x##_OFFSET, \
+ offsetof(ucontext_t,uc_mcontext.gregs[REG_##x]))
+ CHECK_REG(GS);
+ CHECK_REG(FS);
+ CHECK_REG(ES);
+ CHECK_REG(DS);
+ CHECK_REG(EDI);
+ CHECK_REG(ESI);
+ CHECK_REG(EBP);
+ CHECK_REG(ESP);
+ CHECK_REG(EBX);
+ CHECK_REG(EDX);
+ CHECK_REG(ECX);
+ CHECK_REG(EAX);
+ CHECK_REG(TRAPNO);
+ CHECK_REG(ERR);
+ CHECK_REG(EIP);
+ CHECK_REG(CS);
+ CHECK_REG(EFL);
+ CHECK_REG(UESP);
+ CHECK_REG(SS);
+
+ ASSERT_EQ(UCONTEXT_FPREGS_OFFSET, offsetof(ucontext_t,uc_mcontext.fpregs));
+
+ ASSERT_EQ(UCONTEXT_FPREGS_MEM_OFFSET,
+ offsetof(ucontext_t,__fpregs_mem));
+#else
+ ASSERT_EQ(MCONTEXT_GREGS_OFFSET, offsetof(ucontext_t,uc_mcontext.gregs));
+#endif
+}
+
+TEST(AndroidUContext, SigmakOffset) {
+ ASSERT_EQ(UCONTEXT_SIGMASK_OFFSET, offsetof(ucontext_t,uc_sigmask));
+}
diff --git a/src/common/android/include/sys/ucontext.h b/src/common/android/include/sys/ucontext.h
index 7f0eb5a3..4a4e77c1 100644
--- a/src/common/android/include/sys/ucontext.h
+++ b/src/common/android/include/sys/ucontext.h
@@ -50,11 +50,15 @@ extern "C" {
#include <asm/sigcontext.h>
typedef struct sigcontext mcontext_t;
+// The ARM kernel uses a 64-bit signal mask.
+typedef uint32_t kernel_sigmask_t[2];
+
typedef struct ucontext {
uint32_t uc_flags;
struct ucontext* uc_link;
stack_t uc_stack;
mcontext_t uc_mcontext;
+ kernel_sigmask_t uc_sigmask;
// Other fields are not used by Google Breakpad. Don't define them.
} ucontext_t;
@@ -110,12 +114,16 @@ enum {
REG_SS,
};
+// The i386 kernel uses a 64-bit signal mask.
+typedef uint32_t kernel_sigmask_t[2];
+
typedef struct ucontext {
uint32_t uc_flags;
struct ucontext* uc_link;
stack_t uc_stack;
mcontext_t uc_mcontext;
- // Other fields are not used by Google Breakpad. Don't define them.
+ kernel_sigmask_t uc_sigmask;
+ struct _libc_fpstate __fpregs_mem;
} ucontext_t;
#elif defined(__mips__)
@@ -142,11 +150,15 @@ typedef struct {
uint32_t lo3;
} mcontext_t;
+// The MIPS kernel uses a 128-bit signal mask.
+typedef uint32_t kernel_sigmask_t[4];
+
typedef struct ucontext {
uint32_t uc_flags;
struct ucontext* uc_link;
stack_t uc_stack;
mcontext_t uc_mcontext;
+ kernel_sigmask_t uc_sigmask;
// Other fields are not used by Google Breakpad. Don't define them.
} ucontext_t;
diff --git a/src/common/android/include/ucontext.h b/src/common/android/include/ucontext.h
index 6608e063..29db8ade 100644
--- a/src/common/android/include/ucontext.h
+++ b/src/common/android/include/ucontext.h
@@ -30,12 +30,27 @@
#ifndef GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_UCONTEXT_H
#define GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_UCONTEXT_H
+#include <sys/cdefs.h>
+
+#ifdef __BIONIC_UCONTEXT_H
+#include <ucontext.h>
+#else
+
#include <sys/ucontext.h>
-// TODO: Provide a portable implementation of getcontext() then
-// update ExceptionHandler::WriteMinidump() in
-// src/client/linux/handler/exception_handler.cc to use it.
-//
-// extern int getcontext(ucontext_t* ucp);
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+// Provided by src/android/common/breakpad_getcontext.S
+int breakpad_getcontext(ucontext_t* ucp);
+
+#define getcontext(x) breakpad_getcontext(x)
+
+#ifdef __cplusplus
+} // extern "C"
+#endif // __cplusplus
+
+#endif // __BIONIC_UCONTEXT_H
#endif // GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_UCONTEXT_H
diff --git a/src/common/android/ucontext_constants.h b/src/common/android/ucontext_constants.h
new file mode 100644
index 00000000..9c7a6971
--- /dev/null
+++ b/src/common/android/ucontext_constants.h
@@ -0,0 +1,85 @@
+// 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.
+
+// This header can be included either from a C, C++ or Assembly file.
+// Its purpose is to contain constants that must match the offsets of
+// various fields in ucontext_t.
+//
+// They should match the definitions from
+// src/common/android/include/sys/ucontext.h
+//
+// Used by src/common/android/breakpad_getcontext.S
+// Tested by src/common/android/testing/breakpad_getcontext_unittest.cc
+
+#ifndef GOOGLEBREAKPAD_COMMON_ANDROID_UCONTEXT_CONSTANTS_H
+#define GOOGLEBREAKPAD_COMMON_ANDROID_UCONTEXT_CONSTANTS_H
+
+#if defined(__arm__)
+
+#define MCONTEXT_GREGS_OFFSET 32
+#define UCONTEXT_SIGMASK_OFFSET 104
+
+#elif defined(__i386__)
+
+#define MCONTEXT_GREGS_OFFSET 20
+#define MCONTEXT_GS_OFFSET (MCONTEXT_GREGS_OFFSET + 0*4)
+#define MCONTEXT_FS_OFFSET (MCONTEXT_GREGS_OFFSET + 1*4)
+#define MCONTEXT_ES_OFFSET (MCONTEXT_GREGS_OFFSET + 2*4)
+#define MCONTEXT_DS_OFFSET (MCONTEXT_GREGS_OFFSET + 3*4)
+#define MCONTEXT_EDI_OFFSET (MCONTEXT_GREGS_OFFSET + 4*4)
+#define MCONTEXT_ESI_OFFSET (MCONTEXT_GREGS_OFFSET + 5*4)
+#define MCONTEXT_EBP_OFFSET (MCONTEXT_GREGS_OFFSET + 6*4)
+#define MCONTEXT_ESP_OFFSET (MCONTEXT_GREGS_OFFSET + 7*4)
+#define MCONTEXT_EBX_OFFSET (MCONTEXT_GREGS_OFFSET + 8*4)
+#define MCONTEXT_EDX_OFFSET (MCONTEXT_GREGS_OFFSET + 9*4)
+#define MCONTEXT_ECX_OFFSET (MCONTEXT_GREGS_OFFSET + 10*4)
+#define MCONTEXT_EAX_OFFSET (MCONTEXT_GREGS_OFFSET + 11*4)
+#define MCONTEXT_TRAPNO_OFFSET (MCONTEXT_GREGS_OFFSET + 12*4)
+#define MCONTEXT_ERR_OFFSET (MCONTEXT_GREGS_OFFSET + 13*4)
+#define MCONTEXT_EIP_OFFSET (MCONTEXT_GREGS_OFFSET + 14*4)
+#define MCONTEXT_CS_OFFSET (MCONTEXT_GREGS_OFFSET + 15*4)
+#define MCONTEXT_EFL_OFFSET (MCONTEXT_GREGS_OFFSET + 16*4)
+#define MCONTEXT_UESP_OFFSET (MCONTEXT_GREGS_OFFSET + 17*4)
+#define MCONTEXT_SS_OFFSET (MCONTEXT_GREGS_OFFSET + 18*4)
+
+#define UCONTEXT_SIGMASK_OFFSET 108
+
+#define UCONTEXT_FPREGS_OFFSET 96
+#define UCONTEXT_FPREGS_MEM_OFFSET 116
+
+#elif defined(__mips__)
+
+#define MCONTEXT_GREGS_OFFSET 0
+#define UCONTEXT_SIGMASK_OFFSET 0
+
+#else
+#error "This header has not been ported for your CPU"
+#endif
+
+#endif // GOOGLEBREAKPAD_COMMON_ANDROID_UCONTEXT_CONSTANTS_H