summaryrefslogtreecommitdiffstats
path: root/debuggerd
diff options
context:
space:
mode:
Diffstat (limited to 'debuggerd')
-rw-r--r--debuggerd/Android.mk13
-rw-r--r--debuggerd/crasher.c30
-rw-r--r--debuggerd/debuggerd.cpp165
-rw-r--r--debuggerd/elf_utils.cpp121
-rw-r--r--debuggerd/elf_utils.h27
-rw-r--r--debuggerd/mips/machine.cpp16
-rw-r--r--debuggerd/mips64/crashglue.S48
-rw-r--r--debuggerd/mips64/machine.cpp100
-rw-r--r--debuggerd/tombstone.cpp181
-rw-r--r--debuggerd/utility.cpp8
-rw-r--r--debuggerd/utility.h4
11 files changed, 592 insertions, 121 deletions
diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk
index c33b263..dd53296 100644
--- a/debuggerd/Android.mk
+++ b/debuggerd/Android.mk
@@ -5,6 +5,7 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
backtrace.cpp \
debuggerd.cpp \
+ elf_utils.cpp \
getevent.cpp \
tombstone.cpp \
utility.cpp \
@@ -12,7 +13,7 @@ LOCAL_SRC_FILES:= \
LOCAL_SRC_FILES_arm := arm/machine.cpp
LOCAL_SRC_FILES_arm64 := arm64/machine.cpp
LOCAL_SRC_FILES_mips := mips/machine.cpp
-LOCAL_SRC_FILES_mips64 := mips/machine.cpp
+LOCAL_SRC_FILES_mips64 := mips64/machine.cpp
LOCAL_SRC_FILES_x86 := x86/machine.cpp
LOCAL_SRC_FILES_x86_64 := x86_64/machine.cpp
@@ -22,19 +23,23 @@ LOCAL_CPPFLAGS := \
-Wunused \
-Werror \
+ifeq ($(TARGET_IS_64_BIT),true)
+LOCAL_CPPFLAGS += -DTARGET_IS_64_BIT
+endif
+
LOCAL_SHARED_LIBRARIES := \
libbacktrace \
+ libbase \
libcutils \
liblog \
libselinux \
-include external/stlport/libstlport.mk
+LOCAL_CLANG := true
LOCAL_MODULE := debuggerd
LOCAL_MODULE_STEM_32 := debuggerd
LOCAL_MODULE_STEM_64 := debuggerd64
LOCAL_MULTILIB := both
-LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
include $(BUILD_EXECUTABLE)
@@ -45,7 +50,7 @@ LOCAL_SRC_FILES := crasher.c
LOCAL_SRC_FILES_arm := arm/crashglue.S
LOCAL_SRC_FILES_arm64 := arm64/crashglue.S
LOCAL_SRC_FILES_mips := mips/crashglue.S
-LOCAL_SRC_FILES_mips64 := mips/crashglue.S
+LOCAL_SRC_FILES_mips64 := mips64/crashglue.S
LOCAL_SRC_FILES_x86 := x86/crashglue.S
LOCAL_SRC_FILES_x86_64 := x86_64/crashglue.S
LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
diff --git a/debuggerd/crasher.c b/debuggerd/crasher.c
index d315ee5..af86fe9 100644
--- a/debuggerd/crasher.c
+++ b/debuggerd/crasher.c
@@ -32,16 +32,23 @@ static void maybe_abort() {
}
}
-static int smash_stack(int i __unused) {
+static char* smash_stack_dummy_buf;
+__attribute__ ((noinline)) static void smash_stack_dummy_function(volatile int* plen) {
+ smash_stack_dummy_buf[*plen] = 0;
+}
+
+// This must be marked with "__attribute__ ((noinline))", to ensure the
+// compiler generates the proper stack guards around this function.
+// Assign local array address to global variable to force stack guards.
+// Use another noinline function to corrupt the stack.
+__attribute__ ((noinline)) static int smash_stack(volatile int* plen) {
printf("crasher: deliberately corrupting stack...\n");
- // Unless there's a "big enough" buffer on the stack, gcc
- // doesn't bother inserting checks.
- char buf[8];
- // If we don't write something relatively unpredictable
- // into the buffer and then do something with it, gcc
- // optimizes everything away and just returns a constant.
- *(int*)(&buf[7]) = (uintptr_t) &buf[0];
- return *(int*)(&buf[0]);
+
+ char buf[128];
+ smash_stack_dummy_buf = buf;
+ // This should corrupt stack guards and make process abort.
+ smash_stack_dummy_function(plen);
+ return 0;
}
static void* global = 0; // So GCC doesn't optimize the tail recursion out of overflow_stack.
@@ -59,7 +66,7 @@ static void *noisy(void *x)
for(;;) {
usleep(250*1000);
write(2, &c, 1);
- if(c == 'C') *((unsigned*) 0) = 42;
+ if(c == 'C') *((volatile unsigned*) 0) = 42;
}
return NULL;
}
@@ -125,7 +132,8 @@ static int do_action(const char* arg)
} else if (!strcmp(arg, "SIGSEGV-non-null")) {
sigsegv_non_null();
} else if (!strcmp(arg, "smash-stack")) {
- return smash_stack(42);
+ volatile int len = 128;
+ return smash_stack(&len);
} else if (!strcmp(arg, "stack-overflow")) {
overflow_stack(NULL);
} else if (!strcmp(arg, "nostack")) {
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index 06c16f8..039b8ec 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -30,6 +30,8 @@
#include <sys/stat.h>
#include <sys/poll.h>
+#include <selinux/android.h>
+
#include <log/logger.h>
#include <cutils/sockets.h>
@@ -45,6 +47,14 @@
#include "tombstone.h"
#include "utility.h"
+// If the 32 bit executable is compiled on a 64 bit system,
+// use the 32 bit socket name.
+#if defined(TARGET_IS_64_BIT) && !defined(__LP64__)
+#define SOCKET_NAME DEBUGGER32_SOCKET_NAME
+#else
+#define SOCKET_NAME DEBUGGER_SOCKET_NAME
+#endif
+
struct debugger_request_t {
debugger_action_t action;
pid_t pid, tid;
@@ -124,6 +134,53 @@ static int get_process_info(pid_t tid, pid_t* out_pid, uid_t* out_uid, uid_t* ou
return fields == 7 ? 0 : -1;
}
+static int selinux_enabled;
+
+/*
+ * Corresponds with debugger_action_t enum type in
+ * include/cutils/debugger.h.
+ */
+static const char *debuggerd_perms[] = {
+ NULL, /* crash is only used on self, no check applied */
+ "dump_tombstone",
+ "dump_backtrace"
+};
+
+static bool selinux_action_allowed(int s, pid_t tid, debugger_action_t action)
+{
+ char *scon = NULL, *tcon = NULL;
+ const char *tclass = "debuggerd";
+ const char *perm;
+ bool allowed = false;
+
+ if (selinux_enabled <= 0)
+ return true;
+
+ if (action <= 0 || action >= (sizeof(debuggerd_perms)/sizeof(debuggerd_perms[0]))) {
+ ALOGE("SELinux: No permission defined for debugger action %d", action);
+ return false;
+ }
+
+ perm = debuggerd_perms[action];
+
+ if (getpeercon(s, &scon) < 0) {
+ ALOGE("Cannot get peer context from socket\n");
+ goto out;
+ }
+
+ if (getpidcon(tid, &tcon) < 0) {
+ ALOGE("Cannot get context for tid %d\n", tid);
+ goto out;
+ }
+
+ allowed = (selinux_check_access(scon, tcon, tclass, perm, NULL) == 0);
+
+out:
+ freecon(scon);
+ freecon(tcon);
+ return allowed;
+}
+
static int read_request(int fd, debugger_request_t* out_request) {
ucred cr;
socklen_t len = sizeof(cr);
@@ -158,7 +215,7 @@ static int read_request(int fd, debugger_request_t* out_request) {
return -1;
}
- out_request->action = msg.action;
+ out_request->action = static_cast<debugger_action_t>(msg.action);
out_request->tid = msg.tid;
out_request->pid = cr.pid;
out_request->uid = cr.uid;
@@ -186,6 +243,9 @@ static int read_request(int fd, debugger_request_t* out_request) {
ALOGE("tid %d does not exist. ignoring explicit dump request\n", out_request->tid);
return -1;
}
+
+ if (!selinux_action_allowed(fd, out_request->tid, out_request->action))
+ return -1;
} else {
// No one else is allowed to dump arbitrary processes.
return -1;
@@ -203,6 +263,85 @@ static bool should_attach_gdb(debugger_request_t* request) {
return false;
}
+#if defined(__LP64__)
+static bool is32bit(pid_t tid) {
+ char* exeline;
+ if (asprintf(&exeline, "/proc/%d/exe", tid) == -1) {
+ return false;
+ }
+ int fd = TEMP_FAILURE_RETRY(open(exeline, O_RDONLY | O_CLOEXEC));
+ int saved_errno = errno;
+ free(exeline);
+ if (fd == -1) {
+ ALOGW("Failed to open /proc/%d/exe %s", tid, strerror(saved_errno));
+ return false;
+ }
+
+ char ehdr[EI_NIDENT];
+ ssize_t bytes = TEMP_FAILURE_RETRY(read(fd, &ehdr, sizeof(ehdr)));
+ TEMP_FAILURE_RETRY(close(fd));
+ if (bytes != (ssize_t) sizeof(ehdr) || memcmp(ELFMAG, ehdr, SELFMAG) != 0) {
+ return false;
+ }
+ if (ehdr[EI_CLASS] == ELFCLASS32) {
+ return true;
+ }
+ return false;
+}
+
+static void redirect_to_32(int fd, debugger_request_t* request) {
+ debugger_msg_t msg;
+ memset(&msg, 0, sizeof(msg));
+ msg.tid = request->tid;
+ msg.action = request->action;
+
+ int sock_fd = socket_local_client(DEBUGGER32_SOCKET_NAME, ANDROID_SOCKET_NAMESPACE_ABSTRACT,
+ SOCK_STREAM | SOCK_CLOEXEC);
+ if (sock_fd < 0) {
+ ALOGE("Failed to connect to debuggerd32: %s", strerror(errno));
+ return;
+ }
+
+ if (TEMP_FAILURE_RETRY(write(sock_fd, &msg, sizeof(msg))) != (ssize_t) sizeof(msg)) {
+ ALOGE("Failed to write request to debuggerd32 socket: %s", strerror(errno));
+ TEMP_FAILURE_RETRY(close(sock_fd));
+ return;
+ }
+
+ char ack;
+ if (TEMP_FAILURE_RETRY(read(sock_fd, &ack, 1)) == -1) {
+ ALOGE("Failed to read ack from debuggerd32 socket: %s", strerror(errno));
+ TEMP_FAILURE_RETRY(close(sock_fd));
+ return;
+ }
+
+ char buffer[1024];
+ ssize_t bytes_read;
+ while ((bytes_read = TEMP_FAILURE_RETRY(read(sock_fd, buffer, sizeof(buffer)))) > 0) {
+ ssize_t bytes_to_send = bytes_read;
+ ssize_t bytes_written;
+ do {
+ bytes_written = TEMP_FAILURE_RETRY(write(fd, buffer + bytes_read - bytes_to_send,
+ bytes_to_send));
+ if (bytes_written == -1) {
+ if (errno == EAGAIN) {
+ // Retry the write.
+ continue;
+ }
+ ALOGE("Error while writing data to fd: %s", strerror(errno));
+ break;
+ }
+ bytes_to_send -= bytes_written;
+ } while (bytes_written != 0 && bytes_to_send > 0);
+ if (bytes_to_send != 0) {
+ ALOGE("Failed to write all data to fd: read %zd, sent %zd", bytes_read, bytes_to_send);
+ break;
+ }
+ }
+ TEMP_FAILURE_RETRY(close(sock_fd));
+}
+#endif
+
static void handle_request(int fd) {
ALOGV("handle_request(%d)\n", fd);
@@ -213,6 +352,24 @@ static void handle_request(int fd) {
ALOGV("BOOM: pid=%d uid=%d gid=%d tid=%d\n",
request.pid, request.uid, request.gid, request.tid);
+#if defined(__LP64__)
+ // On 64 bit systems, requests to dump 32 bit and 64 bit tids come
+ // to the 64 bit debuggerd. If the process is a 32 bit executable,
+ // redirect the request to the 32 bit debuggerd.
+ if (is32bit(request.tid)) {
+ // Only dump backtrace and dump tombstone requests can be redirected.
+ if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE
+ || request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
+ redirect_to_32(fd, &request);
+ } else {
+ ALOGE("debuggerd: Not allowed to redirect action %d to 32 bit debuggerd\n",
+ request.action);
+ }
+ TEMP_FAILURE_RETRY(close(fd));
+ return;
+ }
+#endif
+
// At this point, the thread that made the request is blocked in
// a read() call. If the thread has crashed, then this gives us
// time to PTRACE_ATTACH to it before it has a chance to really fault.
@@ -376,7 +533,7 @@ static int do_server() {
act.sa_flags = SA_NOCLDWAIT;
sigaction(SIGCHLD, &act, 0);
- int s = socket_local_server(DEBUGGER_SOCKET_NAME, ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
+ int s = socket_local_server(SOCKET_NAME, ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
if (s < 0)
return 1;
fcntl(s, F_SETFD, FD_CLOEXEC);
@@ -430,7 +587,11 @@ static void usage() {
}
int main(int argc, char** argv) {
+ union selinux_callback cb;
if (argc == 1) {
+ selinux_enabled = is_selinux_enabled();
+ cb.func_log = selinux_log_callback;
+ selinux_set_callback(SELINUX_CB_LOG, cb);
return do_server();
}
diff --git a/debuggerd/elf_utils.cpp b/debuggerd/elf_utils.cpp
new file mode 100644
index 0000000..5ea03e7
--- /dev/null
+++ b/debuggerd/elf_utils.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "DEBUG"
+
+#include <elf.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <string>
+
+#include <backtrace/Backtrace.h>
+#include <base/stringprintf.h>
+#include <log/log.h>
+
+#include "elf_utils.h"
+
+#define NOTE_ALIGN(size) ((size + 3) & ~3)
+
+template <typename HdrType, typename PhdrType, typename NhdrType>
+static bool get_build_id(
+ Backtrace* backtrace, uintptr_t base_addr, uint8_t* e_ident, std::string* build_id) {
+ HdrType hdr;
+
+ memcpy(&hdr.e_ident[0], e_ident, EI_NIDENT);
+
+ // First read the rest of the header.
+ if (backtrace->Read(base_addr + EI_NIDENT, reinterpret_cast<uint8_t*>(&hdr) + EI_NIDENT,
+ sizeof(HdrType) - EI_NIDENT) != sizeof(HdrType) - EI_NIDENT) {
+ return false;
+ }
+
+ for (size_t i = 0; i < hdr.e_phnum; i++) {
+ PhdrType phdr;
+ if (backtrace->Read(base_addr + hdr.e_phoff + i * hdr.e_phentsize,
+ reinterpret_cast<uint8_t*>(&phdr), sizeof(phdr)) != sizeof(phdr)) {
+ return false;
+ }
+ // Looking for the .note.gnu.build-id note.
+ if (phdr.p_type == PT_NOTE) {
+ size_t hdr_size = phdr.p_filesz;
+ uintptr_t addr = base_addr + phdr.p_offset;
+ while (hdr_size >= sizeof(NhdrType)) {
+ NhdrType nhdr;
+ if (backtrace->Read(addr, reinterpret_cast<uint8_t*>(&nhdr), sizeof(nhdr)) != sizeof(nhdr)) {
+ return false;
+ }
+ addr += sizeof(nhdr);
+ if (nhdr.n_type == NT_GNU_BUILD_ID) {
+ // Skip the name (which is the owner and should be "GNU").
+ addr += NOTE_ALIGN(nhdr.n_namesz);
+ uint8_t build_id_data[128];
+ if (nhdr.n_namesz > sizeof(build_id_data)) {
+ ALOGE("Possible corrupted note, name size value is too large: %u",
+ nhdr.n_namesz);
+ return false;
+ }
+ if (backtrace->Read(addr, build_id_data, nhdr.n_descsz) != nhdr.n_descsz) {
+ return false;
+ }
+
+ build_id->clear();
+ for (size_t bytes = 0; bytes < nhdr.n_descsz; bytes++) {
+ *build_id += android::base::StringPrintf("%02x", build_id_data[bytes]);
+ }
+
+ return true;
+ } else {
+ // Move past the extra note data.
+ hdr_size -= sizeof(nhdr);
+ size_t skip_bytes = NOTE_ALIGN(nhdr.n_namesz) + NOTE_ALIGN(nhdr.n_descsz);
+ addr += skip_bytes;
+ if (hdr_size < skip_bytes) {
+ break;
+ }
+ hdr_size -= skip_bytes;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool elf_get_build_id(Backtrace* backtrace, uintptr_t addr, std::string* build_id) {
+ // Read and verify the elf magic number first.
+ uint8_t e_ident[EI_NIDENT];
+ if (backtrace->Read(addr, e_ident, SELFMAG) != SELFMAG) {
+ return false;
+ }
+
+ if (memcmp(e_ident, ELFMAG, SELFMAG) != 0) {
+ return false;
+ }
+
+ // Read the rest of EI_NIDENT.
+ if (backtrace->Read(addr + SELFMAG, e_ident + SELFMAG, EI_NIDENT - SELFMAG) != EI_NIDENT - SELFMAG) {
+ return false;
+ }
+
+ if (e_ident[EI_CLASS] == ELFCLASS32) {
+ return get_build_id<Elf32_Ehdr, Elf32_Phdr, Elf32_Nhdr>(backtrace, addr, e_ident, build_id);
+ } else if (e_ident[EI_CLASS] == ELFCLASS64) {
+ return get_build_id<Elf64_Ehdr, Elf64_Phdr, Elf64_Nhdr>(backtrace, addr, e_ident, build_id);
+ }
+
+ return false;
+}
diff --git a/debuggerd/elf_utils.h b/debuggerd/elf_utils.h
new file mode 100644
index 0000000..11d0a43
--- /dev/null
+++ b/debuggerd/elf_utils.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DEBUGGERD_ELF_UTILS_H
+#define _DEBUGGERD_ELF_UTILS_H
+
+#include <stdint.h>
+#include <string>
+
+class Backtrace;
+
+bool elf_get_build_id(Backtrace*, uintptr_t, std::string*);
+
+#endif // _DEBUGGERD_ELF_UTILS_H
diff --git a/debuggerd/mips/machine.cpp b/debuggerd/mips/machine.cpp
index 97834c7..1145963 100644
--- a/debuggerd/mips/machine.cpp
+++ b/debuggerd/mips/machine.cpp
@@ -29,22 +29,10 @@
#define R(x) (static_cast<unsigned int>(x))
-// The MIPS uapi ptrace.h has the wrong definition for pt_regs. PTRACE_GETREGS
-// writes 64-bit quantities even though the public struct uses 32-bit ones.
-struct pt_regs_mips_t {
- uint64_t regs[32];
- uint64_t lo;
- uint64_t hi;
- uint64_t cp0_epc;
- uint64_t cp0_badvaddr;
- uint64_t cp0_status;
- uint64_t cp0_cause;
-};
-
// If configured to do so, dump memory around *all* registers
// for the crashing thread.
void dump_memory_and_code(log_t* log, pid_t tid) {
- pt_regs_mips_t r;
+ pt_regs r;
if (ptrace(PTRACE_GETREGS, tid, 0, &r)) {
return;
}
@@ -85,7 +73,7 @@ void dump_memory_and_code(log_t* log, pid_t tid) {
}
void dump_registers(log_t* log, pid_t tid) {
- pt_regs_mips_t r;
+ pt_regs r;
if(ptrace(PTRACE_GETREGS, tid, 0, &r)) {
_LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
return;
diff --git a/debuggerd/mips64/crashglue.S b/debuggerd/mips64/crashglue.S
new file mode 100644
index 0000000..70a6641
--- /dev/null
+++ b/debuggerd/mips64/crashglue.S
@@ -0,0 +1,48 @@
+ .set noat
+
+ .globl crash1
+ .globl crashnostack
+
+crash1:
+ li $0,0xdead0000+0
+ li $1,0xdead0000+1
+ li $2,0xdead0000+2
+ li $3,0xdead0000+3
+ li $4,0xdead0000+4
+ li $5,0xdead0000+5
+ li $6,0xdead0000+6
+ li $7,0xdead0000+7
+ li $8,0xdead0000+8
+ li $9,0xdead0000+9
+ li $10,0xdead0000+10
+ li $11,0xdead0000+11
+ li $12,0xdead0000+12
+ li $13,0xdead0000+13
+ li $14,0xdead0000+14
+ li $15,0xdead0000+15
+ li $16,0xdead0000+16
+ li $17,0xdead0000+17
+ li $18,0xdead0000+18
+ li $19,0xdead0000+19
+ li $20,0xdead0000+20
+ li $21,0xdead0000+21
+ li $22,0xdead0000+22
+ li $23,0xdead0000+23
+ li $24,0xdead0000+24
+ li $25,0xdead0000+25
+ li $26,0xdead0000+26
+ li $27,0xdead0000+27
+ li $28,0xdead0000+28
+ # don't trash the stack otherwise the signal handler won't run
+ #li $29,0xdead0000+29
+ li $30,0xdead0000+30
+ li $31,0xdead0000+31
+
+ lw $zero,($0)
+ b .
+
+
+crashnostack:
+ li $sp, 0
+ lw $zero,($0)
+ b .
diff --git a/debuggerd/mips64/machine.cpp b/debuggerd/mips64/machine.cpp
new file mode 100644
index 0000000..ef9092f
--- /dev/null
+++ b/debuggerd/mips64/machine.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/ptrace.h>
+
+#include <sys/user.h>
+
+#include "../utility.h"
+#include "../machine.h"
+
+#define R(x) (static_cast<unsigned long>(x))
+
+// If configured to do so, dump memory around *all* registers
+// for the crashing thread.
+void dump_memory_and_code(log_t* log, pid_t tid) {
+ pt_regs r;
+ if (ptrace(PTRACE_GETREGS, tid, 0, &r)) {
+ return;
+ }
+
+ static const char REG_NAMES[] = "$0atv0v1a0a1a2a3a4a5a6a7t0t1t2t3s0s1s2s3s4s5s6s7t8t9k0k1gpsps8ra";
+
+ for (int reg = 0; reg < 32; reg++) {
+ // skip uninteresting registers
+ if (reg == 0 // $0
+ || reg == 26 // $k0
+ || reg == 27 // $k1
+ || reg == 31 // $ra (done below)
+ )
+ continue;
+
+ uintptr_t addr = R(r.regs[reg]);
+
+ // Don't bother if it looks like a small int or ~= null, or if
+ // it's in the kernel area.
+ if (addr < 4096 || addr >= 0x4000000000000000) {
+ continue;
+ }
+
+ _LOG(log, logtype::MEMORY, "\nmemory near %.2s:\n", &REG_NAMES[reg * 2]);
+ dump_memory(log, tid, addr);
+ }
+
+ unsigned long pc = R(r.cp0_epc);
+ unsigned long ra = R(r.regs[31]);
+
+ _LOG(log, logtype::MEMORY, "\ncode around pc:\n");
+ dump_memory(log, tid, (uintptr_t)pc);
+
+ if (pc != ra) {
+ _LOG(log, logtype::MEMORY, "\ncode around ra:\n");
+ dump_memory(log, tid, (uintptr_t)ra);
+ }
+}
+
+void dump_registers(log_t* log, pid_t tid) {
+ pt_regs r;
+ if(ptrace(PTRACE_GETREGS, tid, 0, &r)) {
+ _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+ return;
+ }
+
+ _LOG(log, logtype::REGISTERS, " zr %016lx at %016lx v0 %016lx v1 %016lx\n",
+ R(r.regs[0]), R(r.regs[1]), R(r.regs[2]), R(r.regs[3]));
+ _LOG(log, logtype::REGISTERS, " a0 %016lx a1 %016lx a2 %016lx a3 %016lx\n",
+ R(r.regs[4]), R(r.regs[5]), R(r.regs[6]), R(r.regs[7]));
+ _LOG(log, logtype::REGISTERS, " a4 %016lx a5 %016lx a6 %016lx a7 %016lx\n",
+ R(r.regs[8]), R(r.regs[9]), R(r.regs[10]), R(r.regs[11]));
+ _LOG(log, logtype::REGISTERS, " t0 %016lx t1 %016lx t2 %016lx t3 %016lx\n",
+ R(r.regs[12]), R(r.regs[13]), R(r.regs[14]), R(r.regs[15]));
+ _LOG(log, logtype::REGISTERS, " s0 %016lx s1 %016lx s2 %016lx s3 %016lx\n",
+ R(r.regs[16]), R(r.regs[17]), R(r.regs[18]), R(r.regs[19]));
+ _LOG(log, logtype::REGISTERS, " s4 %016lx s5 %016lx s6 %016lx s7 %016lx\n",
+ R(r.regs[20]), R(r.regs[21]), R(r.regs[22]), R(r.regs[23]));
+ _LOG(log, logtype::REGISTERS, " t8 %016lx t9 %016lx k0 %016lx k1 %016lx\n",
+ R(r.regs[24]), R(r.regs[25]), R(r.regs[26]), R(r.regs[27]));
+ _LOG(log, logtype::REGISTERS, " gp %016lx sp %016lx s8 %016lx ra %016lx\n",
+ R(r.regs[28]), R(r.regs[29]), R(r.regs[30]), R(r.regs[31]));
+ _LOG(log, logtype::REGISTERS, " hi %016lx lo %016lx bva %016lx epc %016lx\n",
+ R(r.hi), R(r.lo), R(r.cp0_badvaddr), R(r.cp0_epc));
+}
diff --git a/debuggerd/tombstone.cpp b/debuggerd/tombstone.cpp
index 0c1b80f..094ab48 100644
--- a/debuggerd/tombstone.cpp
+++ b/debuggerd/tombstone.cpp
@@ -16,6 +16,7 @@
#define LOG_TAG "DEBUG"
+#include <arpa/inet.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
@@ -33,6 +34,7 @@
#include <private/android_filesystem_config.h>
+#include <base/stringprintf.h>
#include <cutils/properties.h>
#include <log/log.h>
#include <log/logger.h>
@@ -45,9 +47,12 @@
#include <UniquePtr.h>
+#include <string>
+
+#include "backtrace.h"
+#include "elf_utils.h"
#include "machine.h"
#include "tombstone.h"
-#include "backtrace.h"
#define STACK_WORDS 16
@@ -233,48 +238,36 @@ static void dump_thread_info(log_t* log, pid_t pid, pid_t tid) {
static void dump_stack_segment(
Backtrace* backtrace, log_t* log, uintptr_t* sp, size_t words, int label) {
+ // Read the data all at once.
+ word_t stack_data[words];
+ size_t bytes_read = backtrace->Read(*sp, reinterpret_cast<uint8_t*>(&stack_data[0]), sizeof(word_t) * words);
+ words = bytes_read / sizeof(word_t);
+ std::string line;
for (size_t i = 0; i < words; i++) {
- word_t stack_content;
- if (!backtrace->ReadWord(*sp, &stack_content)) {
- break;
- }
-
- const backtrace_map_t* map = backtrace->FindMap(stack_content);
- const char* map_name;
- if (!map) {
- map_name = "";
+ line = " ";
+ if (i == 0 && label >= 0) {
+ // Print the label once.
+ line += android::base::StringPrintf("#%02d ", label);
} else {
- map_name = map->name.c_str();
+ line += " ";
}
- uintptr_t offset = 0;
- std::string func_name(backtrace->GetFunctionName(stack_content, &offset));
- if (!func_name.empty()) {
- if (!i && label >= 0) {
+ line += android::base::StringPrintf("%" PRIPTR " %" PRIPTR, *sp, stack_data[i]);
+
+ backtrace_map_t map;
+ backtrace->FillInMap(stack_data[i], &map);
+ if (BacktraceMap::IsValid(map) && !map.name.empty()) {
+ line += " " + map.name;
+ uintptr_t offset = 0;
+ std::string func_name(backtrace->GetFunctionName(stack_data[i], &offset));
+ if (!func_name.empty()) {
+ line += " (" + func_name;
if (offset) {
- _LOG(log, logtype::STACK, " #%02d %" PRIPTR " %" PRIPTR " %s (%s+%" PRIuPTR ")\n",
- label, *sp, stack_content, map_name, func_name.c_str(), offset);
- } else {
- _LOG(log, logtype::STACK, " #%02d %" PRIPTR " %" PRIPTR " %s (%s)\n",
- label, *sp, stack_content, map_name, func_name.c_str());
+ line += android::base::StringPrintf("+%" PRIuPTR, offset);
}
- } else {
- if (offset) {
- _LOG(log, logtype::STACK, " %" PRIPTR " %" PRIPTR " %s (%s+%" PRIuPTR ")\n",
- *sp, stack_content, map_name, func_name.c_str(), offset);
- } else {
- _LOG(log, logtype::STACK, " %" PRIPTR " %" PRIPTR " %s (%s)\n",
- *sp, stack_content, map_name, func_name.c_str());
- }
- }
- } else {
- if (!i && label >= 0) {
- _LOG(log, logtype::STACK, " #%02d %" PRIPTR " %" PRIPTR " %s\n",
- label, *sp, stack_content, map_name);
- } else {
- _LOG(log, logtype::STACK, " %" PRIPTR " %" PRIPTR " %s\n",
- *sp, stack_content, map_name);
+ line += ')';
}
}
+ _LOG(log, logtype::STACK, "%s\n", line.c_str());
*sp += sizeof(word_t);
}
@@ -325,61 +318,83 @@ static void dump_stack(Backtrace* backtrace, log_t* log) {
}
}
-static void dump_backtrace_and_stack(Backtrace* backtrace, log_t* log) {
- if (backtrace->NumFrames()) {
- _LOG(log, logtype::BACKTRACE, "\nbacktrace:\n");
- dump_backtrace_to_log(backtrace, log, " ");
-
- _LOG(log, logtype::STACK, "\nstack:\n");
- dump_stack(backtrace, log);
- }
-}
-
-static void dump_map(log_t* log, const backtrace_map_t* map, bool fault_addr) {
- _LOG(log, logtype::MAPS, "%s%" PRIPTR "-%" PRIPTR " %c%c%c %7" PRIdPTR " %s\n",
- (fault_addr? "--->" : " "), map->start, map->end - 1,
- (map->flags & PROT_READ) ? 'r' : '-', (map->flags & PROT_WRITE) ? 'w' : '-',
- (map->flags & PROT_EXEC) ? 'x' : '-',
- (map->end - map->start), map->name.c_str());
-}
-
-static void dump_nearby_maps(BacktraceMap* map, log_t* log, pid_t tid) {
+static void dump_all_maps(Backtrace* backtrace, BacktraceMap* map, log_t* log, pid_t tid) {
+ bool print_fault_address_marker = false;
+ uintptr_t addr = 0;
siginfo_t si;
memset(&si, 0, sizeof(si));
if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si)) {
- _LOG(log, logtype::MAPS, "cannot get siginfo for %d: %s\n", tid, strerror(errno));
- return;
+ _LOG(log, logtype::ERROR, "cannot get siginfo for %d: %s\n", tid, strerror(errno));
+ } else {
+ print_fault_address_marker = signal_has_si_addr(si.si_signo);
+ addr = reinterpret_cast<uintptr_t>(si.si_addr);
}
- bool has_fault_address = signal_has_si_addr(si.si_signo);
- uintptr_t addr = reinterpret_cast<uintptr_t>(si.si_addr);
-
- _LOG(log, logtype::MAPS, "\nmemory map: %s\n", has_fault_address ? "(fault address prefixed with --->)" : "");
-
- if (has_fault_address && (addr < map->begin()->start)) {
- _LOG(log, logtype::MAPS, "--->Fault address falls at %" PRIPTR " before any mapped regions\n", addr);
+ _LOG(log, logtype::MAPS, "\n");
+ if (!print_fault_address_marker) {
+ _LOG(log, logtype::MAPS, "memory map:\n");
+ } else {
+ _LOG(log, logtype::MAPS, "memory map: (fault address prefixed with --->)\n");
+ if (map->begin() != map->end() && addr < map->begin()->start) {
+ _LOG(log, logtype::MAPS, "--->Fault address falls at %" PRIPTR " before any mapped regions\n",
+ addr);
+ print_fault_address_marker = false;
+ }
}
- BacktraceMap::const_iterator prev = map->begin();
+ std::string line;
for (BacktraceMap::const_iterator it = map->begin(); it != map->end(); ++it) {
- if (addr >= (*prev).end && addr < (*it).start) {
- _LOG(log, logtype::MAPS, "--->Fault address falls at %" PRIPTR " between mapped regions\n", addr);
+ line = " ";
+ if (print_fault_address_marker) {
+ if (addr < it->start) {
+ _LOG(log, logtype::MAPS, "--->Fault address falls at %" PRIPTR " between mapped regions\n",
+ addr);
+ print_fault_address_marker = false;
+ } else if (addr >= it->start && addr < it->end) {
+ line = "--->";
+ print_fault_address_marker = false;
+ }
+ }
+ line += android::base::StringPrintf("%" PRIPTR "-%" PRIPTR " ", it->start, it->end - 1);
+ if (it->flags & PROT_READ) {
+ line += 'r';
+ } else {
+ line += '-';
+ }
+ if (it->flags & PROT_WRITE) {
+ line += 'w';
+ } else {
+ line += '-';
}
- prev = it;
- bool in_map = has_fault_address && (addr >= (*it).start) && (addr < (*it).end);
- dump_map(log, &*it, in_map);
+ if (it->flags & PROT_EXEC) {
+ line += 'x';
+ } else {
+ line += '-';
+ }
+ line += android::base::StringPrintf(" %8" PRIxPTR, it->end - it->start);
+ if (it->name.length() > 0) {
+ line += " " + it->name;
+ std::string build_id;
+ if ((it->flags & PROT_READ) && elf_get_build_id(backtrace, it->start, &build_id)) {
+ line += " (BuildId: " + build_id + ")";
+ }
+ }
+ _LOG(log, logtype::MAPS, "%s\n", line.c_str());
}
- if (has_fault_address && (addr >= (*prev).end)) {
- _LOG(log, logtype::MAPS, "--->Fault address falls at %" PRIPTR " after any mapped regions\n", addr);
+ if (print_fault_address_marker) {
+ _LOG(log, logtype::MAPS, "--->Fault address falls at %" PRIPTR " after any mapped regions\n",
+ addr);
}
}
-static void dump_thread(Backtrace* backtrace, log_t* log) {
- dump_registers(log, backtrace->Tid());
- dump_backtrace_and_stack(backtrace, log);
+static void dump_backtrace_and_stack(Backtrace* backtrace, log_t* log) {
+ if (backtrace->NumFrames()) {
+ _LOG(log, logtype::BACKTRACE, "\nbacktrace:\n");
+ dump_backtrace_to_log(backtrace, log, " ");
- dump_memory_and_code(log, backtrace->Tid());
- dump_nearby_maps(backtrace->GetMap(), log, backtrace->Tid());
+ _LOG(log, logtype::STACK, "\nstack:\n");
+ dump_stack(backtrace, log);
+ }
}
// Return true if some thread is not detached cleanly
@@ -425,9 +440,10 @@ static bool dump_sibling_thread_report(
_LOG(log, logtype::THREAD, "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n");
dump_thread_info(log, pid, new_tid);
+ dump_registers(log, new_tid);
UniquePtr<Backtrace> backtrace(Backtrace::Create(pid, new_tid, map));
if (backtrace->Unwind(0)) {
- dump_thread(backtrace.get(), log);
+ dump_backtrace_and_stack(backtrace.get(), log);
}
log->current_tid = log->crashed_tid;
@@ -458,7 +474,7 @@ static void dump_log_file(
}
logger_list = android_logger_list_open(
- android_name_to_log_id(filename), O_RDONLY | O_NONBLOCK, tail, pid);
+ android_name_to_log_id(filename), ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, tail, pid);
if (!logger_list) {
ALOGE("Unable to open %s: %s\n", filename, strerror(errno));
@@ -626,10 +642,13 @@ static bool dump_crash(log_t* log, pid_t pid, pid_t tid, int signal, int si_code
UniquePtr<BacktraceMap> map(BacktraceMap::Create(pid));
UniquePtr<Backtrace> backtrace(Backtrace::Create(pid, tid, map.get()));
+ dump_abort_message(backtrace.get(), log, abort_msg_address);
+ dump_registers(log, tid);
if (backtrace->Unwind(0)) {
- dump_abort_message(backtrace.get(), log, abort_msg_address);
- dump_thread(backtrace.get(), log);
+ dump_backtrace_and_stack(backtrace.get(), log);
}
+ dump_memory_and_code(log, tid);
+ dump_all_maps(backtrace.get(), map.get(), log, tid);
if (want_logs) {
dump_logs(log, pid, 5);
diff --git a/debuggerd/utility.cpp b/debuggerd/utility.cpp
index 2baf9de..e10feff 100644
--- a/debuggerd/utility.cpp
+++ b/debuggerd/utility.cpp
@@ -131,12 +131,6 @@ int wait_for_sigstop(pid_t tid, int* total_sleep_time_usec, bool* detach_failed)
return -1;
}
-#if defined (__mips__)
-#define DUMP_MEMORY_AS_ASCII 1
-#else
-#define DUMP_MEMORY_AS_ASCII 0
-#endif
-
void dump_memory(log_t* log, pid_t tid, uintptr_t addr) {
char code_buffer[64];
char ascii_buffer[32];
@@ -183,7 +177,6 @@ void dump_memory(log_t* log, pid_t tid, uintptr_t addr) {
static_cast<uintptr_t>(data));
}
-#if DUMP_MEMORY_AS_ASCII
for (size_t j = 0; j < sizeof(long); j++) {
/*
* Our isprint() allows high-ASCII characters that display
@@ -197,7 +190,6 @@ void dump_memory(log_t* log, pid_t tid, uintptr_t addr) {
*asc_out++ = '.';
}
}
-#endif
p += sizeof(long);
}
*asc_out = '\0';
diff --git a/debuggerd/utility.h b/debuggerd/utility.h
index 58a882c..49b46e8 100644
--- a/debuggerd/utility.h
+++ b/debuggerd/utility.h
@@ -26,8 +26,10 @@
#define ABI_STRING "arm"
#elif defined(__aarch64__)
#define ABI_STRING "arm64"
-#elif defined(__mips__)
+#elif defined(__mips__) && !defined(__LP64__)
#define ABI_STRING "mips"
+#elif defined(__mips__) && defined(__LP64__)
+#define ABI_STRING "mips64"
#elif defined(__i386__)
#define ABI_STRING "x86"
#elif defined(__x86_64__)