diff options
Diffstat (limited to 'debuggerd')
-rw-r--r-- | debuggerd/Android.mk | 13 | ||||
-rw-r--r-- | debuggerd/crasher.c | 30 | ||||
-rw-r--r-- | debuggerd/debuggerd.cpp | 165 | ||||
-rw-r--r-- | debuggerd/elf_utils.cpp | 121 | ||||
-rw-r--r-- | debuggerd/elf_utils.h | 27 | ||||
-rw-r--r-- | debuggerd/mips/machine.cpp | 16 | ||||
-rw-r--r-- | debuggerd/mips64/crashglue.S | 48 | ||||
-rw-r--r-- | debuggerd/mips64/machine.cpp | 100 | ||||
-rw-r--r-- | debuggerd/tombstone.cpp | 181 | ||||
-rw-r--r-- | debuggerd/utility.cpp | 8 | ||||
-rw-r--r-- | debuggerd/utility.h | 4 |
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", ®_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__) |