M7350v1_en_gpl

This commit is contained in:
T
2024-09-09 08:52:07 +00:00
commit f9cc65cfda
65988 changed files with 26357421 additions and 0 deletions

View File

@ -0,0 +1,49 @@
# 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
ifeq ($(ARCH_ARM_HAVE_VFP),true)
LOCAL_CFLAGS += -DWITH_VFP
endif # ARCH_ARM_HAVE_VFP
ifeq ($(ARCH_ARM_HAVE_VFP_D32),true)
LOCAL_CFLAGS += -DWITH_VFP_D32
endif # ARCH_ARM_HAVE_VFP_D32
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)
ifeq ($(ARCH_ARM_HAVE_VFP),true)
include $(CLEAR_VARS)
LOCAL_CFLAGS += -DWITH_VFP
ifeq ($(ARCH_ARM_HAVE_VFP_D32),true)
LOCAL_CFLAGS += -DWITH_VFP_D32
endif # ARCH_ARM_HAVE_VFP_D32
LOCAL_SRC_FILES := vfp-crasher.c vfp.S
LOCAL_MODULE := vfp-crasher
LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
LOCAL_MODULE_TAGS := eng
LOCAL_SHARED_LIBRARIES := libcutils libc
include $(BUILD_EXECUTABLE)
endif # ARCH_ARM_HAVE_VFP == true
endif # TARGET_ARCH == arm

View File

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

View File

@ -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();
}

View File

@ -0,0 +1,30 @@
.globl crash1
.type crash1, %function
.globl crashnostack
.type crashnostack, %function
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 .

View File

