diff options
| -rw-r--r-- | debuggerd/arm/machine.c | 2 | ||||
| -rw-r--r-- | debuggerd/debuggerd.c | 521 | ||||
| -rw-r--r-- | libcorkscrew/arch-arm/backtrace-arm.c | 40 |
3 files changed, 342 insertions, 221 deletions
diff --git a/debuggerd/arm/machine.c b/debuggerd/arm/machine.c index 891b1efa..d941684f 100644 --- a/debuggerd/arm/machine.c +++ b/debuggerd/arm/machine.c @@ -121,7 +121,7 @@ void dump_registers(ptrace_context_t* context __attribute((unused)), _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); + _LOG(tfd, only_in_tombstone, " scr %08lx\n", vfp_regs.fpscr); #endif } diff --git a/debuggerd/debuggerd.c b/debuggerd/debuggerd.c index 15994392..5a180f1a 100644 --- a/debuggerd/debuggerd.c +++ b/debuggerd/debuggerd.c @@ -28,6 +28,7 @@ #include <sys/wait.h> #include <sys/exec_elf.h> #include <sys/stat.h> +#include <sys/poll.h> #include <cutils/sockets.h> #include <cutils/logd.h> @@ -64,6 +65,7 @@ static const char *get_signame(int sig) case SIGFPE: return "SIGFPE"; case SIGSEGV: return "SIGSEGV"; case SIGSTKFLT: return "SIGSTKFLT"; + case SIGSTOP: return "SIGSTOP"; default: return "?"; } } @@ -156,49 +158,50 @@ static void dump_crash_banner(int tfd, pid_t pid, pid_t tid, int sig) /* Return true if some thread is not detached cleanly */ static bool dump_sibling_thread_report(ptrace_context_t* context, - int tfd, pid_t pid, pid_t tid) -{ - char task_path[1024]; - - sprintf(task_path, "/proc/%d/task", pid); - DIR *d; - struct dirent *de; - int need_cleanup = 0; + int tfd, pid_t pid, pid_t tid) { + char task_path[64]; + snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid); - d = opendir(task_path); + DIR* 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; } + + bool detach_failed = false; + struct dirent *de; while ((de = readdir(d)) != NULL) { pid_t new_tid; /* Ignore "." and ".." */ - if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) + 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) + if (new_tid == tid) { continue; + } /* Skip this thread if cannot ptrace it */ - if (ptrace(PTRACE_ATTACH, new_tid, 0, 0) < 0) + if (ptrace(PTRACE_ATTACH, new_tid, 0, 0) < 0) { continue; + } - _LOG(tfd, true, - "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n"); + _LOG(tfd, true, "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n"); _LOG(tfd, true, "pid: %d, tid: %d\n", pid, new_tid); dump_thread(context, tfd, new_tid, false); if (ptrace(PTRACE_DETACH, new_tid, 0, 0) != 0) { - XLOG("detach of tid %d failed: %s\n", new_tid, strerror(errno)); - need_cleanup = 1; + LOG("ptrace detach from %d failed: %s\n", new_tid, strerror(errno)); + detach_failed = true; } } - closedir(d); - return need_cleanup != 0; + closedir(d); + return detach_failed; } /* @@ -355,7 +358,6 @@ static bool dump_crash(int tfd, pid_t pid, pid_t tid, int signal, char value[PROPERTY_VALUE_MAX]; property_get("ro.debuggable", value, "0"); bool wantLogs = (value[0] == '1'); - bool need_cleanup = false; dump_crash_banner(tfd, pid, tid, signal); @@ -367,8 +369,9 @@ static bool dump_crash(int tfd, pid_t pid, pid_t tid, int signal, dump_logs(tfd, pid, true); } + bool detach_failed = false; if (dump_sibling_threads) { - need_cleanup = dump_sibling_thread_report(context, tfd, pid, tid); + detach_failed = dump_sibling_thread_report(context, tfd, pid, tid); } free_ptrace_context(context); @@ -376,7 +379,7 @@ static bool dump_crash(int tfd, pid_t pid, pid_t tid, int signal, if (wantLogs) { dump_logs(tfd, pid, false); } - return need_cleanup; + return detach_failed; } #define MAX_TOMBSTONES 10 @@ -443,20 +446,18 @@ static int find_and_open_tombstone(void) static bool engrave_tombstone(pid_t pid, pid_t tid, int signal, bool dump_sibling_threads) { - 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; + int fd = find_and_open_tombstone(); + if (fd < 0) { + return false; + } - need_cleanup = dump_crash(fd, pid, tid, signal, dump_sibling_threads); + bool detach_failed = dump_crash(fd, pid, tid, signal, dump_sibling_threads); close(fd); - return need_cleanup; + return detach_failed; } static int @@ -504,21 +505,21 @@ void disable_debug_led(void) write_string("/sys/class/leds/left/cadence", "0,0"); } -static void wait_for_user_action(pid_t tid, struct ucred* cr) -{ - (void)tid; +static void wait_for_user_action(pid_t pid) { /* 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" + "* attach gdbserver for a gdb connection on port 5039\n" + "* and start gdbclient:\n" "*\n" - "* adb shell gdbserver :5039 --attach %d &\n" + "* gdbclient app_process :5039 %d\n" "*\n" - "* Press HOME key to let the process continue crashing.\n" + "* Wait for gdb to start, then press HOME or VOLUME DOWN key\n" + "* to let the process continue crashing.\n" "********************************************************\n", - cr->pid, cr->pid); + pid, pid); - /* wait for HOME key (TODO: something useful for devices w/o HOME key) */ + /* wait for HOME or VOLUME DOWN key */ if (init_getevent() == 0) { int ms = 1200 / 10; int dit = 1; @@ -531,15 +532,18 @@ static void wait_for_user_action(pid_t tid, struct ucred* cr) }; size_t s = 0; struct input_event e; - int home = 0; + bool done = false; 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; + if (e.type == EV_KEY + && (e.code == KEY_HOME || e.code == KEY_VOLUMEDOWN) + && e.value == 0) { + done = true; + } } else if (res == 1) { if (++s >= sizeof(codes)/sizeof(*codes)) s = 0; @@ -549,209 +553,281 @@ static void wait_for_user_action(pid_t tid, struct ucred* cr) disable_debug_led(); } } - } while (!home); + } while (!done); uninit_getevent(); } /* don't forget to turn debug led off */ disable_debug_led(); + LOG("debuggerd resuming process %d", pid); +} - /* close filedescriptor */ - LOG("debuggerd resuming process %d", cr->pid); - } +static int get_process_info(pid_t tid, pid_t* out_pid, uid_t* out_uid, uid_t* out_gid) { + char path[64]; + snprintf(path, sizeof(path), "/proc/%d/status", tid); -static void handle_crashing_process(int fd) -{ - char buf[64]; - struct stat s; - pid_t tid; - struct ucred cr; - int n, len, status; - int tid_attach_status = -1; - unsigned retry = 30; - bool need_cleanup = false; + FILE* fp = fopen(path, "r"); + if (!fp) { + return -1; + } - XLOG("handle_crashing_process(%d)\n", fd); + int fields = 0; + char line[1024]; + while (fgets(line, sizeof(line), fp)) { + size_t len = strlen(line); + if (len > 6 && !memcmp(line, "Tgid:\t", 6)) { + *out_pid = atoi(line + 6); + fields |= 1; + } else if (len > 5 && !memcmp(line, "Uid:\t", 5)) { + *out_uid = atoi(line + 5); + fields |= 2; + } else if (len > 5 && !memcmp(line, "Gid:\t", 5)) { + *out_gid = atoi(line + 5); + fields |= 4; + } + } + fclose(fp); + return fields == 7 ? 0 : -1; +} - char value[PROPERTY_VALUE_MAX]; - property_get("debug.db.uid", value, "-1"); - int debug_uid = atoi(value); +static int wait_for_signal(pid_t tid, int* total_sleep_time_usec) { + const int sleep_time_usec = 200000; /* 0.2 seconds */ + const int max_total_sleep_usec = 3000000; /* 3 seconds */ + for (;;) { + int status; + pid_t n = waitpid(tid, &status, __WALL | WNOHANG); + if (n < 0) { + if(errno == EAGAIN) continue; + LOG("waitpid failed: %s\n", strerror(errno)); + return -1; + } else if (n > 0) { + XLOG("waitpid: n=%d status=%08x\n", n, status); + if (WIFSTOPPED(status)) { + return WSTOPSIG(status); + } else { + LOG("unexpected waitpid response: n=%d, status=%08x\n", n, status); + return -1; + } + } + + if (*total_sleep_time_usec > max_total_sleep_usec) { + LOG("timed out waiting for tid=%d to die\n", tid); + return -1; + } - len = sizeof(cr); - n = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len); - if(n != 0) { + /* not ready yet */ + XLOG("not ready yet\n"); + usleep(sleep_time_usec); + *total_sleep_time_usec += sleep_time_usec; + } +} + +enum { + REQUEST_TYPE_CRASH, + REQUEST_TYPE_DUMP, +}; + +typedef struct { + int type; + pid_t pid, tid; + uid_t uid, gid; +} request_t; + +static int read_request(int fd, request_t* out_request) { + struct ucred cr; + int len = sizeof(cr); + int status = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len); + if (status != 0) { LOG("cannot get credentials\n"); - goto done; + return -1; } XLOG("reading tid\n"); fcntl(fd, F_SETFL, O_NONBLOCK); - while((n = read(fd, &tid, sizeof(pid_t))) != sizeof(pid_t)) { - 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; + + struct pollfd pollfds[1]; + pollfds[0].fd = fd; + pollfds[0].events = POLLIN; + pollfds[0].revents = 0; + status = TEMP_FAILURE_RETRY(poll(pollfds, 1, 3000)); + if (status != 1) { + LOG("timed out reading tid\n"); + return -1; } - snprintf(buf, sizeof 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; + status = TEMP_FAILURE_RETRY(read(fd, &out_request->tid, sizeof(pid_t))); + if (status < 0) { + LOG("read failure? %s\n", strerror(errno)); + return -1; + } + if (status != sizeof(pid_t)) { + LOG("invalid crash request of size %d\n", status); + return -1; } - XLOG("BOOM: pid=%d uid=%d gid=%d tid=%d\n", cr.pid, cr.uid, cr.gid, tid); + if (out_request->tid < 0 && cr.uid == 0) { + /* Root can ask us to attach to any process and dump it explicitly. */ + out_request->type = REQUEST_TYPE_DUMP; + out_request->tid = -out_request->tid; + status = get_process_info(out_request->tid, &out_request->pid, + &out_request->uid, &out_request->gid); + if (status < 0) { + LOG("tid %d does not exist. ignoring explicit dump request\n", + out_request->tid); + return -1; + } + return 0; + } - /* - * 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. - */ - bool dump_sibling_threads = (signed)cr.pid > debug_uid; - - /* Note that at this point, the target thread's signal handler - * is blocked in a read() call. This gives us the time to PTRACE_ATTACH - * to it before it has a chance to really fault. - * - * The PTRACE_ATTACH sends a SIGSTOP to the target process, but it - * won't necessarily have stopped by the time ptrace() returns. (We - * currently assume it does.) We write to the file descriptor to - * ensure that it can run as soon as we call PTRACE_CONT below. - * See details in bionic/libc/linker/debugger.c, in function - * debugger_signal_handler(). - */ - tid_attach_status = ptrace(PTRACE_ATTACH, tid, 0, 0); - int ptrace_error = errno; + /* Ensure that the tid reported by the crashing process is valid. */ + out_request->type = REQUEST_TYPE_CRASH; + out_request->pid = cr.pid; + out_request->uid = cr.uid; + out_request->gid = cr.gid; - if (TEMP_FAILURE_RETRY(write(fd, &tid, 1)) != 1) { - XLOG("failed responding to client: %s\n", - strerror(errno)); - goto done; + char buf[64]; + struct stat s; + snprintf(buf, sizeof buf, "/proc/%d/task/%d", out_request->pid, out_request->tid); + if(stat(buf, &s)) { + LOG("tid %d does not exist in pid %d. ignoring debug request\n", + out_request->tid, out_request->pid); + return -1; } + return 0; +} - if(tid_attach_status < 0) { - LOG("ptrace attach failed: %s\n", strerror(ptrace_error)); - goto done; +static bool should_attach_gdb(request_t* request) { + if (request->type == REQUEST_TYPE_CRASH) { + char value[PROPERTY_VALUE_MAX]; + property_get("debug.db.uid", value, "-1"); + int debug_uid = atoi(value); + return debug_uid >= 0 && request->uid <= (uid_t)debug_uid; } + return false; +} - close(fd); - fd = -1; +static void handle_request(int fd) { + XLOG("handle_request(%d)\n", fd); - const int sleep_time_usec = 200000; /* 0.2 seconds */ - const int max_total_sleep_usec = 3000000; /* 3 seconds */ - int loop_limit = max_total_sleep_usec / sleep_time_usec; - for(;;) { - if (loop_limit-- == 0) { - LOG("timed out waiting for pid=%d tid=%d uid=%d to die\n", - cr.pid, tid, cr.uid); - goto done; - } - n = waitpid(tid, &status, __WALL | WNOHANG); + request_t request; + int status = read_request(fd, &request); + if (!status) { + XLOG("BOOM: pid=%d uid=%d gid=%d tid=%d\n", pid, uid, gid, tid); - if (n == 0) { - /* not ready yet */ - XLOG("not ready yet\n"); - usleep(sleep_time_usec); - continue; - } + /* At this point, the thread that made the request is blocked in + * a read() call. If the thread has crashed, then this gives us + * time to PTRACE_ATTACH to it before it has a chance to really fault. + * + * The PTRACE_ATTACH sends a SIGSTOP to the target process, but it + * won't necessarily have stopped by the time ptrace() returns. (We + * currently assume it does.) We write to the file descriptor to + * ensure that it can run as soon as we call PTRACE_CONT below. + * See details in bionic/libc/linker/debugger.c, in function + * debugger_signal_handler(). + */ + if (ptrace(PTRACE_ATTACH, request.tid, 0, 0)) { + LOG("ptrace attach failed: %s\n", strerror(errno)); + } else { + bool detach_failed = false; + bool attach_gdb = should_attach_gdb(&request); + char response = 0; + if (TEMP_FAILURE_RETRY(write(fd, &response, 1)) != 1) { + LOG("failed responding to client: %s\n", strerror(errno)); + } else { + close(fd); + fd = -1; + + int total_sleep_time_usec = 0; + for (;;) { + int signal = wait_for_signal(request.tid, &total_sleep_time_usec); + if (signal < 0) { + break; + } + + switch (signal) { + case SIGSTOP: + if (request.type == REQUEST_TYPE_DUMP) { + XLOG("stopped -- dumping\n"); + detach_failed = engrave_tombstone(request.pid, request.tid, + signal, true); + } else { + XLOG("stopped -- continuing\n"); + status = ptrace(PTRACE_CONT, request.tid, 0, 0); + if (status) { + LOG("ptrace continue failed: %s\n", strerror(errno)); + } + continue; /* loop again */ + } + break; + + case SIGILL: + case SIGABRT: + case SIGBUS: + case SIGFPE: + case SIGSEGV: + case SIGSTKFLT: { + XLOG("stopped -- fatal signal\n"); + /* don't dump sibling threads when attaching to GDB because it + * makes the process less reliable, apparently... */ + detach_failed = engrave_tombstone(request.pid, request.tid, + signal, !attach_gdb); + break; + } + + default: + XLOG("stopped -- unexpected signal\n"); + LOG("process stopped due to unexpected signal %d\n", signal); + break; + } + break; + } + } - if(n < 0) { - if(errno == EAGAIN) continue; - LOG("waitpid failed: %s\n", strerror(errno)); - goto done; - } + XLOG("detaching\n"); + if (attach_gdb) { + /* stop the process so we can debug */ + kill(request.pid, SIGSTOP); - 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; + /* detach so we can attach gdbserver */ + if (ptrace(PTRACE_DETACH, request.tid, 0, 0)) { + LOG("ptrace detach from %d failed: %s\n", request.tid, strerror(errno)); + detach_failed = true; } - 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, n, - dump_sibling_threads); - kill(tid, SIGSTOP); - goto done; + /* + * 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) + */ + wait_for_user_action(request.pid); + } else { + /* just detach */ + if (ptrace(PTRACE_DETACH, request.tid, 0, 0)) { + LOG("ptrace detach from %d failed: %s\n", request.tid, strerror(errno)); + detach_failed = true; + } } - default: - XLOG("stopped -- unexpected signal\n"); - goto done; + /* resume stopped process (so it can crash in peace). */ + kill(request.pid, SIGCONT); + + /* If we didn't successfully detach, we're still the parent, and the + * actual parent won't receive a death notification via wait(2). At this point + * there's not much we can do about that. */ + if (detach_failed) { + LOG("debuggerd committing suicide to free the zombie!\n"); + kill(getpid(), SIGKILL); } - } 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). If we didn't - * successfully detach, we're still the parent, and the actual parent - * won't receive a death notification via wait(2). At this point - * there's not much we can do about that. - */ - kill(cr.pid, SIGCONT); - - if (need_cleanup) { - LOG("debuggerd committing suicide to free the zombie!\n"); - kill(getpid(), SIGKILL); + if (fd >= 0) { + close(fd); } - - if(fd != -1) close(fd); } - -int main() -{ +static int do_server() { int s; struct sigaction act; int logsocket = -1; @@ -784,7 +860,7 @@ int main() s = socket_local_server("android:debuggerd", ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM); - if(s < 0) return -1; + if(s < 0) return 1; fcntl(s, F_SETFD, FD_CLOEXEC); LOG("debuggerd: " __DATE__ " " __TIME__ "\n"); @@ -804,7 +880,42 @@ int main() fcntl(fd, F_SETFD, FD_CLOEXEC); - handle_crashing_process(fd); + handle_request(fd); + } + return 0; +} + +static int do_explicit_dump(pid_t tid) { + fprintf(stdout, "Sending request to dump task %d.\n", tid); + + int fd = socket_local_client("android:debuggerd", + ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM); + if (fd < 0) { + fputs("Error opening local socket to debuggerd.\n", stderr); + return 1; + } + + pid_t request = -tid; + write(fd, &request, sizeof(pid_t)); + if (read(fd, &request, 1) != 1) { + /* did not get expected reply, debuggerd must have closed the socket */ + fputs("Error sending request. Did not receive reply from debuggerd.\n", stderr); } + close(fd); return 0; } + +int main(int argc, char** argv) { + if (argc == 2) { + pid_t tid = atoi(argv[1]); + if (!tid) { + fputs("Usage: [<tid>]\n" + "\n" + "If tid specified, sends a request to debuggerd to dump that task.\n" + "Otherwise, starts the debuggerd server.\n", stderr); + return 1; + } + return do_explicit_dump(tid); + } + return do_server(); +} diff --git a/libcorkscrew/arch-arm/backtrace-arm.c b/libcorkscrew/arch-arm/backtrace-arm.c index f2db6429..93144f03 100644 --- a/libcorkscrew/arch-arm/backtrace-arm.c +++ b/libcorkscrew/arch-arm/backtrace-arm.c @@ -25,23 +25,13 @@ * the exception handling table of each function, sorted by program * counter address. * - * When the executable is statically linked, the EXIDX section can be - * accessed by querying the values of the __exidx_start and __exidx_end - * symbols. That said, this library is currently only compiled as - * a dynamic library, so we will not trouble ourselves with statically - * linked executables any further. - * - * When the Bionic dynamic linker is used, it exports a function called - * dl_unwind_find_exidx that obtains the EXIDX section for a given - * absolute program counter address. - * * This implementation also supports unwinding other processes via ptrace(). * In that case, the EXIDX section is found by reading the ELF section table * structures using ptrace(). * * Because the tables are used for exception handling, it can happen that * a given function will not have an exception handling table. In particular, - * exceptions are assumes to only ever be thrown at call sites. Therefore, + * exceptions are assumed to only ever be thrown at call sites. Therefore, * by definition leaf functions will not have exception handling tables. * This may make unwinding impossible in some cases although we can still get * some idea of the call stack by examining the PC and LR registers. @@ -100,9 +90,29 @@ static const int R_PC = 15; /* Special EXIDX value that indicates that a frame cannot be unwound. */ static const uint32_t EXIDX_CANTUNWIND = 1; -/* The function exported by the Bionic linker to find the EXIDX - * table for a given program counter address. */ -extern uintptr_t dl_unwind_find_exidx(uintptr_t pc, size_t* out_exidx_size); +/* Get the EXIDX section start and size for the module that contains a + * given program counter address. + * + * When the executable is statically linked, the EXIDX section can be + * accessed by querying the values of the __exidx_start and __exidx_end + * symbols. + * + * When the executable is dynamically linked, the linker exports a function + * called dl_unwind_find_exidx that obtains the EXIDX section for a given + * absolute program counter address. + * + * Bionic exports a helpful function called __gnu_Unwind_Find_exidx that + * handles both cases, so we use that here. + */ +typedef long unsigned int* _Unwind_Ptr; +extern _Unwind_Ptr __gnu_Unwind_Find_exidx(_Unwind_Ptr pc, int *pcount); + +static uintptr_t find_exidx(uintptr_t pc, size_t* out_exidx_size) { + int count; + uintptr_t start = (uintptr_t)__gnu_Unwind_Find_exidx((_Unwind_Ptr)pc, &count); + *out_exidx_size = count; + return start; +} /* Transforms a 31-bit place-relative offset to an absolute address. * We assume the most significant bit is clear. */ @@ -115,7 +125,7 @@ static uintptr_t get_exception_handler( uintptr_t exidx_start; size_t exidx_size; if (tid < 0) { - exidx_start = dl_unwind_find_exidx(pc, &exidx_size); + exidx_start = find_exidx(pc, &exidx_size); } else { const map_info_t* mi = find_map_info(context->map_info_list, pc); if (mi && mi->data) { |
