diff options
Diffstat (limited to 'debuggerd')
-rw-r--r-- | debuggerd/Android.mk | 26 | ||||
-rw-r--r-- | debuggerd/MODULE_LICENSE_APACHE2 | 0 | ||||
-rw-r--r-- | debuggerd/NOTICE | 190 | ||||
-rw-r--r-- | debuggerd/crasher.c | 105 | ||||
-rw-r--r-- | debuggerd/crashglue.S | 28 | ||||
-rw-r--r-- | debuggerd/debuggerd.c | 852 | ||||
-rw-r--r-- | debuggerd/getevent.c | 219 | ||||
-rw-r--r-- | debuggerd/pr-support.c | 345 | ||||
-rw-r--r-- | debuggerd/unwind-arm.c | 654 | ||||
-rw-r--r-- | debuggerd/utility.c | 83 | ||||
-rw-r--r-- | debuggerd/utility.h | 56 |
11 files changed, 2558 insertions, 0 deletions
diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk new file mode 100644 index 000000000..b22e1a81d --- /dev/null +++ b/debuggerd/Android.mk @@ -0,0 +1,26 @@ +# Copyright 2005 The Android Open Source Project + +ifeq ($(TARGET_ARCH),arm) + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= debuggerd.c getevent.c unwind-arm.c pr-support.c utility.c +LOCAL_CFLAGS := -Wall +LOCAL_MODULE := debuggerd + +LOCAL_STATIC_LIBRARIES := libcutils libc + +include $(BUILD_EXECUTABLE) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := crasher.c +LOCAL_SRC_FILES += crashglue.S +LOCAL_MODULE := crasher +LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) +LOCAL_MODULE_TAGS := eng +#LOCAL_FORCE_STATIC_EXECUTABLE := true +LOCAL_SHARED_LIBRARIES := libcutils libc +include $(BUILD_EXECUTABLE) + +endif # TARGET_ARCH == arm diff --git a/debuggerd/MODULE_LICENSE_APACHE2 b/debuggerd/MODULE_LICENSE_APACHE2 new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/debuggerd/MODULE_LICENSE_APACHE2 diff --git a/debuggerd/NOTICE b/debuggerd/NOTICE new file mode 100644 index 000000000..c5b1efa7a --- /dev/null +++ b/debuggerd/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2008, 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. + + 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. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/debuggerd/crasher.c b/debuggerd/crasher.c new file mode 100644 index 000000000..f4a5a62bd --- /dev/null +++ b/debuggerd/crasher.c @@ -0,0 +1,105 @@ + +//#include <cutils/misc.h> + +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sched.h> +#include <errno.h> + +#include <signal.h> +#include <sys/ptrace.h> +#include <sys/wait.h> +#include <sys/socket.h> + +#include <pthread.h> + +#include <cutils/sockets.h> + +void crash1(void); +void crashnostack(void); + +static void debuggerd_connect() +{ + char tmp[1]; + int s; + sprintf(tmp, "%d", gettid()); + s = socket_local_client("android:debuggerd", + ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM); + if(s >= 0) { + read(s, tmp, 1); + close(s); + } +} + +void test_call1() +{ + *((int*) 32) = 1; +} + +void *test_thread(void *x) +{ + printf("crasher: thread pid=%d tid=%d\n", getpid(), gettid()); + + sleep(1); + test_call1(); + printf("goodbye\n"); + + return 0; +} + +void *noisy(void *x) +{ + char c = (unsigned) x; + for(;;) { + usleep(250*1000); + write(2, &c, 1); + if(c == 'C') *((unsigned*) 0) = 42; + } + return 0; +} + +int ctest() +{ + pthread_t thr; + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_create(&thr, &attr, noisy, (void*) 'A'); + pthread_create(&thr, &attr, noisy, (void*) 'B'); + pthread_create(&thr, &attr, noisy, (void*) 'C'); + for(;;) ; + return 0; +} + +int main(int argc, char **argv) +{ + pthread_t thr; + pthread_attr_t attr; + + fprintf(stderr,"crasher: " __TIME__ "!@\n"); + fprintf(stderr,"crasher: init pid=%d tid=%d\n", getpid(), gettid()); + + if(argc > 1) { + if(!strcmp(argv[1],"nostack")) crashnostack(); + if(!strcmp(argv[1],"ctest")) return ctest(); + if(!strcmp(argv[1],"exit")) exit(1); + if(!strcmp(argv[1],"abort")) maybeabort(); + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_create(&thr, &attr, test_thread, 0); + while(1) sleep(1); + } else { + crash1(); +// *((int*) 0) = 42; + } + + return 0; +} + +void maybeabort() +{ + if(time(0) != 42) abort(); +} diff --git a/debuggerd/crashglue.S b/debuggerd/crashglue.S new file mode 100644 index 000000000..888951b9c --- /dev/null +++ b/debuggerd/crashglue.S @@ -0,0 +1,28 @@ +.globl crash1 +.globl crashnostack + +crash1: + ldr r0, =0xa5a50000 + ldr r1, =0xa5a50001 + ldr r2, =0xa5a50002 + ldr r3, =0xa5a50003 + ldr r4, =0xa5a50004 + ldr r5, =0xa5a50005 + ldr r6, =0xa5a50006 + ldr r7, =0xa5a50007 + ldr r8, =0xa5a50008 + ldr r9, =0xa5a50009 + ldr r10, =0xa5a50010 + ldr r11, =0xa5a50011 + ldr r12, =0xa5a50012 + + mov lr, #0 + ldr lr, [lr] + b . + + +crashnostack: + mov sp, #0 + mov r0, #0 + ldr r0, [r0] + b .
\ No newline at end of file diff --git a/debuggerd/debuggerd.c b/debuggerd/debuggerd.c new file mode 100644 index 000000000..9394e1cac --- /dev/null +++ b/debuggerd/debuggerd.c @@ -0,0 +1,852 @@ +/* system/debuggerd/debuggerd.c +** +** Copyright 2006, 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 <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <signal.h> +#include <pthread.h> +#include <stdarg.h> +#include <fcntl.h> +#include <sys/types.h> +#include <dirent.h> + +#include <sys/ptrace.h> +#include <sys/wait.h> +#include <sys/exec_elf.h> +#include <sys/stat.h> + +#include <cutils/sockets.h> +#include <cutils/logd.h> +#include <cutils/sockets.h> +#include <cutils/properties.h> + +#include <linux/input.h> + +#include <private/android_filesystem_config.h> + +#include "utility.h" + +/* Main entry point to get the backtrace from the crashing process */ +extern int unwind_backtrace_with_ptrace(int tfd, pid_t pid, mapinfo *map, + unsigned int sp_list[], + int *frame0_pc_sane, + bool at_fault); + +static char **process_name_ptr; + +static int logsocket = -1; + +#define ANDROID_LOG_INFO 4 + +/* Log information onto the tombstone */ +void _LOG(int tfd, bool in_tombstone_only, const char *fmt, ...) +{ + char buf[128]; + + va_list ap; + va_start(ap, fmt); + + if (tfd >= 0) { + int len; + vsnprintf(buf, sizeof(buf), fmt, ap); + len = strlen(buf); + if(tfd >= 0) write(tfd, buf, len); + } + + if (!in_tombstone_only) + __android_log_vprint(ANDROID_LOG_INFO, "DEBUG", fmt, ap); +} + +#define LOG(fmt...) _LOG(-1, 0, fmt) +#if 0 +#define XLOG(fmt...) _LOG(-1, 0, fmt) +#else +#define XLOG(fmt...) do {} while(0) +#endif + +// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so +// 012345678901234567890123456789012345678901234567890123456789 +// 0 1 2 3 4 5 + +mapinfo *parse_maps_line(char *line) +{ + mapinfo *mi; + int len = strlen(line); + + if(len < 1) return 0; + line[--len] = 0; + + if(len < 50) return 0; + if(line[20] != 'x') return 0; + + mi = malloc(sizeof(mapinfo) + (len - 47)); + if(mi == 0) return 0; + + mi->start = strtoul(line, 0, 16); + mi->end = strtoul(line + 9, 0, 16); + /* To be filled in parse_exidx_info if the mapped section starts with + * elf_header + */ + mi->exidx_start = mi->exidx_end = 0; + mi->next = 0; + strcpy(mi->name, line + 49); + + return mi; +} + +void dump_build_info(int tfd) +{ + char fingerprint[PROPERTY_VALUE_MAX]; + + property_get("ro.build.fingerprint", fingerprint, "unknown"); + + _LOG(tfd, false, "Build fingerprint: '%s'\n", fingerprint); +} + + +void dump_stack_and_code(int tfd, int pid, mapinfo *map, + int unwind_depth, unsigned int sp_list[], + int frame0_pc_sane, bool at_fault) +{ + unsigned int sp, pc, p, end, data; + struct pt_regs r; + int sp_depth; + bool only_in_tombstone = !at_fault; + + if(ptrace(PTRACE_GETREGS, pid, 0, &r)) return; + sp = r.ARM_sp; + pc = r.ARM_pc; + + /* Died because calling the weeds - dump + * the code around the PC in the next frame instead. + */ + if (frame0_pc_sane == 0) { + pc = r.ARM_lr; + } + + _LOG(tfd, true, "code%s:\n", frame0_pc_sane ? "" : " (around frame #01)"); + + end = p = pc & ~3; + p -= 16; + + /* Dump the code as: + * PC contents + * 00008d34 fffffcd0 4c0eb530 b0934a0e 1c05447c + * 00008d44 f7ff18a0 490ced94 68035860 d0012b00 + */ + while (p <= end) { + int i; + + _LOG(tfd, true, " %08x ", p); + for (i = 0; i < 4; i++) { + data = ptrace(PTRACE_PEEKTEXT, pid, (void*)p, NULL); + _LOG(tfd, true, " %08x", data); + p += 4; + } + _LOG(tfd, true, "\n", p); + } + + p = sp - 64; + p &= ~3; + if (unwind_depth != 0) { + if (unwind_depth < STACK_CONTENT_DEPTH) { + end = sp_list[unwind_depth-1]; + } + else { + end = sp_list[STACK_CONTENT_DEPTH-1]; + } + } + else { + end = sp | 0x000000ff; + end += 0xff; + } + + _LOG(tfd, only_in_tombstone, "stack:\n"); + + /* If the crash is due to PC == 0, there will be two frames that + * have identical SP value. + */ + if (sp_list[0] == sp_list[1]) { + sp_depth = 1; + } + else { + sp_depth = 0; + } + + while (p <= end) { + char *prompt; + char level[16]; + data = ptrace(PTRACE_PEEKTEXT, pid, (void*)p, NULL); + if (p == sp_list[sp_depth]) { + sprintf(level, "#%02d", sp_depth++); + prompt = level; + } + else { + prompt = " "; + } + + /* Print the stack content in the log for the first 3 frames. For the + * rest only print them in the tombstone file. + */ + _LOG(tfd, (sp_depth > 2) || only_in_tombstone, + "%s %08x %08x %s\n", prompt, p, data, + map_to_name(map, data, "")); + p += 4; + } + /* print another 64-byte of stack data after the last frame */ + + end = p+64; + while (p <= end) { + data = ptrace(PTRACE_PEEKTEXT, pid, (void*)p, NULL); + _LOG(tfd, (sp_depth > 2) || only_in_tombstone, + " %08x %08x %s\n", p, data, + map_to_name(map, data, "")); + p += 4; + } +} + +void dump_pc_and_lr(int tfd, int pid, mapinfo *map, int unwound_level, + bool at_fault) +{ + struct pt_regs r; + + if(ptrace(PTRACE_GETREGS, pid, 0, &r)) { + _LOG(tfd, !at_fault, "tid %d not responding!\n", pid); + return; + } + + if (unwound_level == 0) { + _LOG(tfd, !at_fault, " #%02d pc %08x %s\n", 0, r.ARM_pc, + map_to_name(map, r.ARM_pc, "<unknown>")); + } + _LOG(tfd, !at_fault, " #%02d lr %08x %s\n", 1, r.ARM_lr, + map_to_name(map, r.ARM_lr, "<unknown>")); +} + +void dump_registers(int tfd, int pid, bool at_fault) +{ + struct pt_regs r; + bool only_in_tombstone = !at_fault; + + if(ptrace(PTRACE_GETREGS, pid, 0, &r)) { + _LOG(tfd, only_in_tombstone, + "cannot get registers: %s\n", strerror(errno)); + return; + } + + _LOG(tfd, only_in_tombstone, " r0 %08x r1 %08x r2 %08x r3 %08x\n", + r.ARM_r0, r.ARM_r1, r.ARM_r2, r.ARM_r3); + _LOG(tfd, only_in_tombstone, " r4 %08x r5 %08x r6 %08x r7 %08x\n", + r.ARM_r4, r.ARM_r5, r.ARM_r6, r.ARM_r7); + _LOG(tfd, only_in_tombstone, " r8 %08x r9 %08x 10 %08x fp %08x\n", + r.ARM_r8, r.ARM_r9, r.ARM_r10, r.ARM_fp); + _LOG(tfd, only_in_tombstone, + " ip %08x sp %08x lr %08x pc %08x cpsr %08x\n", + r.ARM_ip, r.ARM_sp, r.ARM_lr, r.ARM_pc, r.ARM_cpsr); +} + +const char *get_signame(int sig) +{ + switch(sig) { + case SIGILL: return "SIGILL"; + case SIGABRT: return "SIGABRT"; + case SIGBUS: return "SIGBUS"; + case SIGFPE: return "SIGFPE"; + case SIGSEGV: return "SIGSEGV"; + case SIGSTKFLT: return "SIGSTKFLT"; + default: return "?"; + } +} + +void dump_fault_addr(int tfd, int pid, int sig) +{ + siginfo_t si; + + memset(&si, 0, sizeof(si)); + if(ptrace(PTRACE_GETSIGINFO, pid, 0, &si)){ + _LOG(tfd, false, "cannot get siginfo: %s\n", strerror(errno)); + } else { + _LOG(tfd, false, "signal %d (%s), fault addr %08x\n", + sig, get_signame(sig), si.si_addr); + } +} + +void dump_crash_banner(int tfd, unsigned pid, unsigned tid, int sig) +{ + char data[1024]; + char *x = 0; + FILE *fp; + + sprintf(data, "/proc/%d/cmdline", pid); + fp = fopen(data, "r"); + if(fp) { + x = fgets(data, 1024, fp); + fclose(fp); + } + + _LOG(tfd, false, + "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n"); + dump_build_info(tfd); + _LOG(tfd, false, "pid: %d, tid: %d >>> %s <<<\n", + pid, tid, x ? x : "UNKNOWN"); + + if(sig) dump_fault_addr(tfd, tid, sig); +} + +static void parse_exidx_info(mapinfo *milist, pid_t pid) +{ + mapinfo *mi; + for (mi = milist; mi != NULL; mi = mi->next) { + Elf32_Ehdr ehdr; + + memset(&ehdr, 0, sizeof(Elf32_Ehdr)); + /* Read in sizeof(Elf32_Ehdr) worth of data from the beginning of + * mapped section. + */ + get_remote_struct(pid, (void *) (mi->start), &ehdr, + sizeof(Elf32_Ehdr)); + /* Check if it has the matching magic words */ + if (IS_ELF(ehdr)) { + Elf32_Phdr phdr; + Elf32_Phdr *ptr; + int i; + + ptr = (Elf32_Phdr *) (mi->start + ehdr.e_phoff); + for (i = 0; i < ehdr.e_phnum; i++) { + /* Parse the program header */ + get_remote_struct(pid, (void *) ptr+i, &phdr, + sizeof(Elf32_Phdr)); + /* Found a EXIDX segment? */ + if (phdr.p_type == PT_ARM_EXIDX) { + mi->exidx_start = mi->start + phdr.p_offset; + mi->exidx_end = mi->exidx_start + phdr.p_filesz; + break; + } + } + } + } +} + +void dump_crash_report(int tfd, unsigned pid, unsigned tid, bool at_fault) +{ + char data[1024]; + FILE *fp; + mapinfo *milist = 0; + unsigned int sp_list[STACK_CONTENT_DEPTH]; + int stack_depth; + int frame0_pc_sane = 1; + + if (!at_fault) { + _LOG(tfd, true, + "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n"); + _LOG(tfd, true, "pid: %d, tid: %d\n", pid, tid); + } + + dump_registers(tfd, tid, at_fault); + + /* Clear stack pointer records */ + memset(sp_list, 0, sizeof(sp_list)); + + sprintf(data, "/proc/%d/maps", pid); + fp = fopen(data, "r"); + if(fp) { + while(fgets(data, 1024, fp)) { + mapinfo *mi = parse_maps_line(data); + if(mi) { + mi->next = milist; + milist = mi; + } + } + fclose(fp); + } + + parse_exidx_info(milist, tid); + + /* If stack unwinder fails, use the default solution to dump the stack + * content. + */ + stack_depth = unwind_backtrace_with_ptrace(tfd, tid, milist, sp_list, + &frame0_pc_sane, at_fault); + + /* The stack unwinder should at least unwind two levels of stack. If less + * level is seen we make sure at lease pc and lr are dumped. + */ + if (stack_depth < 2) { + dump_pc_and_lr(tfd, tid, milist, stack_depth, at_fault); + } + + dump_stack_and_code(tfd, tid, milist, stack_depth, sp_list, frame0_pc_sane, + at_fault); + + while(milist) { + mapinfo *next = milist->next; + free(milist); + milist = next; + } +} + +/* FIXME: unused: use it or lose it*/ +#if 0 +static +void start_gdbserver_vs(int pid, int port) +{ + pid_t p; + char *args[5]; + char commspec[16]; + char pidspec[16]; + + p = fork(); + if(p < 0) { + LOG("could not fork()\n"); + return; + } + + if(p == 0) { + sprintf(commspec, ":%d", port); + sprintf(pidspec, "%d", pid); + args[0] = "/system/bin/gdbserver"; + args[1] = commspec; + args[2] = "--attach"; + args[3] = pidspec; + args[4] = 0; + exit(execv(args[0], args)); + } else { + LOG("gdbserver pid=%d port=%d targetpid=%d\n", + p, port, pid); + + sleep(5); + } +} +#endif + +#define MAX_TOMBSTONES 10 + +#define typecheck(x,y) { \ + typeof(x) __dummy1; \ + typeof(y) __dummy2; \ + (void)(&__dummy1 == &__dummy2); } + +#define TOMBSTONE_DIR "/data/tombstones" + +/* + * find_and_open_tombstone - find an available tombstone slot, if any, of the + * form tombstone_XX where XX is 00 to MAX_TOMBSTONES-1, inclusive. If no + * file is available, we reuse the least-recently-modified file. + */ +static int find_and_open_tombstone(void) +{ + unsigned long mtime = ULONG_MAX; + struct stat sb; + char path[128]; + int fd, i, oldest = 0; + + /* + * XXX: Our stat.st_mtime isn't time_t. If it changes, as it probably ought + * to, our logic breaks. This check will generate a warning if that happens. + */ + typecheck(mtime, sb.st_mtime); + + /* + * In a single wolf-like pass, find an available slot and, in case none + * exist, find and record the least-recently-modified file. + */ + for (i = 0; i < MAX_TOMBSTONES; i++) { + snprintf(path, sizeof(path), TOMBSTONE_DIR"/tombstone_%02d", i); + + if (!stat(path, &sb)) { + if (sb.st_mtime < mtime) { + oldest = i; + mtime = sb.st_mtime; + } + continue; + } + if (errno != ENOENT) + continue; + + fd = open(path, O_CREAT | O_EXCL | O_WRONLY, 0600); + if (fd < 0) + continue; /* raced ? */ + + fchown(fd, AID_SYSTEM, AID_SYSTEM); + return fd; + } + + /* we didn't find an available file, so we clobber the oldest one */ + snprintf(path, sizeof(path), TOMBSTONE_DIR"/tombstone_%02d", oldest); + fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600); + fchown(fd, AID_SYSTEM, AID_SYSTEM); + + return fd; +} + +/* Return true if some thread is not detached cleanly */ +static bool dump_sibling_thread_report(int tfd, unsigned pid, unsigned tid) +{ + char task_path[1024]; + + sprintf(task_path, "/proc/%d/task", pid); + DIR *d; + struct dirent *de; + int need_cleanup = 0; + + d = opendir(task_path); + /* Bail early if cannot open the task directory */ + if (d == NULL) { + XLOG("Cannot open /proc/%d/task\n", pid); + return false; + } + while ((de = readdir(d)) != NULL) { + unsigned new_tid; + /* Ignore "." and ".." */ + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) + continue; + new_tid = atoi(de->d_name); + /* The main thread at fault has been handled individually */ + if (new_tid == tid) + continue; + + /* Skip this thread if cannot ptrace it */ + if (ptrace(PTRACE_ATTACH, new_tid, 0, 0) < 0) + continue; + + dump_crash_report(tfd, pid, new_tid, false); + need_cleanup |= ptrace(PTRACE_DETACH, new_tid, 0, 0); + } + closedir(d); + return need_cleanup != 0; +} + +/* Return true if some thread is not detached cleanly */ +static bool engrave_tombstone(unsigned pid, unsigned tid, int debug_uid, + int signal) +{ + int fd; + bool need_cleanup = false; + + mkdir(TOMBSTONE_DIR, 0755); + chown(TOMBSTONE_DIR, AID_SYSTEM, AID_SYSTEM); + + fd = find_and_open_tombstone(); + if (fd < 0) + return need_cleanup; + + dump_crash_banner(fd, pid, tid, signal); + dump_crash_report(fd, pid, tid, true); + /* + * If the user has requested to attach gdb, don't collect the per-thread + * information as it increases the chance to lose track of the process. + */ + if ((signed)pid > debug_uid) { + need_cleanup = dump_sibling_thread_report(fd, pid, tid); + } + + close(fd); + return need_cleanup; +} + +static int +write_string(const char* file, const char* string) +{ + int len; + int fd; + ssize_t amt; + fd = open(file, O_RDWR); + len = strlen(string); + if (fd < 0) + return -errno; + amt = write(fd, string, len); + close(fd); + return amt >= 0 ? 0 : -errno; +} + +static +void init_debug_led(void) +{ + // trout leds + write_string("/sys/class/leds/red/brightness", "0"); + write_string("/sys/class/leds/green/brightness", "0"); + write_string("/sys/class/leds/blue/brightness", "0"); + write_string("/sys/class/leds/red/device/blink", "0"); + // sardine leds + write_string("/sys/class/leds/left/cadence", "0,0"); +} + +static +void enable_debug_led(void) +{ + // trout leds + write_string("/sys/class/leds/red/brightness", "255"); + // sardine leds + write_string("/sys/class/leds/left/cadence", "1,0"); +} + +static +void disable_debug_led(void) +{ + // trout leds + write_string("/sys/class/leds/red/brightness", "0"); + // sardine leds + write_string("/sys/class/leds/left/cadence", "0,0"); +} + +extern int init_getevent(); +extern void uninit_getevent(); +extern int get_event(struct input_event* event, int timeout); + +static void wait_for_user_action(unsigned tid, struct ucred* cr) +{ + (void)tid; + /* First log a helpful message */ + LOG( "********************************************************\n" + "* process %d crashed. debuggerd waiting for gdbserver \n" + "* \n" + "* adb shell gdbserver :port --attach %d & \n" + "* \n" + "* and press the HOME key. \n" + "********************************************************\n", + cr->pid, cr->pid); + + /* wait for HOME key */ + if (init_getevent() == 0) { + int ms = 1200 / 10; + int dit = 1; + int dah = 3*dit; + int _ = -dit; + int ___ = 3*_; + int _______ = 7*_; + const signed char codes[] = { + dit,_,dit,_,dit,___,dah,_,dah,_,dah,___,dit,_,dit,_,dit,_______ + }; + size_t s = 0; + struct input_event e; + int home = 0; + init_debug_led(); + enable_debug_led(); + do { + int timeout = abs((int)(codes[s])) * ms; + int res = get_event(&e, timeout); + if (res == 0) { + if (e.type==EV_KEY && e.code==KEY_HOME && e.value==0) + home = 1; + } else if (res == 1) { + if (++s >= sizeof(codes)/sizeof(*codes)) + s = 0; + if (codes[s] > 0) { + enable_debug_led(); + } else { + disable_debug_led(); + } + } + } while (!home); + uninit_getevent(); + } + + /* don't forget to turn debug led off */ + disable_debug_led(); + + /* close filedescriptor */ + LOG("debuggerd resuming process %d", cr->pid); + } + +static void handle_crashing_process(int fd) +{ + char buf[64]; + struct stat s; + unsigned tid; + struct ucred cr; + int n, len, status; + int tid_attach_status = -1; + unsigned retry = 30; + bool need_cleanup = false; + + char value[PROPERTY_VALUE_MAX]; + property_get("debug.db.uid", value, "-1"); + int debug_uid = atoi(value); + + XLOG("handle_crashing_process(%d)\n", fd); + + len = sizeof(cr); + n = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len); + if(n != 0) { + LOG("cannot get credentials\n"); + goto done; + } + + XLOG("reading tid\n"); + fcntl(fd, F_SETFL, O_NONBLOCK); + while((n = read(fd, &tid, sizeof(unsigned))) != sizeof(unsigned)) { + if(errno == EINTR) continue; + if(errno == EWOULDBLOCK) { + if(retry-- > 0) { + usleep(100 * 1000); + continue; + } + LOG("timed out reading tid\n"); + goto done; + } + LOG("read failure? %s\n", strerror(errno)); + goto done; + } + + sprintf(buf,"/proc/%d/task/%d", cr.pid, tid); + if(stat(buf, &s)) { + LOG("tid %d does not exist in pid %d. ignorning debug request\n", + tid, cr.pid); + close(fd); + return; + } + + XLOG("BOOM: pid=%d uid=%d gid=%d tid=%d\n", cr.pid, cr.uid, cr.gid, tid); + + tid_attach_status = ptrace(PTRACE_ATTACH, tid, 0, 0); + if(tid_attach_status < 0) { + LOG("ptrace attach failed: %s\n", strerror(errno)); + goto done; + } + + close(fd); + fd = -1; + + for(;;) { + n = waitpid(tid, &status, __WALL); + + if(n < 0) { + if(errno == EAGAIN) continue; + LOG("waitpid failed: %s\n", strerror(errno)); + goto done; + } + + XLOG("waitpid: n=%d status=%08x\n", n, status); + + if(WIFSTOPPED(status)){ + n = WSTOPSIG(status); + switch(n) { + case SIGSTOP: + XLOG("stopped -- continuing\n"); + n = ptrace(PTRACE_CONT, tid, 0, 0); + if(n) { + LOG("ptrace failed: %s\n", strerror(errno)); + goto done; + } + continue; + + case SIGILL: + case SIGABRT: + case SIGBUS: + case SIGFPE: + case SIGSEGV: + case SIGSTKFLT: { + XLOG("stopped -- fatal signal\n"); + need_cleanup = engrave_tombstone(cr.pid, tid, debug_uid, n); + kill(tid, SIGSTOP); + goto done; + } + + default: + XLOG("stopped -- unexpected signal\n"); + goto done; + } + } else { + XLOG("unexpected waitpid response\n"); + goto done; + } + } + +done: + XLOG("detaching\n"); + + /* stop the process so we can debug */ + kill(cr.pid, SIGSTOP); + + /* + * If a thread has been attached by ptrace, make sure it is detached + * successfully otherwise we will get a zombie. + */ + if (tid_attach_status == 0) { + int detach_status; + /* detach so we can attach gdbserver */ + detach_status = ptrace(PTRACE_DETACH, tid, 0, 0); + need_cleanup |= (detach_status != 0); + } + + /* + * if debug.db.uid is set, its value indicates if we should wait + * for user action for the crashing process. + * in this case, we log a message and turn the debug LED on + * waiting for a gdb connection (for instance) + */ + + if ((signed)cr.uid <= debug_uid) { + wait_for_user_action(tid, &cr); + } + + /* resume stopped process (so it can crash in peace) */ + kill(cr.pid, SIGCONT); + + if (need_cleanup) { + LOG("debuggerd committing suicide to free the zombie!\n"); + kill(getpid(), SIGKILL); + } + + if(fd != -1) close(fd); +} + +int main(int argc, char **argv) +{ + int s; + struct sigaction act; + + process_name_ptr = argv; + + logsocket = socket_local_client("logd", + ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_DGRAM); + if(logsocket < 0) { + logsocket = -1; + } else { + fcntl(logsocket, F_SETFD, FD_CLOEXEC); + } + + act.sa_handler = SIG_DFL; + sigemptyset(&act.sa_mask); + sigaddset(&act.sa_mask,SIGCHLD); + act.sa_flags = SA_NOCLDWAIT; + sigaction(SIGCHLD, &act, 0); + + s = socket_local_server("android:debuggerd", + ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM); + if(s < 0) return -1; + fcntl(s, F_SETFD, FD_CLOEXEC); + + LOG("debuggerd: " __DATE__ " " __TIME__ "\n"); + + for(;;) { + struct sockaddr addr; + socklen_t alen; + int fd; + + alen = sizeof(addr); + fd = accept(s, &addr, &alen); + if(fd < 0) continue; + + fcntl(fd, F_SETFD, FD_CLOEXEC); + + handle_crashing_process(fd); + } + return 0; +} diff --git a/debuggerd/getevent.c b/debuggerd/getevent.c new file mode 100644 index 000000000..ebd070c06 --- /dev/null +++ b/debuggerd/getevent.c @@ -0,0 +1,219 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <dirent.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/inotify.h> +#include <sys/limits.h> +#include <sys/poll.h> +#include <linux/input.h> +#include <errno.h> +#include <cutils/log.h> + +static struct pollfd *ufds; +static char **device_names; +static int nfds; + +static int open_device(const char *device) +{ + int version; + int fd; + struct pollfd *new_ufds; + char **new_device_names; + char name[80]; + char location[80]; + char idstr[80]; + struct input_id id; + + fd = open(device, O_RDWR); + if(fd < 0) { + return -1; + } + + if(ioctl(fd, EVIOCGVERSION, &version)) { + return -1; + } + if(ioctl(fd, EVIOCGID, &id)) { + return -1; + } + name[sizeof(name) - 1] = '\0'; + location[sizeof(location) - 1] = '\0'; + idstr[sizeof(idstr) - 1] = '\0'; + if(ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) { + //fprintf(stderr, "could not get device name for %s, %s\n", device, strerror(errno)); + name[0] = '\0'; + } + if(ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) { + //fprintf(stderr, "could not get location for %s, %s\n", device, strerror(errno)); + location[0] = '\0'; + } + if(ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1) { + //fprintf(stderr, "could not get idstring for %s, %s\n", device, strerror(errno)); + idstr[0] = '\0'; + } + + new_ufds = realloc(ufds, sizeof(ufds[0]) * (nfds + 1)); + if(new_ufds == NULL) { + fprintf(stderr, "out of memory\n"); + return -1; + } + ufds = new_ufds; + new_device_names = realloc(device_names, sizeof(device_names[0]) * (nfds + 1)); + if(new_device_names == NULL) { + fprintf(stderr, "out of memory\n"); + return -1; + } + device_names = new_device_names; + ufds[nfds].fd = fd; + ufds[nfds].events = POLLIN; + device_names[nfds] = strdup(device); + nfds++; + + return 0; +} + +int close_device(const char *device) +{ + int i; + for(i = 1; i < nfds; i++) { + if(strcmp(device_names[i], device) == 0) { + int count = nfds - i - 1; + free(device_names[i]); + memmove(device_names + i, device_names + i + 1, sizeof(device_names[0]) * count); + memmove(ufds + i, ufds + i + 1, sizeof(ufds[0]) * count); + nfds--; + return 0; + } + } + return -1; +} + +static int read_notify(const char *dirname, int nfd) +{ + int res; + char devname[PATH_MAX]; + char *filename; + char event_buf[512]; + int event_size; + int event_pos = 0; + struct inotify_event *event; + + res = read(nfd, event_buf, sizeof(event_buf)); + if(res < (int)sizeof(*event)) { + if(errno == EINTR) + return 0; + fprintf(stderr, "could not get event, %s\n", strerror(errno)); + return 1; + } + //printf("got %d bytes of event information\n", res); + + strcpy(devname, dirname); + filename = devname + strlen(devname); + *filename++ = '/'; + + while(res >= (int)sizeof(*event)) { + event = (struct inotify_event *)(event_buf + event_pos); + //printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : ""); + if(event->len) { + strcpy(filename, event->name); + if(event->mask & IN_CREATE) { + open_device(devname); + } + else { + close_device(devname); + } + } + event_size = sizeof(*event) + event->len; + res -= event_size; + event_pos += event_size; + } + return 0; +} + +static int scan_dir(const char *dirname) +{ + char devname[PATH_MAX]; + char *filename; + DIR *dir; + struct dirent *de; + dir = opendir(dirname); + if(dir == NULL) + return -1; + strcpy(devname, dirname); + filename = devname + strlen(devname); + *filename++ = '/'; + while((de = readdir(dir))) { + if(de->d_name[0] == '.' && + (de->d_name[1] == '\0' || + (de->d_name[1] == '.' && de->d_name[2] == '\0'))) + continue; + strcpy(filename, de->d_name); + open_device(devname); + } + closedir(dir); + return 0; +} + +int init_getevent() +{ + int res; + const char *device_path = "/dev/input"; + + nfds = 1; + ufds = calloc(1, sizeof(ufds[0])); + ufds[0].fd = inotify_init(); + ufds[0].events = POLLIN; + + res = inotify_add_watch(ufds[0].fd, device_path, IN_DELETE | IN_CREATE); + if(res < 0) { + return 1; + } + res = scan_dir(device_path); + if(res < 0) { + return 1; + } + return 0; +} + +void uninit_getevent() +{ + int i; + for(i = 0; i < nfds; i++) { + close(ufds[i].fd); + } + free(ufds); + ufds = 0; + nfds = 0; +} + +int get_event(struct input_event* event, int timeout) +{ + int res; + int i; + int pollres; + const char *device_path = "/dev/input"; + while(1) { + pollres = poll(ufds, nfds, timeout); + if (pollres == 0) { + return 1; + } + if(ufds[0].revents & POLLIN) { + read_notify(device_path, ufds[0].fd); + } + for(i = 1; i < nfds; i++) { + if(ufds[i].revents) { + if(ufds[i].revents & POLLIN) { + res = read(ufds[i].fd, event, sizeof(*event)); + if(res < (int)sizeof(event)) { + fprintf(stderr, "could not get event\n"); + return -1; + } + return 0; + } + } + } + } + return 0; +} diff --git a/debuggerd/pr-support.c b/debuggerd/pr-support.c new file mode 100644 index 000000000..358d9b77a --- /dev/null +++ b/debuggerd/pr-support.c @@ -0,0 +1,345 @@ +/* ARM EABI compliant unwinding routines + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + Contributed by Paul Brook + + This file is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + In addition to the permissions in the GNU General Public License, the + Free Software Foundation gives you unlimited permission to link the + compiled version of this file into combinations with other programs, + and to distribute those combinations without any restriction coming + from the use of this file. (The General Public License restrictions + do apply in other respects; for example, they cover modification of + the file, and distribution when not linked into a combine + executable.) + + This file is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/**************************************************************************** + * The functions here are derived from gcc/config/arm/pr-support.c from the + * 4.3.x release. The main changes here involve the use of ptrace to retrieve + * memory/processor states from a remote process. + ****************************************************************************/ + +#include <sys/types.h> +#include <unwind.h> + +#include "utility.h" + +/* We add a prototype for abort here to avoid creating a dependency on + target headers. */ +extern void abort (void); + +/* Derived from _Unwind_VRS_Pop to use ptrace */ +extern _Unwind_VRS_Result +unwind_VRS_Pop_with_ptrace (_Unwind_Context *context, + _Unwind_VRS_RegClass regclass, + _uw discriminator, + _Unwind_VRS_DataRepresentation representation, + pid_t pid); + +typedef struct _ZSt9type_info type_info; /* This names C++ type_info type */ + +/* Misc constants. */ +#define R_IP 12 +#define R_SP 13 +#define R_LR 14 +#define R_PC 15 + +#define uint32_highbit (((_uw) 1) << 31) + +void __attribute__((weak)) __cxa_call_unexpected(_Unwind_Control_Block *ucbp); + +/* Unwind descriptors. */ + +typedef struct +{ + _uw16 length; + _uw16 offset; +} EHT16; + +typedef struct +{ + _uw length; + _uw offset; +} EHT32; + +/* Personality routine helper functions. */ + +#define CODE_FINISH (0xb0) + +/* Derived from next_unwind_byte to use ptrace */ +/* Return the next byte of unwinding information, or CODE_FINISH if there is + no data remaining. */ +static inline _uw8 +next_unwind_byte_with_ptrace (__gnu_unwind_state * uws, pid_t pid) +{ + _uw8 b; + + if (uws->bytes_left == 0) + { + /* Load another word */ + if (uws->words_left == 0) + return CODE_FINISH; /* Nothing left. */ + uws->words_left--; + uws->data = get_remote_word(pid, uws->next); + uws->next++; + uws->bytes_left = 3; + } + else + uws->bytes_left--; + + /* Extract the most significant byte. */ + b = (uws->data >> 24) & 0xff; + uws->data <<= 8; + return b; +} + +/* Execute the unwinding instructions described by UWS. */ +_Unwind_Reason_Code +unwind_execute_with_ptrace(_Unwind_Context * context, __gnu_unwind_state * uws, + pid_t pid) +{ + _uw op; + int set_pc; + _uw reg; + + set_pc = 0; + for (;;) + { + op = next_unwind_byte_with_ptrace (uws, pid); + if (op == CODE_FINISH) + { + /* If we haven't already set pc then copy it from lr. */ + if (!set_pc) + { + _Unwind_VRS_Get (context, _UVRSC_CORE, R_LR, _UVRSD_UINT32, + ®); + _Unwind_VRS_Set (context, _UVRSC_CORE, R_PC, _UVRSD_UINT32, + ®); + set_pc = 1; + } + /* Drop out of the loop. */ + break; + } + if ((op & 0x80) == 0) + { + /* vsp = vsp +- (imm6 << 2 + 4). */ + _uw offset; + + offset = ((op & 0x3f) << 2) + 4; + _Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, ®); + if (op & 0x40) + reg -= offset; + else + reg += offset; + _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, ®); + continue; + } + + if ((op & 0xf0) == 0x80) + { + op = (op << 8) | next_unwind_byte_with_ptrace (uws, pid); + if (op == 0x8000) + { + /* Refuse to unwind. */ + return _URC_FAILURE; + } + /* Pop r4-r15 under mask. */ + op = (op << 4) & 0xfff0; + if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_CORE, op, _UVRSD_UINT32, + pid) + != _UVRSR_OK) + return _URC_FAILURE; + if (op & (1 << R_PC)) + set_pc = 1; + continue; + } + if ((op & 0xf0) == 0x90) + { + op &= 0xf; + if (op == 13 || op == 15) + /* Reserved. */ + return _URC_FAILURE; + /* vsp = r[nnnn]. */ + _Unwind_VRS_Get (context, _UVRSC_CORE, op, _UVRSD_UINT32, ®); + _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, ®); + continue; + } + if ((op & 0xf0) == 0xa0) + { + /* Pop r4-r[4+nnn], [lr]. */ + _uw mask; + + mask = (0xff0 >> (7 - (op & 7))) & 0xff0; + if (op & 8) + mask |= (1 << R_LR); + if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_CORE, mask, _UVRSD_UINT32, + pid) + != _UVRSR_OK) + return _URC_FAILURE; + continue; + } + if ((op & 0xf0) == 0xb0) + { + /* op == 0xb0 already handled. */ + if (op == 0xb1) + { + op = next_unwind_byte_with_ptrace (uws, pid); + if (op == 0 || ((op & 0xf0) != 0)) + /* Spare. */ + return _URC_FAILURE; + /* Pop r0-r4 under mask. */ + if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_CORE, op, + _UVRSD_UINT32, pid) + != _UVRSR_OK) + return _URC_FAILURE; + continue; + } + if (op == 0xb2) + { + /* vsp = vsp + 0x204 + (uleb128 << 2). */ + int shift; + + _Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, + ®); + op = next_unwind_byte_with_ptrace (uws, pid); + shift = 2; + while (op & 0x80) + { + reg += ((op & 0x7f) << shift); + shift += 7; + op = next_unwind_byte_with_ptrace (uws, pid); + } + reg += ((op & 0x7f) << shift) + 0x204; + _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, + ®); + continue; + } + if (op == 0xb3) + { + /* Pop VFP registers with fldmx. */ + op = next_unwind_byte_with_ptrace (uws, pid); + op = ((op & 0xf0) << 12) | ((op & 0xf) + 1); + if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op, _UVRSD_VFPX, + pid) + != _UVRSR_OK) + return _URC_FAILURE; + continue; + } + if ((op & 0xfc) == 0xb4) + { + /* Pop FPA E[4]-E[4+nn]. */ + op = 0x40000 | ((op & 3) + 1); + if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_FPA, op, _UVRSD_FPAX, + pid) + != _UVRSR_OK) + return _URC_FAILURE; + continue; + } + /* op & 0xf8 == 0xb8. */ + /* Pop VFP D[8]-D[8+nnn] with fldmx. */ + op = 0x80000 | ((op & 7) + 1); + if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op, _UVRSD_VFPX, pid) + != _UVRSR_OK) + return _URC_FAILURE; + continue; + } + if ((op & 0xf0) == 0xc0) + { + if (op == 0xc6) + { + /* Pop iWMMXt D registers. */ + op = next_unwind_byte_with_ptrace (uws, pid); + op = ((op & 0xf0) << 12) | ((op & 0xf) + 1); + if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_WMMXD, op, + _UVRSD_UINT64, pid) + != _UVRSR_OK) + return _URC_FAILURE; + continue; + } + if (op == 0xc7) + { + op = next_unwind_byte_with_ptrace (uws, pid); + if (op == 0 || (op & 0xf0) != 0) + /* Spare. */ + return _URC_FAILURE; + /* Pop iWMMXt wCGR{3,2,1,0} under mask. */ + if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_WMMXC, op, + _UVRSD_UINT32, pid) + != _UVRSR_OK) + return _URC_FAILURE; + continue; + } + if ((op & 0xf8) == 0xc0) + { + /* Pop iWMMXt wR[10]-wR[10+nnn]. */ + op = 0xa0000 | ((op & 0xf) + 1); + if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_WMMXD, op, + _UVRSD_UINT64, pid) + != _UVRSR_OK) + return _URC_FAILURE; + continue; + } + if (op == 0xc8) + { +#ifndef __VFP_FP__ + /* Pop FPA registers. */ + op = next_unwind_byte_with_ptrace (uws, pid); + op = ((op & 0xf0) << 12) | ((op & 0xf) + 1); + if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_FPA, op, _UVRSD_FPAX, + pid) + != _UVRSR_OK) + return _URC_FAILURE; + continue; +#else + /* Pop VFPv3 registers D[16+ssss]-D[16+ssss+cccc] with vldm. */ + op = next_unwind_byte_with_ptrace (uws, pid); + op = (((op & 0xf0) + 16) << 12) | ((op & 0xf) + 1); + if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op, + _UVRSD_DOUBLE, pid) + != _UVRSR_OK) + return _URC_FAILURE; + continue; +#endif + } + if (op == 0xc9) + { + /* Pop VFP registers with fldmd. */ + op = next_unwind_byte_with_ptrace (uws, pid); + op = ((op & 0xf0) << 12) | ((op & 0xf) + 1); + if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op, + _UVRSD_DOUBLE, pid) + != _UVRSR_OK) + return _URC_FAILURE; + continue; + } + /* Spare. */ + return _URC_FAILURE; + } + if ((op & 0xf8) == 0xd0) + { + /* Pop VFP D[8]-D[8+nnn] with fldmd. */ + op = 0x80000 | ((op & 7) + 1); + if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op, _UVRSD_DOUBLE, + pid) + != _UVRSR_OK) + return _URC_FAILURE; + continue; + } + /* Spare. */ + return _URC_FAILURE; + } + return _URC_OK; +} diff --git a/debuggerd/unwind-arm.c b/debuggerd/unwind-arm.c new file mode 100644 index 000000000..9642d2e4a --- /dev/null +++ b/debuggerd/unwind-arm.c @@ -0,0 +1,654 @@ +/* ARM EABI compliant unwinding routines. + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + Contributed by Paul Brook + + This file is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + In addition to the permissions in the GNU General Public License, the + Free Software Foundation gives you unlimited permission to link the + compiled version of this file into combinations with other programs, + and to distribute those combinations without any restriction coming + from the use of this file. (The General Public License restrictions + do apply in other respects; for example, they cover modification of + the file, and distribution when not linked into a combine + executable.) + + This file is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/**************************************************************************** + * The functions here are derived from gcc/config/arm/unwind-arm.c from the + * 4.3.x release. The main changes here involve the use of ptrace to retrieve + * memory/processor states from a remote process. + ****************************************************************************/ + +#include <cutils/logd.h> +#include <sys/ptrace.h> +#include <unwind.h> +#include "utility.h" + +typedef struct _ZSt9type_info type_info; /* This names C++ type_info type */ + +void __attribute__((weak)) __cxa_call_unexpected(_Unwind_Control_Block *ucbp); +bool __attribute__((weak)) __cxa_begin_cleanup(_Unwind_Control_Block *ucbp); +bool __attribute__((weak)) __cxa_type_match(_Unwind_Control_Block *ucbp, + const type_info *rttip, + bool is_reference, + void **matched_object); + +/* Misc constants. */ +#define R_IP 12 +#define R_SP 13 +#define R_LR 14 +#define R_PC 15 + +#define EXIDX_CANTUNWIND 1 +#define uint32_highbit (((_uw) 1) << 31) + +#define UCB_FORCED_STOP_FN(ucbp) ((ucbp)->unwinder_cache.reserved1) +#define UCB_PR_ADDR(ucbp) ((ucbp)->unwinder_cache.reserved2) +#define UCB_SAVED_CALLSITE_ADDR(ucbp) ((ucbp)->unwinder_cache.reserved3) +#define UCB_FORCED_STOP_ARG(ucbp) ((ucbp)->unwinder_cache.reserved4) + +struct core_regs +{ + _uw r[16]; +}; + +/* We use normal integer types here to avoid the compiler generating + coprocessor instructions. */ +struct vfp_regs +{ + _uw64 d[16]; + _uw pad; +}; + +struct vfpv3_regs +{ + /* Always populated via VSTM, so no need for the "pad" field from + vfp_regs (which is used to store the format word for FSTMX). */ + _uw64 d[16]; +}; + +struct fpa_reg +{ + _uw w[3]; +}; + +struct fpa_regs +{ + struct fpa_reg f[8]; +}; + +struct wmmxd_regs +{ + _uw64 wd[16]; +}; + +struct wmmxc_regs +{ + _uw wc[4]; +}; + +/* Unwind descriptors. */ + +typedef struct +{ + _uw16 length; + _uw16 offset; +} EHT16; + +typedef struct +{ + _uw length; + _uw offset; +} EHT32; + +/* The ABI specifies that the unwind routines may only use core registers, + except when actually manipulating coprocessor state. This allows + us to write one implementation that works on all platforms by + demand-saving coprocessor registers. + + During unwinding we hold the coprocessor state in the actual hardware + registers and allocate demand-save areas for use during phase1 + unwinding. */ + +typedef struct +{ + /* The first fields must be the same as a phase2_vrs. */ + _uw demand_save_flags; + struct core_regs core; + _uw prev_sp; /* Only valid during forced unwinding. */ + struct vfp_regs vfp; + struct vfpv3_regs vfp_regs_16_to_31; + struct fpa_regs fpa; + struct wmmxd_regs wmmxd; + struct wmmxc_regs wmmxc; +} phase1_vrs; + +/* This must match the structure created by the assembly wrappers. */ +typedef struct +{ + _uw demand_save_flags; + struct core_regs core; +} phase2_vrs; + + +/* An exception index table entry. */ + +typedef struct __EIT_entry +{ + _uw fnoffset; + _uw content; +} __EIT_entry; + +/* Derived version to use ptrace */ +typedef _Unwind_Reason_Code (*personality_routine_with_ptrace) + (_Unwind_State, + _Unwind_Control_Block *, + _Unwind_Context *, + pid_t); + +/* Derived version to use ptrace */ +/* ABI defined personality routines. */ +static _Unwind_Reason_Code unwind_cpp_pr0_with_ptrace (_Unwind_State, + _Unwind_Control_Block *, _Unwind_Context *, pid_t); +static _Unwind_Reason_Code unwind_cpp_pr1_with_ptrace (_Unwind_State, + _Unwind_Control_Block *, _Unwind_Context *, pid_t); +static _Unwind_Reason_Code unwind_cpp_pr2_with_ptrace (_Unwind_State, + _Unwind_Control_Block *, _Unwind_Context *, pid_t); + +/* Execute the unwinding instructions described by UWS. */ +extern _Unwind_Reason_Code +unwind_execute_with_ptrace(_Unwind_Context * context, __gnu_unwind_state * uws, + pid_t pid); + +/* Derived version to use ptrace. Only handles core registers. Disregards + * FP and others. + */ +/* ABI defined function to pop registers off the stack. */ + +_Unwind_VRS_Result unwind_VRS_Pop_with_ptrace (_Unwind_Context *context, + _Unwind_VRS_RegClass regclass, + _uw discriminator, + _Unwind_VRS_DataRepresentation representation, + pid_t pid) +{ + phase1_vrs *vrs = (phase1_vrs *) context; + + switch (regclass) + { + case _UVRSC_CORE: + { + _uw *ptr; + _uw mask; + int i; + + if (representation != _UVRSD_UINT32) + return _UVRSR_FAILED; + + mask = discriminator & 0xffff; + ptr = (_uw *) vrs->core.r[R_SP]; + /* Pop the requested registers. */ + for (i = 0; i < 16; i++) + { + if (mask & (1 << i)) { + vrs->core.r[i] = get_remote_word(pid, ptr); + ptr++; + } + } + /* Writeback the stack pointer value if it wasn't restored. */ + if ((mask & (1 << R_SP)) == 0) + vrs->core.r[R_SP] = (_uw) ptr; + } + return _UVRSR_OK; + + default: + return _UVRSR_FAILED; + } +} + +/* Core unwinding functions. */ + +/* Calculate the address encoded by a 31-bit self-relative offset at address + P. */ +static inline _uw +selfrel_offset31 (const _uw *p, pid_t pid) +{ + _uw offset = get_remote_word(pid, (void*)p); + + //offset = *p; + /* Sign extend to 32 bits. */ + if (offset & (1 << 30)) + offset |= 1u << 31; + else + offset &= ~(1u << 31); + + return offset + (_uw) p; +} + + +/* Perform a binary search for RETURN_ADDRESS in TABLE. The table contains + NREC entries. */ + +static const __EIT_entry * +search_EIT_table (const __EIT_entry * table, int nrec, _uw return_address, + pid_t pid) +{ + _uw next_fn; + _uw this_fn; + int n, left, right; + + if (nrec == 0) + return (__EIT_entry *) 0; + + left = 0; + right = nrec - 1; + + while (1) + { + n = (left + right) / 2; + this_fn = selfrel_offset31 (&table[n].fnoffset, pid); + if (n != nrec - 1) + next_fn = selfrel_offset31 (&table[n + 1].fnoffset, pid) - 1; + else + next_fn = (_uw)0 - 1; + + if (return_address < this_fn) + { + if (n == left) + return (__EIT_entry *) 0; + right = n - 1; + } + else if (return_address <= next_fn) + return &table[n]; + else + left = n + 1; + } +} + +/* Find the exception index table eintry for the given address. */ +static const __EIT_entry* +get_eitp(_uw return_address, pid_t pid, mapinfo *map, mapinfo **containing_map) +{ + const __EIT_entry *eitp = NULL; + int nrec; + mapinfo *mi; + + /* The return address is the address of the instruction following the + call instruction (plus one in thumb mode). If this was the last + instruction in the function the address will lie in the following + function. Subtract 2 from the address so that it points within the call + instruction itself. */ + if (return_address >= 2) + return_address -= 2; + + for (mi = map; mi != NULL; mi = mi->next) { + if (return_address >= mi->start && return_address <= mi->end) break; + } + + if (mi) { + if (containing_map) *containing_map = mi; + eitp = (__EIT_entry *) mi->exidx_start; + nrec = (mi->exidx_end - mi->exidx_start)/sizeof(__EIT_entry); + eitp = search_EIT_table (eitp, nrec, return_address, pid); + } + return eitp; +} + +/* Find the exception index table eintry for the given address. + Fill in the relevant fields of the UCB. + Returns _URC_FAILURE if an error occurred, _URC_OK on success. */ + +static _Unwind_Reason_Code +get_eit_entry (_Unwind_Control_Block *ucbp, _uw return_address, pid_t pid, + mapinfo *map, mapinfo **containing_map) +{ + const __EIT_entry *eitp; + + eitp = get_eitp(return_address, pid, map, containing_map); + + if (!eitp) + { + UCB_PR_ADDR (ucbp) = 0; + return _URC_FAILURE; + } + ucbp->pr_cache.fnstart = selfrel_offset31 (&eitp->fnoffset, pid); + + _uw eitp_content = get_remote_word(pid, (void *)&eitp->content); + + /* Can this frame be unwound at all? */ + if (eitp_content == EXIDX_CANTUNWIND) + { + UCB_PR_ADDR (ucbp) = 0; + return _URC_END_OF_STACK; + } + + /* Obtain the address of the "real" __EHT_Header word. */ + + if (eitp_content & uint32_highbit) + { + /* It is immediate data. */ + ucbp->pr_cache.ehtp = (_Unwind_EHT_Header *)&eitp->content; + ucbp->pr_cache.additional = 1; + } + else + { + /* The low 31 bits of the content field are a self-relative + offset to an _Unwind_EHT_Entry structure. */ + ucbp->pr_cache.ehtp = + (_Unwind_EHT_Header *) selfrel_offset31 (&eitp->content, pid); + ucbp->pr_cache.additional = 0; + } + + /* Discover the personality routine address. */ + if (get_remote_word(pid, ucbp->pr_cache.ehtp) & (1u << 31)) + { + /* One of the predefined standard routines. */ + _uw idx = (get_remote_word(pid, ucbp->pr_cache.ehtp) >> 24) & 0xf; + if (idx == 0) + UCB_PR_ADDR (ucbp) = (_uw) &unwind_cpp_pr0_with_ptrace; + else if (idx == 1) + UCB_PR_ADDR (ucbp) = (_uw) &unwind_cpp_pr1_with_ptrace; + else if (idx == 2) + UCB_PR_ADDR (ucbp) = (_uw) &unwind_cpp_pr2_with_ptrace; + else + { /* Failed */ + UCB_PR_ADDR (ucbp) = 0; + return _URC_FAILURE; + } + } + else + { + /* Execute region offset to PR */ + UCB_PR_ADDR (ucbp) = selfrel_offset31 (ucbp->pr_cache.ehtp, pid); + /* Since we are unwinding the stack from a different process, it is + * impossible to execute the personality routine in debuggerd. Punt here. + */ + return _URC_FAILURE; + } + return _URC_OK; +} + +/* Print out the current call level, pc, and module name in the crash log */ +static _Unwind_Reason_Code log_function(_Unwind_Context *context, pid_t pid, + int tfd, + int stack_level, + mapinfo *map, + unsigned int sp_list[], + bool at_fault) +{ + _uw pc; + _uw rel_pc; + phase2_vrs *vrs = (phase2_vrs*) context; + const mapinfo *mi; + bool only_in_tombstone = !at_fault; + + if (stack_level < STACK_CONTENT_DEPTH) { + sp_list[stack_level] = vrs->core.r[R_SP]; + } + pc = vrs->core.r[R_PC]; + + // Top level frame + if (stack_level == 0) { + pc &= ~1; + } + // For deeper framers, rollback pc by one instruction + else { + pc = vrs->core.r[R_PC]; + /* Thumb mode - need to check whether the bl(x) has long offset or not. + * Examples: + * + * arm blx in the middle of thumb: + * 187ae: 2300 movs r3, #0 + * 187b0: f7fe ee1c blx 173ec + * 187b4: 2c00 cmp r4, #0 + * + * arm bl in the middle of thumb: + * 187d8: 1c20 adds r0, r4, #0 + * 187da: f136 fd15 bl 14f208 + * 187de: 2800 cmp r0, #0 + * + * pure thumb: + * 18894: 189b adds r3, r3, r2 + * 18896: 4798 blx r3 + * 18898: b001 add sp, #4 + */ + if (pc & 1) { + _uw prev_word; + pc = (pc & ~1); + prev_word = get_remote_word(pid, (void *) pc-4); + // Long offset + if ((prev_word & 0xf0000000) == 0xf0000000 && + (prev_word & 0x0000e000) == 0x0000e000) { + pc -= 4; + } + else { + pc -= 2; + } + } + else { + pc -= 4; + } + } + + /* We used to print the absolute PC in the back trace, and mask out the top + * 3 bits to guesstimate the offset in the .so file. This is not working for + * non-prelinked libraries since the starting offset may not be aligned on + * 1MB boundaries, and the library may be larger than 1MB. So for .so + * addresses we print the relative offset in back trace. + */ + rel_pc = pc; + mi = pc_to_mapinfo(map, pc, &rel_pc); + + _LOG(tfd, only_in_tombstone, + " #%02d pc %08x %s\n", stack_level, rel_pc, + mi ? mi->name : ""); + + return _URC_NO_REASON; +} + +/* Derived from __gnu_Unwind_Backtrace to use ptrace */ +/* Perform stack backtrace through unwind data. Return the level of stack it + * unwinds. + */ +int unwind_backtrace_with_ptrace(int tfd, pid_t pid, mapinfo *map, + unsigned int sp_list[], int *frame0_pc_sane, + bool at_fault) +{ + phase1_vrs saved_vrs; + _Unwind_Reason_Code code = _URC_OK; + struct pt_regs r; + int i; + int stack_level = 0; + + _Unwind_Control_Block ucb; + _Unwind_Control_Block *ucbp = &ucb; + + if(ptrace(PTRACE_GETREGS, pid, 0, &r)) return 0; + + for (i = 0; i < 16; i++) { + saved_vrs.core.r[i] = r.uregs[i]; + /* + _LOG(tfd, "r[%d] = 0x%x\n", i, saved_vrs.core.r[i]); + */ + } + + /* Set demand-save flags. */ + saved_vrs.demand_save_flags = ~(_uw) 0; + + /* + * If the app crashes because of calling the weeds, we cannot pass the PC + * to the usual unwinding code as the EXIDX mapping will fail. + * Instead, we simply print out the 0 as the top frame, and resume the + * unwinding process with the value stored in LR. + */ + if (get_eitp(saved_vrs.core.r[R_PC], pid, map, NULL) == NULL) { + *frame0_pc_sane = 0; + log_function ((_Unwind_Context *) &saved_vrs, pid, tfd, stack_level, + map, sp_list, at_fault); + saved_vrs.core.r[R_PC] = saved_vrs.core.r[R_LR]; + stack_level++; + } + + do { + mapinfo *this_map = NULL; + /* Find the entry for this routine. */ + if (get_eit_entry(ucbp, saved_vrs.core.r[R_PC], pid, map, &this_map) + != _URC_OK) { + /* Uncomment the code below to study why the unwinder failed */ +#if 0 + /* Shed more debugging info for stack unwinder improvement */ + if (this_map) { + _LOG(tfd, 1, + "Relative PC=%#x from %s not contained in EXIDX\n", + saved_vrs.core.r[R_PC] - this_map->start, this_map->name); + } + _LOG(tfd, 1, "PC=%#x SP=%#x\n", + saved_vrs.core.r[R_PC], saved_vrs.core.r[R_SP]); +#endif + code = _URC_FAILURE; + break; + } + + /* The dwarf unwinder assumes the context structure holds things + like the function and LSDA pointers. The ARM implementation + caches these in the exception header (UCB). To avoid + rewriting everything we make the virtual IP register point at + the UCB. */ + _Unwind_SetGR((_Unwind_Context *)&saved_vrs, 12, (_Unwind_Ptr) ucbp); + + /* Call log function. */ + if (log_function ((_Unwind_Context *) &saved_vrs, pid, tfd, stack_level, + map, sp_list, at_fault) != _URC_NO_REASON) { + code = _URC_FAILURE; + break; + } + stack_level++; + + /* Call the pr to decide what to do. */ + code = ((personality_routine_with_ptrace) UCB_PR_ADDR (ucbp))( + _US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND, ucbp, + (void *) &saved_vrs, pid); + /* + * In theory the unwinding process will stop when the end of stack is + * reached or there is no unwinding information for the code address. + * To add another level of guarantee that the unwinding process + * will terminate we will stop it when the STACK_CONTENT_DEPTH is reached. + */ + } while (code != _URC_END_OF_STACK && code != _URC_FAILURE && + stack_level < STACK_CONTENT_DEPTH); + return stack_level; +} + + +/* Derived version to use ptrace */ +/* Common implementation for ARM ABI defined personality routines. + ID is the index of the personality routine, other arguments are as defined + by __aeabi_unwind_cpp_pr{0,1,2}. */ + +static _Unwind_Reason_Code +unwind_pr_common_with_ptrace (_Unwind_State state, + _Unwind_Control_Block *ucbp, + _Unwind_Context *context, + int id, + pid_t pid) +{ + __gnu_unwind_state uws; + _uw *data; + int phase2_call_unexpected_after_unwind = 0; + + state &= _US_ACTION_MASK; + + data = (_uw *) ucbp->pr_cache.ehtp; + uws.data = get_remote_word(pid, data); + data++; + uws.next = data; + if (id == 0) + { + uws.data <<= 8; + uws.words_left = 0; + uws.bytes_left = 3; + } + else + { + uws.words_left = (uws.data >> 16) & 0xff; + uws.data <<= 16; + uws.bytes_left = 2; + data += uws.words_left; + } + + /* Restore the saved pointer. */ + if (state == _US_UNWIND_FRAME_RESUME) + data = (_uw *) ucbp->cleanup_cache.bitpattern[0]; + + if ((ucbp->pr_cache.additional & 1) == 0) + { + /* Process descriptors. */ + while (get_remote_word(pid, data)) { + /********************************************************************** + * The original code here seems to deal with exceptions that are not + * applicable in our toolchain, thus there is no way to test it for now. + * Instead of leaving it here and causing potential instability in + * debuggerd, we'd better punt here and leave the stack unwound. + * In the future when we discover cases where the stack should be unwound + * further but is not, we can revisit the code here. + **********************************************************************/ + return _URC_FAILURE; + } + /* Finished processing this descriptor. */ + } + + if (unwind_execute_with_ptrace (context, &uws, pid) != _URC_OK) + return _URC_FAILURE; + + if (phase2_call_unexpected_after_unwind) + { + /* Enter __cxa_unexpected as if called from the call site. */ + _Unwind_SetGR (context, R_LR, _Unwind_GetGR (context, R_PC)); + _Unwind_SetGR (context, R_PC, (_uw) &__cxa_call_unexpected); + return _URC_INSTALL_CONTEXT; + } + + return _URC_CONTINUE_UNWIND; +} + + +/* ABI defined personality routine entry points. */ + +static _Unwind_Reason_Code +unwind_cpp_pr0_with_ptrace (_Unwind_State state, + _Unwind_Control_Block *ucbp, + _Unwind_Context *context, + pid_t pid) +{ + return unwind_pr_common_with_ptrace (state, ucbp, context, 0, pid); +} + +static _Unwind_Reason_Code +unwind_cpp_pr1_with_ptrace (_Unwind_State state, + _Unwind_Control_Block *ucbp, + _Unwind_Context *context, + pid_t pid) +{ + return unwind_pr_common_with_ptrace (state, ucbp, context, 1, pid); +} + +static _Unwind_Reason_Code +unwind_cpp_pr2_with_ptrace (_Unwind_State state, + _Unwind_Control_Block *ucbp, + _Unwind_Context *context, + pid_t pid) +{ + return unwind_pr_common_with_ptrace (state, ucbp, context, 2, pid); +} diff --git a/debuggerd/utility.c b/debuggerd/utility.c new file mode 100644 index 000000000..8f3931c1f --- /dev/null +++ b/debuggerd/utility.c @@ -0,0 +1,83 @@ +/* system/debuggerd/utility.c +** +** Copyright 2008, 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 <sys/ptrace.h> +#include <sys/exec_elf.h> +#include <assert.h> +#include <string.h> +#include <errno.h> + +#include "utility.h" + +/* Get a word from pid using ptrace. The result is the return value. */ +int get_remote_word(int pid, void *src) +{ + return ptrace(PTRACE_PEEKTEXT, pid, src, NULL); +} + + +/* Handy routine to read aggregated data from pid using ptrace. The read + * values are written to the dest locations directly. + */ +void get_remote_struct(int pid, void *src, void *dst, size_t size) +{ + unsigned int i; + + for (i = 0; i+4 <= size; i+=4) { + *(int *)(dst+i) = ptrace(PTRACE_PEEKTEXT, pid, src+i, NULL); + } + + if (i < size) { + int val; + + assert((size - i) < 4); + val = ptrace(PTRACE_PEEKTEXT, pid, src+i, NULL); + while (i < size) { + ((unsigned char *)dst)[i] = val & 0xff; + i++; + val >>= 8; + } + } +} + +/* Map a pc address to the name of the containing ELF file */ +const char *map_to_name(mapinfo *mi, unsigned pc, const char* def) +{ + while(mi) { + if((pc >= mi->start) && (pc < mi->end)){ + return mi->name; + } + mi = mi->next; + } + return def; +} + +/* Find the containing map info for the pc */ +const mapinfo *pc_to_mapinfo(mapinfo *mi, unsigned pc, unsigned *rel_pc) +{ + while(mi) { + if((pc >= mi->start) && (pc < mi->end)){ + // Only calculate the relative offset for shared libraries + if (strstr(mi->name, ".so")) { + *rel_pc = pc - mi->start; + } + return mi; + } + mi = mi->next; + } + return NULL; +} diff --git a/debuggerd/utility.h b/debuggerd/utility.h new file mode 100644 index 000000000..49f59518e --- /dev/null +++ b/debuggerd/utility.h @@ -0,0 +1,56 @@ +/* system/debuggerd/utility.h +** +** Copyright 2008, 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 __utility_h +#define __utility_h + +#include <stddef.h> +#include <stdbool.h> + +#ifndef PT_ARM_EXIDX +#define PT_ARM_EXIDX 0x70000001 /* .ARM.exidx segment */ +#endif + +#define STACK_CONTENT_DEPTH 32 + +typedef struct mapinfo { + struct mapinfo *next; + unsigned start; + unsigned end; + unsigned exidx_start; + unsigned exidx_end; + char name[]; +} mapinfo; + +/* Get a word from pid using ptrace. The result is the return value. */ +extern int get_remote_word(int pid, void *src); + +/* Handy routine to read aggregated data from pid using ptrace. The read + * values are written to the dest locations directly. + */ +extern void get_remote_struct(int pid, void *src, void *dst, size_t size); + +/* Find the containing map for the pc */ +const mapinfo *pc_to_mapinfo (mapinfo *mi, unsigned pc, unsigned *rel_pc); + +/* Map a pc address to the name of the containing ELF file */ +const char *map_to_name(mapinfo *mi, unsigned pc, const char* def); + +/* Log information onto the tombstone */ +extern void _LOG(int tfd, bool in_tombstone_only, const char *fmt, ...); + +#endif |