@ -0,0 +1,906 @@
/* 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"
#ifdef WITH_VFP
#ifdef WITH_VFP_D32
#define NUM_VFP_REGS 32
#else
#define NUM_VFP_REGS 16
#endif
#endif
/* 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 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[],
bool at_fault)
{
unsigned int sp, pc, p, end, data;
struct pt_regs r;
int sp_depth;
bool only_in_tombstone = !at_fault;
char code_buffer[80];
if(ptrace(PTRACE_GETREGS, pid, 0, &r)) return;
sp = r.ARM_sp;
pc = r.ARM_pc;
_LOG(tfd, only_in_tombstone, "\ncode around pc:\n");
end = p = pc & ~3;
p -= 32;
end += 32;
/* Dump the code around PC as:
* addr contents
* 00008d34 fffffcd0 4c0eb530 b0934a0e 1c05447c
* 00008d44 f7ff18a0 490ced94 68035860 d0012b00
*/
while (p <= end) {
int i;
sprintf(code_buffer, "%08x ", p);
for (i = 0; i < 4; i++) {
data = ptrace(PTRACE_PEEKTEXT, pid, (void*)p, NULL);
sprintf(code_buffer + strlen(code_buffer), "%08x ", data);
p += 4;
}
_LOG(tfd, only_in_tombstone, "%s\n", code_buffer);
}
if ((unsigned) r.ARM_lr != pc) {
_LOG(tfd, only_in_tombstone, "\ncode around lr:\n");
end = p = r.ARM_lr & ~3;
p -= 32;
end += 32;
/* Dump the code around LR as:
* addr contents
* 00008d34 fffffcd0 4c0eb530 b0934a0e 1c05447c
* 00008d44 f7ff18a0 490ced94 68035860 d0012b00
*/
while (p <= end) {
int i;
sprintf(code_buffer, "%08x ", p);
for (i = 0; i < 4; i++) {
data = ptrace(PTRACE_PEEKTEXT, pid, (void*)p, NULL);
sprintf(code_buffer + strlen(code_buffer), "%08x ", data);
p += 4;
}
_LOG(tfd, only_in_tombstone, "%s\n", code_buffer);
}
}
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, "\nstack:\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);
#ifdef WITH_VFP
struct user_vfp vfp_regs;
int i;
if(ptrace(PTRACE_GETVFPREGS, pid, 0, &vfp_regs)) {
_LOG(tfd, only_in_tombstone,
"cannot get registers: %s\n", strerror(errno));
return;
}
for (i = 0; i < NUM_VFP_REGS; i += 2) {
_LOG(tfd, only_in_tombstone,
" d%-2d %016llx d%-2d %016llx\n",
i, vfp_regs.fpregs[i], i+1, vfp_regs.fpregs[i+1]);
}
_LOG(tfd, only_in_tombstone, " scr %08lx\n\n", vfp_regs.fpscr);
#endif
}
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 "?";
}
}
const char *get_sigcode(int signo, int code)
{
switch (signo) {
case SIGILL:
switch (code) {
case ILL_ILLOPC: return "ILL_ILLOPC";
case ILL_ILLOPN: return "ILL_ILLOPN";
case ILL_ILLADR: return "ILL_ILLADR";
case ILL_ILLTRP: return "ILL_ILLTRP";
case ILL_PRVOPC: return "ILL_PRVOPC";
case ILL_PRVREG: return "ILL_PRVREG";
case ILL_COPROC: return "ILL_COPROC";
case ILL_BADSTK: return "ILL_BADSTK";
}
break;
case SIGBUS:
switch (code) {
case BUS_ADRALN: return "BUS_ADRALN";
case BUS_ADRERR: return "BUS_ADRERR";
case BUS_OBJERR: return "BUS_OBJERR";
}
break;
case SIGFPE:
switch (code) {
case FPE_INTDIV: return "FPE_INTDIV";
case FPE_INTOVF: return "FPE_INTOVF";
case FPE_FLTDIV: return "FPE_FLTDIV";
case FPE_FLTOVF: return "FPE_FLTOVF";
case FPE_FLTUND: return "FPE_FLTUND";
case FPE_FLTRES: return "FPE_FLTRES";
case FPE_FLTINV: return "FPE_FLTINV";
case FPE_FLTSUB: return "FPE_FLTSUB";
}
break;
case SIGSEGV:
switch (code) {
case SEGV_MAPERR: return "SEGV_MAPERR";
case SEGV_ACCERR: return "SEGV_ACCERR";
}
break;
}
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), code %d (%s), fault addr %08x\n",
sig, get_signame(sig),
si.si_code, get_sigcode(sig, si.si_code),
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, (char *) (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, at_fault);
while(milist) {
mapinfo *next = milist->next;
free(milist);
milist = next;
}
}
#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 has been suspended while crashing. To\n"
"* attach gdbserver for a gdb connection on port 5039:\n"
"*\n"
"* adb shell gdbserver :5039 --attach %d &\n"
"*\n"
"* Press HOME key to let the process continue crashing.\n"
"********************************************************\n",
cr->pid, cr->pid);
/* wait for HOME key (TODO: something useful for devices w/o 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. ignoring 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 s;
struct sigaction act;
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;
}

View File

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

View File

@ -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,
&reg);
_Unwind_VRS_Set (context, _UVRSC_CORE, R_PC, _UVRSD_UINT32,
&reg);
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, &reg);
if (op & 0x40)
reg -= offset;
else
reg += offset;
_Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, &reg);
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, &reg);
_Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, &reg);
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,
&reg);
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,
&reg);
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;
}

View File

@ -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);
}

View File

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

View File

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

View File

@ -0,0 +1,7 @@
int main()
{
extern void crash(void);
crash();
return 0;
}

View File

@ -0,0 +1,48 @@
.text
.align 2
.global crash
.type crash, %function
crash:
#if defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_6__)
//VFPv3 was the first to have the fconstd instruction.
#else //// defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_6__)
fconstd d0, #0
fconstd d1, #1
fconstd d2, #2
fconstd d3, #3
fconstd d4, #4
fconstd d5, #5
fconstd d6, #6
fconstd d7, #7
fconstd d8, #8
fconstd d9, #9
fconstd d10, #10
fconstd d11, #11
fconstd d12, #12
fconstd d13, #13
fconstd d14, #14
fconstd d15, #15
#ifdef WITH_VFP_D32
fconstd d16, #16
fconstd d17, #17
fconstd d18, #18
fconstd d19, #19
fconstd d20, #20
fconstd d21, #21
fconstd d22, #22
fconstd d23, #23
fconstd d24, #24
fconstd d25, #25
fconstd d26, #26
fconstd d27, #27
fconstd d28, #28
fconstd d29, #29
fconstd d30, #30
fconstd d31, #31
#endif
#endif // defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_6__)
mov r0, #0
str r0, [r0]
bx lr