Merge "Fix dhcp comments"
diff --git a/charger/charger.c b/charger/charger.c
index 03280bf..abf5517 100644
--- a/charger/charger.c
+++ b/charger/charger.c
@@ -87,6 +87,7 @@
     const char *name;
     int disp_time;
     int min_capacity;
+    bool level_only;
 
     gr_surface surface;
 };
@@ -157,6 +158,7 @@
         .name = "charger/battery_4",
         .disp_time = 750,
         .min_capacity = 80,
+        .level_only = true,
     },
     {
         .name = "charger/battery_5",
@@ -735,7 +737,14 @@
      * if necessary, advance cycle cntr, and reset frame cntr
      */
     batt_anim->cur_frame++;
-    if (batt_anim->cur_frame == batt_anim->num_frames) {
+
+    /* if the frame is used for level-only, that is only show it when it's
+     * the current level, skip it during the animation.
+     */
+    while (batt_anim->cur_frame < batt_anim->num_frames &&
+           batt_anim->frames[batt_anim->cur_frame].level_only)
+        batt_anim->cur_frame++;
+    if (batt_anim->cur_frame >= batt_anim->num_frames) {
         batt_anim->cur_cycle++;
         batt_anim->cur_frame = 0;
 
diff --git a/debuggerd/arm/machine.c b/debuggerd/arm/machine.c
index d941684..ca45c9b 100644
--- a/debuggerd/arm/machine.c
+++ b/debuggerd/arm/machine.c
@@ -87,7 +87,7 @@
     }
 }
 
-void dump_registers(ptrace_context_t* context __attribute((unused)),
+void dump_registers(const ptrace_context_t* context __attribute((unused)),
         int tfd, pid_t tid, bool at_fault)
 {
     struct pt_regs r;
@@ -125,7 +125,7 @@
 #endif
 }
 
-void dump_thread(ptrace_context_t* context, int tfd, pid_t tid, bool at_fault) {
+void dump_thread(const ptrace_context_t* context, int tfd, pid_t tid, bool at_fault) {
     dump_registers(context, tfd, tid, at_fault);
 
     dump_backtrace_and_stack(context, tfd, tid, at_fault);
diff --git a/debuggerd/debuggerd.c b/debuggerd/debuggerd.c
index 5a180f1..0f05bfd 100644
--- a/debuggerd/debuggerd.c
+++ b/debuggerd/debuggerd.c
@@ -114,12 +114,12 @@
     return "?";
 }
 
-static void dump_fault_addr(int tfd, pid_t pid, int sig)
+static void dump_fault_addr(int tfd, pid_t tid, int sig)
 {
     siginfo_t si;
 
     memset(&si, 0, sizeof(si));
-    if(ptrace(PTRACE_GETSIGINFO, pid, 0, &si)){
+    if(ptrace(PTRACE_GETSIGINFO, tid, 0, &si)){
         _LOG(tfd, false, "cannot get siginfo: %s\n", strerror(errno));
     } else if (signal_has_address(sig)) {
         _LOG(tfd, false, "signal %d (%s), code %d (%s), fault addr %08x\n",
@@ -157,7 +157,7 @@
 }
 
 /* Return true if some thread is not detached cleanly */
-static bool dump_sibling_thread_report(ptrace_context_t* context,
+static bool dump_sibling_thread_report(const ptrace_context_t* context,
         int tfd, pid_t pid, pid_t tid) {
     char task_path[64];
     snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid);
@@ -361,7 +361,7 @@
 
     dump_crash_banner(tfd, pid, tid, signal);
 
-    ptrace_context_t* context = load_ptrace_context(pid);
+    ptrace_context_t* context = load_ptrace_context(tid);
 
     dump_thread(context, tfd, tid, true);
 
@@ -395,13 +395,13 @@
  * 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.
+ *
+ * Returns the path of the tombstone file, allocated using malloc().  Caller must free() it.
  */
-static int find_and_open_tombstone(void)
+static char* find_and_open_tombstone(int* fd)
 {
     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
@@ -413,7 +413,9 @@
      * 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++) {
+    char path[128];
+    int oldest = 0;
+    for (int i = 0; i < MAX_TOMBSTONES; i++) {
         snprintf(path, sizeof(path), TOMBSTONE_DIR"/tombstone_%02d", i);
 
         if (!stat(path, &sb)) {
@@ -426,38 +428,43 @@
         if (errno != ENOENT)
             continue;
 
-        fd = open(path, O_CREAT | O_EXCL | O_WRONLY, 0600);
-        if (fd < 0)
+        *fd = open(path, O_CREAT | O_EXCL | O_WRONLY, 0600);
+        if (*fd < 0)
             continue;	/* raced ? */
 
-        fchown(fd, AID_SYSTEM, AID_SYSTEM);
-        return fd;
+        fchown(*fd, AID_SYSTEM, AID_SYSTEM);
+        return strdup(path);
     }
 
     /* 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;
+    *fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
+    if (*fd < 0) {
+        LOG("failed to open tombstone file '%s': %s\n", path, strerror(errno));
+        return NULL;
+    }
+    fchown(*fd, AID_SYSTEM, AID_SYSTEM);
+    return strdup(path);
 }
 
 /* Return true if some thread is not detached cleanly */
-static bool engrave_tombstone(pid_t pid, pid_t tid, int signal,
-        bool dump_sibling_threads)
+static char* engrave_tombstone(pid_t pid, pid_t tid, int signal, bool dump_sibling_threads,
+        bool* detach_failed)
 {
     mkdir(TOMBSTONE_DIR, 0755);
     chown(TOMBSTONE_DIR, AID_SYSTEM, AID_SYSTEM);
 
-    int fd = find_and_open_tombstone();
-    if (fd < 0) {
-        return false;
+    int fd;
+    char* path = find_and_open_tombstone(&fd);
+    if (!path) {
+        *detach_failed = false;
+        return NULL;
     }
 
-    bool detach_failed = dump_crash(fd, pid, tid, signal, dump_sibling_threads);
+    *detach_failed = dump_crash(fd, pid, tid, signal, dump_sibling_threads);
 
     close(fd);
-    return detach_failed;
+    return path;
 }
 
 static int
@@ -734,8 +741,12 @@
             if (TEMP_FAILURE_RETRY(write(fd, &response, 1)) != 1) {
                 LOG("failed responding to client: %s\n", strerror(errno));
             } else {
-                close(fd);
-                fd = -1;
+                char* tombstone_path = NULL;
+
+                if (request.type != REQUEST_TYPE_DUMP) {
+                    close(fd);
+                    fd = -1;
+                }
 
                 int total_sleep_time_usec = 0;
                 for (;;) {
@@ -748,8 +759,8 @@
                     case SIGSTOP:
                         if (request.type == REQUEST_TYPE_DUMP) {
                             XLOG("stopped -- dumping\n");
-                            detach_failed = engrave_tombstone(request.pid, request.tid,
-                                    signal, true);
+                            tombstone_path = engrave_tombstone(request.pid, request.tid,
+                                    signal, true, &detach_failed);
                         } else {
                             XLOG("stopped -- continuing\n");
                             status = ptrace(PTRACE_CONT, request.tid, 0, 0);
@@ -769,8 +780,8 @@
                         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);
+                        tombstone_path = engrave_tombstone(request.pid, request.tid,
+                                signal, !attach_gdb, &detach_failed);
                         break;
                     }
 
@@ -781,6 +792,15 @@
                     }
                     break;
                 }
+
+                if (request.type == REQUEST_TYPE_DUMP) {
+                    if (tombstone_path) {
+                        write(fd, tombstone_path, strlen(tombstone_path));
+                    }
+                    close(fd);
+                    fd = -1;
+                }
+                free(tombstone_path);
             }
 
             XLOG("detaching\n");
@@ -900,7 +920,17 @@
     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);
+    } else {
+        char tombstone_path[PATH_MAX];
+        ssize_t n = read(fd, &tombstone_path, sizeof(tombstone_path) - 1);
+        if (n <= 0) {
+            fputs("Error dumping process.  Check log for details.\n", stderr);
+        } else {
+            tombstone_path[n] = '\0';
+            fprintf(stderr, "Tombstone written to: %s\n", tombstone_path);
+        }
     }
+
     close(fd);
     return 0;
 }
diff --git a/debuggerd/machine.h b/debuggerd/machine.h
index f9ca6bd..6049b69 100644
--- a/debuggerd/machine.h
+++ b/debuggerd/machine.h
@@ -20,6 +20,6 @@
 #include <corkscrew/backtrace.h>
 #include <sys/types.h>
 
-void dump_thread(ptrace_context_t* context, int tfd, pid_t tid, bool at_fault);
+void dump_thread(const ptrace_context_t* context, int tfd, pid_t tid, bool at_fault);
 
 #endif // _DEBUGGERD_MACHINE_H
diff --git a/debuggerd/utility.c b/debuggerd/utility.c
index c0fb13a..64e5980 100644
--- a/debuggerd/utility.c
+++ b/debuggerd/utility.c
@@ -57,8 +57,8 @@
     }
 }
 
-static void dump_backtrace(ptrace_context_t* context __attribute((unused)),
-        int tfd, int pid __attribute((unused)), bool at_fault,
+static void dump_backtrace(const ptrace_context_t* context __attribute((unused)),
+        int tfd, pid_t tid __attribute((unused)), bool at_fault,
         const backtrace_frame_t* backtrace, size_t frames) {
     _LOG(tfd, !at_fault, "\nbacktrace:\n");
 
@@ -66,7 +66,7 @@
     get_backtrace_symbols_ptrace(context, backtrace, frames, backtrace_symbols);
     for (size_t i = 0; i < frames; i++) {
         const backtrace_symbol_t* symbol = &backtrace_symbols[i];
-        const char* map_name = symbol->map_info ? symbol->map_info->name : "<unknown>";
+        const char* map_name = symbol->map_name ? symbol->map_name : "<unknown>";
         const char* symbol_name = symbol->demangled_name ? symbol->demangled_name : symbol->name;
         if (symbol_name) {
             _LOG(tfd, !at_fault, "    #%02d  pc %08x  %s (%s)\n",
@@ -79,11 +79,11 @@
     free_backtrace_symbols(backtrace_symbols, frames);
 }
 
-static void dump_stack_segment(ptrace_context_t* context, int tfd, int pid,
+static void dump_stack_segment(const ptrace_context_t* context, int tfd, pid_t tid,
         bool only_in_tombstone, uintptr_t* sp, size_t words, int label) {
     for (size_t i = 0; i < words; i++) {
         uint32_t stack_content;
-        if (!try_get_word(pid, *sp, &stack_content)) {
+        if (!try_get_word_ptrace(tid, *sp, &stack_content)) {
             break;
         }
 
@@ -116,7 +116,7 @@
     }
 }
 
-static void dump_stack(ptrace_context_t* context, int tfd, int pid, bool at_fault,
+static void dump_stack(const ptrace_context_t* context, int tfd, pid_t tid, bool at_fault,
         const backtrace_frame_t* backtrace, size_t frames) {
     bool have_first = false;
     size_t first, last;
@@ -138,7 +138,7 @@
     // Dump a few words before the first frame.
     bool only_in_tombstone = !at_fault;
     uintptr_t sp = backtrace[first].stack_top - STACK_WORDS * sizeof(uint32_t);
-    dump_stack_segment(context, tfd, pid, only_in_tombstone, &sp, STACK_WORDS, -1);
+    dump_stack_segment(context, tfd, tid, only_in_tombstone, &sp, STACK_WORDS, -1);
 
     // Dump a few words from all successive frames.
     // Only log the first 3 frames, put the rest in the tombstone.
@@ -152,7 +152,7 @@
             only_in_tombstone = true;
         }
         if (i == last) {
-            dump_stack_segment(context, tfd, pid, only_in_tombstone, &sp, STACK_WORDS, i);
+            dump_stack_segment(context, tfd, tid, only_in_tombstone, &sp, STACK_WORDS, i);
             if (sp < frame->stack_top + frame->stack_size) {
                 _LOG(tfd, only_in_tombstone, "         ........  ........\n");
             }
@@ -163,12 +163,13 @@
             } else if (words > STACK_WORDS) {
                 words = STACK_WORDS;
             }
-            dump_stack_segment(context, tfd, pid, only_in_tombstone, &sp, words, i);
+            dump_stack_segment(context, tfd, tid, only_in_tombstone, &sp, words, i);
         }
     }
 }
 
-void dump_backtrace_and_stack(ptrace_context_t* context, int tfd, pid_t tid, bool at_fault) {
+void dump_backtrace_and_stack(const ptrace_context_t* context, int tfd, pid_t tid,
+        bool at_fault) {
     backtrace_frame_t backtrace[STACK_DEPTH];
     ssize_t frames = unwind_backtrace_ptrace(tid, context, backtrace, 0, STACK_DEPTH);
     if (frames > 0) {
@@ -237,7 +238,7 @@
     }
 }
 
-void dump_nearby_maps(ptrace_context_t* context, int tfd, pid_t tid) {
+void dump_nearby_maps(const ptrace_context_t* context, int tfd, pid_t tid) {
     siginfo_t si;
     memset(&si, 0, sizeof(si));
     if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si)) {
diff --git a/debuggerd/utility.h b/debuggerd/utility.h
index 879c8b4..39f91cb 100644
--- a/debuggerd/utility.h
+++ b/debuggerd/utility.h
@@ -52,7 +52,7 @@
 /*
  * Dumps the backtrace and contents of the stack.
  */
-void dump_backtrace_and_stack(ptrace_context_t* context, int tfd, pid_t pid, bool at_fault);
+void dump_backtrace_and_stack(const ptrace_context_t* context, int tfd, pid_t tid, bool at_fault);
 
 /*
  * Dumps a few bytes of memory, starting a bit before and ending a bit
@@ -66,7 +66,7 @@
  *
  * This only makes sense to do on the thread that crashed.
  */
-void dump_nearby_maps(ptrace_context_t* context, int tfd, pid_t tid);
+void dump_nearby_maps(const ptrace_context_t* context, int tfd, pid_t tid);
 
 
 #endif // _DEBUGGERD_UTILITY_H
diff --git a/debuggerd/x86/machine.c b/debuggerd/x86/machine.c
index 57f51c8..2729c7e 100644
--- a/debuggerd/x86/machine.c
+++ b/debuggerd/x86/machine.c
@@ -39,12 +39,12 @@
 #include "../machine.h"
 #include "../utility.h"
 
-static void dump_registers(ptrace_context_t* context __attribute((unused)),
-        int tfd, pid_t pid, bool at_fault) {
+static void dump_registers(const ptrace_context_t* context __attribute((unused)),
+        int tfd, pid_t tid, bool at_fault) {
     struct pt_regs_x86 r;
     bool only_in_tombstone = !at_fault;
 
-    if(ptrace(PTRACE_GETREGS, pid, 0, &r)) {
+    if(ptrace(PTRACE_GETREGS, tid, 0, &r)) {
         _LOG(tfd, only_in_tombstone, "cannot get registers: %s\n", strerror(errno));
         return;
     }
@@ -61,7 +61,7 @@
          r.eip, r.ebp, r.esp, r.eflags);
 }
 
-void dump_thread(ptrace_context_t* context, int tfd, pid_t tid, bool at_fault) {
+void dump_thread(const ptrace_context_t* context, int tfd, pid_t tid, bool at_fault) {
     dump_registers(context, tfd, tid, at_fault);
 
     dump_backtrace_and_stack(context, tfd, tid, at_fault);
diff --git a/include/corkscrew/backtrace.h b/include/corkscrew/backtrace.h
index cecb13d..157d029 100644
--- a/include/corkscrew/backtrace.h
+++ b/include/corkscrew/backtrace.h
@@ -43,8 +43,8 @@
 typedef struct {
     uintptr_t relative_pc;       /* relative PC offset from the start of the library,
                                     or the absolute PC if the library is unknown */
-    const map_info_t* map_info;  /* memory map of the library, or NULL if unknown */
-    const char* name;            /* symbol name, or NULL if unknown */
+    char* map_name;              /* executable or library name, or NULL if unknown */
+    char* name;                  /* symbol name, or NULL if unknown */
     char* demangled_name;        /* demangled symbol name, or NULL if unknown */
 } backtrace_symbol_t;
 
diff --git a/include/corkscrew/map_info.h b/include/corkscrew/map_info.h
index a1c344f..c5cd8f8 100644
--- a/include/corkscrew/map_info.h
+++ b/include/corkscrew/map_info.h
@@ -30,6 +30,7 @@
     struct map_info* next;
     uintptr_t start;
     uintptr_t end;
+    bool is_readable;
     bool is_executable;
     void* data; // arbitrary data associated with the map by the user, initially NULL
     char name[];
@@ -44,8 +45,20 @@
 /* Finds the memory map that contains the specified address. */
 const map_info_t* find_map_info(const map_info_t* milist, uintptr_t addr);
 
-/* Gets the memory map for this process.  (result is cached) */
-const map_info_t* my_map_info_list();
+/* Returns true if the addr is in an readable map. */
+bool is_readable_map(const map_info_t* milist, uintptr_t addr);
+
+/* Returns true if the addr is in an executable map. */
+bool is_executable_map(const map_info_t* milist, uintptr_t addr);
+
+/* Acquires a reference to the memory map for this process.
+ * The result is cached and refreshed automatically.
+ * Make sure to release the map info when done. */
+map_info_t* acquire_my_map_info_list();
+
+/* Releases a reference to the map info for this process that was
+ * previous acquired using acquire_my_map_info_list(). */
+void release_my_map_info_list(map_info_t* milist);
 
 #ifdef __cplusplus
 }
diff --git a/include/corkscrew/ptrace.h b/include/corkscrew/ptrace.h
index 6acf9eb..172e348 100644
--- a/include/corkscrew/ptrace.h
+++ b/include/corkscrew/ptrace.h
@@ -35,6 +35,12 @@
     map_info_t* map_info_list;
 } ptrace_context_t;
 
+/* Describes how to access memory from a process. */
+typedef struct {
+    pid_t tid;
+    const map_info_t* map_info_list;
+} memory_t;
+
 #if __i386__
 /* ptrace() register context. */
 typedef struct pt_regs_x86 {
@@ -59,11 +65,28 @@
 #endif
 
 /*
- * Reads a word of memory safely.
- * Uses ptrace() if tid >= 0, local memory otherwise.
- * Returns false if the word could not be read.
+ * Initializes a memory structure for accessing memory from this process.
  */
-bool try_get_word(pid_t tid, uintptr_t ptr, uint32_t* out_value);
+void init_memory(memory_t* memory, const map_info_t* map_info_list);
+
+/*
+ * Initializes a memory structure for accessing memory from another process
+ * using ptrace().
+ */
+void init_memory_ptrace(memory_t* memory, pid_t tid);
+
+/*
+ * Reads a word of memory safely.
+ * If the memory is local, ensures that the address is readable before dereferencing it.
+ * Returns false and a value of 0xffffffff if the word could not be read.
+ */
+bool try_get_word(const memory_t* memory, uintptr_t ptr, uint32_t* out_value);
+
+/*
+ * Reads a word of memory safely using ptrace().
+ * Returns false and a value of 0xffffffff if the word could not be read.
+ */
+bool try_get_word_ptrace(pid_t tid, uintptr_t ptr, uint32_t* out_value);
 
 /*
  * Loads information needed for examining a remote process using ptrace().
diff --git a/libcorkscrew/arch-arm/backtrace-arm.c b/libcorkscrew/arch-arm/backtrace-arm.c
index 6bed2ae..cf9a5ab 100644
--- a/libcorkscrew/arch-arm/backtrace-arm.c
+++ b/libcorkscrew/arch-arm/backtrace-arm.c
@@ -120,35 +120,31 @@
     return place + (((int32_t)(prel_offset << 1)) >> 1);
 }
 
-static uintptr_t get_exception_handler(
-        const ptrace_context_t* context, pid_t tid, uintptr_t pc) {
+static uintptr_t get_exception_handler(const memory_t* memory,
+        const map_info_t* map_info_list, uintptr_t pc) {
+    if (!pc) {
+        ALOGV("get_exception_handler: pc is zero, no handler");
+        return 0;
+    }
+
     uintptr_t exidx_start;
     size_t exidx_size;
     const map_info_t* mi;
-    if (tid < 0) {
+    if (memory->tid < 0) {
         mi = NULL;
         exidx_start = find_exidx(pc, &exidx_size);
     } else {
-        mi = find_map_info(context->map_info_list, pc);
+        mi = find_map_info(map_info_list, pc);
         if (mi && mi->data) {
             const map_info_data_t* data = (const map_info_data_t*)mi->data;
             exidx_start = data->exidx_start;
-            exidx_size = data->exidx_size / 8;
+            exidx_size = data->exidx_size;
         } else {
             exidx_start = 0;
             exidx_size = 0;
         }
     }
 
-    // The PC points to the instruction following the branch.
-    // We want to find the exception handler entry that corresponds to the branch itself,
-    // so we offset the PC backwards into the previous instruction.
-    // ARM instructions are 4 bytes, Thumb are 2, so we just subtract two so we either
-    // end up in the middle (ARM) or at the beginning of the instruction (Thumb).
-    if (pc >= 2) {
-        pc -= 2;
-    }
-
     uintptr_t handler = 0;
     if (exidx_start) {
         uint32_t low = 0;
@@ -157,7 +153,7 @@
             uint32_t index = (low + high) / 2;
             uintptr_t entry = exidx_start + index * 8;
             uint32_t entry_prel_pc;
-            if (!try_get_word(tid, entry, &entry_prel_pc)) {
+            if (!try_get_word(memory, entry, &entry_prel_pc)) {
                 break;
             }
             uintptr_t entry_pc = prel_to_absolute(entry, entry_prel_pc);
@@ -168,7 +164,7 @@
             if (index + 1 < exidx_size) {
                 uintptr_t next_entry = entry + 8;
                 uint32_t next_entry_prel_pc;
-                if (!try_get_word(tid, next_entry, &next_entry_prel_pc)) {
+                if (!try_get_word(memory, next_entry, &next_entry_prel_pc)) {
                     break;
                 }
                 uintptr_t next_entry_pc = prel_to_absolute(next_entry, next_entry_prel_pc);
@@ -180,7 +176,7 @@
 
             uintptr_t entry_handler_ptr = entry + 4;
             uint32_t entry_handler;
-            if (!try_get_word(tid, entry_handler_ptr, &entry_handler)) {
+            if (!try_get_word(memory, entry_handler_ptr, &entry_handler)) {
                 break;
             }
             if (entry_handler & (1L << 31)) {
@@ -191,10 +187,15 @@
             break;
         }
     }
-    ALOGV("get_exception_handler: pc=0x%08x, module='%s', module_start=0x%08x, "
-            "exidx_start=0x%08x, exidx_size=%d, handler=0x%08x",
-            pc, mi ? mi->name : "<unknown>", mi ? mi->start : 0,
-            exidx_start, exidx_size, handler);
+    if (mi) {
+        ALOGV("get_exception_handler: pc=0x%08x, module='%s', module_start=0x%08x, "
+                "exidx_start=0x%08x, exidx_size=%d, handler=0x%08x",
+                pc, mi->name, mi->start, exidx_start, exidx_size, handler);
+    } else {
+        ALOGV("get_exception_handler: pc=0x%08x, "
+                "exidx_start=0x%08x, exidx_size=%d, handler=0x%08x",
+                pc, exidx_start, exidx_size, handler);
+    }
     return handler;
 }
 
@@ -203,11 +204,11 @@
     uint32_t word;
 } byte_stream_t;
 
-static bool try_next_byte(pid_t tid, byte_stream_t* stream, uint8_t* out_value) {
+static bool try_next_byte(const memory_t* memory, byte_stream_t* stream, uint8_t* out_value) {
     uint8_t result;
     switch (stream->ptr & 3) {
     case 0:
-        if (!try_get_word(tid, stream->ptr, &stream->word)) {
+        if (!try_get_word(memory, stream->ptr, &stream->word)) {
             *out_value = 0;
             return false;
         }
@@ -237,13 +238,13 @@
     state->gregs[reg] = value;
 }
 
-static bool try_pop_registers(pid_t tid, unwind_state_t* state, uint32_t mask) {
+static bool try_pop_registers(const memory_t* memory, unwind_state_t* state, uint32_t mask) {
     uint32_t sp = state->gregs[R_SP];
     bool sp_updated = false;
     for (int i = 0; i < 16; i++) {
         if (mask & (1 << i)) {
             uint32_t value;
-            if (!try_get_word(tid, sp, &value)) {
+            if (!try_get_word(memory, sp, &value)) {
                 return false;
             }
             if (i == R_SP) {
@@ -272,8 +273,8 @@
  * virtual register state (including the stack pointer) such that
  * the call frame is unwound and the PC register points to the call site.
  */
-static bool execute_personality_routine(pid_t tid, unwind_state_t* state,
-        byte_stream_t* stream, int pr_index) {
+static bool execute_personality_routine(const memory_t* memory,
+        unwind_state_t* state, byte_stream_t* stream, int pr_index) {
     size_t size;
     switch (pr_index) {
     case 0: // Personality routine #0, short frame, descriptors have 16-bit scope.
@@ -282,7 +283,7 @@
     case 1: // Personality routine #1, long frame, descriptors have 16-bit scope.
     case 2: { // Personality routine #2, long frame, descriptors have 32-bit scope.
         uint8_t size_byte;
-        if (!try_next_byte(tid, stream, &size_byte)) {
+        if (!try_next_byte(memory, stream, &size_byte)) {
             return false;
         }
         size = (uint32_t)size_byte * sizeof(uint32_t) + 2;
@@ -295,7 +296,7 @@
     bool pc_was_set = false;
     while (size--) {
         uint8_t op;
-        if (!try_next_byte(tid, stream, &op)) {
+        if (!try_next_byte(memory, stream, &op)) {
             return false;
         }
         if ((op & 0xc0) == 0x00) {
@@ -306,13 +307,13 @@
             set_reg(state, R_SP, state->gregs[R_SP] - ((op & 0x3f) << 2) - 4);
         } else if ((op & 0xf0) == 0x80) {
             uint8_t op2;
-            if (!(size--) || !try_next_byte(tid, stream, &op2)) {
+            if (!(size--) || !try_next_byte(memory, stream, &op2)) {
                 return false;
             }
             uint32_t mask = (((uint32_t)op & 0x0f) << 12) | ((uint32_t)op2 << 4);
             if (mask) {
                 // "Pop up to 12 integer registers under masks {r15-r12}, {r11-r4}"
-                if (!try_pop_registers(tid, state, mask)) {
+                if (!try_pop_registers(memory, state, mask)) {
                     return false;
                 }
                 if (mask & (1 << R_PC)) {
@@ -334,13 +335,13 @@
         } else if ((op & 0xf8) == 0xa0) {
             // "Pop r4-r[4+nnn]"
             uint32_t mask = (0x0ff0 >> (7 - (op & 0x07))) & 0x0ff0;
-            if (!try_pop_registers(tid, state, mask)) {
+            if (!try_pop_registers(memory, state, mask)) {
                 return false;
             }
         } else if ((op & 0xf8) == 0xa8) {
             // "Pop r4-r[4+nnn], r14"
             uint32_t mask = ((0x0ff0 >> (7 - (op & 0x07))) & 0x0ff0) | 0x4000;
-            if (!try_pop_registers(tid, state, mask)) {
+            if (!try_pop_registers(memory, state, mask)) {
                 return false;
             }
         } else if (op == 0xb0) {
@@ -348,12 +349,12 @@
             break;
         } else if (op == 0xb1) {
             uint8_t op2;
-            if (!(size--) || !try_next_byte(tid, stream, &op2)) {
+            if (!(size--) || !try_next_byte(memory, stream, &op2)) {
                 return false;
             }
             if (op2 != 0x00 && (op2 & 0xf0) == 0x00) {
                 // "Pop integer registers under mask {r3, r2, r1, r0}"
-                if (!try_pop_registers(tid, state, op2)) {
+                if (!try_pop_registers(memory, state, op2)) {
                     return false;
                 }
             } else {
@@ -366,7 +367,7 @@
             uint32_t shift = 0;
             uint8_t op2;
             do {
-                if (!(size--) || !try_next_byte(tid, stream, &op2)) {
+                if (!(size--) || !try_next_byte(memory, stream, &op2)) {
                     return false;
                 }
                 value |= (op2 & 0x7f) << shift;
@@ -376,7 +377,7 @@
         } else if (op == 0xb3) {
             // "Pop VFP double-precision registers D[ssss]-D[ssss+cccc] saved (as if) by FSTMFDX"
             uint8_t op2;
-            if (!(size--) || !try_next_byte(tid, stream, &op2)) {
+            if (!(size--) || !try_next_byte(memory, stream, &op2)) {
                 return false;
             }
             set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op2 & 0x0f) * 8 + 12);
@@ -389,13 +390,13 @@
         } else if (op == 0xc6) {
             // "Intel Wireless MMX pop wR[ssss]-wR[ssss+cccc]"
             uint8_t op2;
-            if (!(size--) || !try_next_byte(tid, stream, &op2)) {
+            if (!(size--) || !try_next_byte(memory, stream, &op2)) {
                 return false;
             }
             set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op2 & 0x0f) * 8 + 8);
         } else if (op == 0xc7) {
             uint8_t op2;
-            if (!(size--) || !try_next_byte(tid, stream, &op2)) {
+            if (!(size--) || !try_next_byte(memory, stream, &op2)) {
                 return false;
             }
             if (op2 != 0x00 && (op2 & 0xf0) == 0x00) {
@@ -409,14 +410,14 @@
             // "Pop VFP double precision registers D[16+ssss]-D[16+ssss+cccc]
             // saved (as if) by FSTMFD"
             uint8_t op2;
-            if (!(size--) || !try_next_byte(tid, stream, &op2)) {
+            if (!(size--) || !try_next_byte(memory, stream, &op2)) {
                 return false;
             }
             set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op2 & 0x0f) * 8 + 8);
         } else if (op == 0xc9) {
             // "Pop VFP double precision registers D[ssss]-D[ssss+cccc] saved (as if) by FSTMFDD"
             uint8_t op2;
-            if (!(size--) || !try_next_byte(tid, stream, &op2)) {
+            if (!(size--) || !try_next_byte(memory, stream, &op2)) {
                 return false;
             }
             set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op2 & 0x0f) * 8 + 8);
@@ -434,52 +435,86 @@
     return true;
 }
 
-static ssize_t unwind_backtrace_common(pid_t tid, const ptrace_context_t* context,
+static bool try_get_half_word(const memory_t* memory, uint32_t pc, uint16_t* out_value) {
+    uint32_t word;
+    if (try_get_word(memory, pc & ~2, &word)) {
+        *out_value = pc & 2 ? word >> 16 : word & 0xffff;
+        return true;
+    }
+    return false;
+}
+
+uintptr_t rewind_pc_arch(const memory_t* memory, uintptr_t pc) {
+    if (pc & 1) {
+        /* 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
+         */
+        pc &= ~1;
+        uint16_t prev1, prev2;
+        if (try_get_half_word(memory, pc - 4, &prev1)
+            && ((prev1 & 0xf000) == 0xf000)
+            && try_get_half_word(memory, pc - 2, &prev2)
+            && ((prev2 & 0xe000) == 0xe000)) {
+            pc -= 4; // long offset
+        } else {
+            pc -= 2;
+        }
+    } else {
+        /* ARM mode, all instructions are 32bit.  Yay! */
+        pc -= 4;
+    }
+    return pc;
+}
+
+static ssize_t unwind_backtrace_common(const memory_t* memory,
+        const map_info_t* map_info_list,
         unwind_state_t* state, backtrace_frame_t* backtrace,
         size_t ignore_depth, size_t max_depth) {
     size_t ignored_frames = 0;
     size_t returned_frames = 0;
 
-    uintptr_t handler = get_exception_handler(context, tid, state->gregs[R_PC]);
-    if (!handler) {
-        // If there is no handler for the PC, the program may have branched to
-        // an invalid address.  Check whether we have a handler for the LR
-        // where we came from and use that instead.
-        backtrace_frame_t* frame = add_backtrace_entry(state->gregs[R_PC], backtrace,
-                ignore_depth, max_depth, &ignored_frames, &returned_frames);
+    for (size_t index = 0; returned_frames < max_depth; index++) {
+        uintptr_t pc = index ? rewind_pc_arch(memory, state->gregs[R_PC])
+                : state->gregs[R_PC];
+        backtrace_frame_t* frame = add_backtrace_entry(pc,
+                backtrace, ignore_depth, max_depth, &ignored_frames, &returned_frames);
         if (frame) {
             frame->stack_top = state->gregs[R_SP];
         }
 
-        handler = get_exception_handler(context, tid, state->gregs[R_LR]);
+        uintptr_t handler = get_exception_handler(memory, map_info_list, pc);
         if (!handler) {
-            // We don't have a handler here either.  Unwinding will not be possible.
-            // Return the PC and LR (if it looks sane) and call it good.
-            if (state->gregs[R_LR] && state->gregs[R_LR] != state->gregs[R_PC]) {
-                // Don't return the SP for this second frame because we don't
-                // know how big the first one is so we don't know where this
-                // one starts.
-                add_backtrace_entry(state->gregs[R_LR], backtrace,
-                        ignore_depth, max_depth, &ignored_frames, &returned_frames);
+            // If there is no handler for the PC and this is the first frame,
+            // then the program may have branched to an invalid address.
+            // Try starting from the LR instead, otherwise stop unwinding.
+            if (index == 0 && state->gregs[R_LR]
+                    && state->gregs[R_LR] != state->gregs[R_PC]) {
+                set_reg(state, R_PC, state->gregs[R_LR]);
+                continue;
+            } else {
+                break;
             }
-            return returned_frames;
-        }
-
-        // Ok, continue from the LR.
-        set_reg(state, R_PC, state->gregs[R_LR]);
-    }
-
-    while (handler && returned_frames < max_depth) {
-        backtrace_frame_t* frame = add_backtrace_entry(state->gregs[R_PC], backtrace,
-                ignore_depth, max_depth, &ignored_frames, &returned_frames);
-        if (frame) {
-            frame->stack_top = state->gregs[R_SP];
         }
 
         byte_stream_t stream;
         stream.ptr = handler;
         uint8_t pr;
-        if (!try_next_byte(tid, &stream, &pr)) {
+        if (!try_next_byte(memory, &stream, &pr)) {
             break;
         }
         if ((pr & 0xf0) != 0x80) {
@@ -490,19 +525,33 @@
 
         // The first byte indicates the personality routine to execute.
         // Following bytes provide instructions to the personality routine.
-        if (!execute_personality_routine(tid, state, &stream, pr & 0x0f)) {
+        if (!execute_personality_routine(memory, state, &stream, pr & 0x0f)) {
             break;
         }
         if (frame && state->gregs[R_SP] > frame->stack_top) {
             frame->stack_size = state->gregs[R_SP] - frame->stack_top;
         }
+        if (!state->gregs[R_PC]) {
+            break;
+        }
+    }
 
-        handler = get_exception_handler(context, tid, state->gregs[R_PC]);
+    // Ran out of frames that we could unwind using handlers.
+    // Add a final entry for the LR if it looks sane and call it good.
+    if (returned_frames < max_depth
+            && state->gregs[R_LR]
+            && state->gregs[R_LR] != state->gregs[R_PC]
+            && is_executable_map(map_info_list, state->gregs[R_LR])) {
+        // We don't know where the stack for this extra frame starts so we
+        // don't return any stack information for it.
+        add_backtrace_entry(rewind_pc_arch(memory, state->gregs[R_LR]),
+                backtrace, ignore_depth, max_depth, &ignored_frames, &returned_frames);
     }
     return returned_frames;
 }
 
 ssize_t unwind_backtrace_signal_arch(siginfo_t* siginfo, void* sigcontext,
+        const map_info_t* map_info_list,
         backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) {
     const ucontext_t* uc = (const ucontext_t*)sigcontext;
 
@@ -511,7 +560,10 @@
         state.gregs[i] = uc->uc_mcontext.gregs[i];
     }
 
-    return unwind_backtrace_common(-1, NULL, &state, backtrace, ignore_depth, max_depth);
+    memory_t memory;
+    init_memory(&memory, map_info_list);
+    return unwind_backtrace_common(&memory, map_info_list, &state,
+            backtrace, ignore_depth, max_depth);
 }
 
 ssize_t unwind_backtrace_ptrace_arch(pid_t tid, const ptrace_context_t* context,
@@ -526,5 +578,8 @@
         state.gregs[i] = regs.uregs[i];
     }
 
-    return unwind_backtrace_common(tid, context, &state, backtrace, ignore_depth, max_depth);
+    memory_t memory;
+    init_memory_ptrace(&memory, tid);
+    return unwind_backtrace_common(&memory, context->map_info_list, &state,
+            backtrace, ignore_depth, max_depth);
 }
diff --git a/libcorkscrew/arch-arm/ptrace-arm.c b/libcorkscrew/arch-arm/ptrace-arm.c
index fd15505..868230c 100644
--- a/libcorkscrew/arch-arm/ptrace-arm.c
+++ b/libcorkscrew/arch-arm/ptrace-arm.c
@@ -29,26 +29,31 @@
 static void load_exidx_header(pid_t pid, map_info_t* mi,
         uintptr_t* out_exidx_start, size_t* out_exidx_size) {
     uint32_t elf_phoff;
-    uint32_t elf_phnum;
-    if (try_get_word(pid, mi->start + offsetof(Elf32_Ehdr, e_phoff), &elf_phoff)
-            && try_get_word(pid, mi->start + offsetof(Elf32_Ehdr, e_phnum), &elf_phnum)) {
+    uint32_t elf_phentsize_phnum;
+    if (try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phoff), &elf_phoff)
+            && try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phnum),
+                    &elf_phentsize_phnum)) {
+        uint32_t elf_phentsize = elf_phentsize_phnum >> 16;
+        uint32_t elf_phnum = elf_phentsize_phnum & 0xffff;
         for (uint32_t i = 0; i < elf_phnum; i++) {
-            uintptr_t elf_phdr = mi->start + elf_phoff + i * sizeof(Elf32_Phdr);
+            uintptr_t elf_phdr = mi->start + elf_phoff + i * elf_phentsize;
             uint32_t elf_phdr_type;
-            if (!try_get_word(pid, elf_phdr + offsetof(Elf32_Phdr, p_type), &elf_phdr_type)) {
+            if (!try_get_word_ptrace(pid, elf_phdr + offsetof(Elf32_Phdr, p_type), &elf_phdr_type)) {
                 break;
             }
             if (elf_phdr_type == PT_ARM_EXIDX) {
                 uint32_t elf_phdr_offset;
                 uint32_t elf_phdr_filesz;
-                if (!try_get_word(pid, elf_phdr + offsetof(Elf32_Phdr, p_offset),
+                if (!try_get_word_ptrace(pid, elf_phdr + offsetof(Elf32_Phdr, p_offset),
                         &elf_phdr_offset)
-                        || !try_get_word(pid, elf_phdr + offsetof(Elf32_Phdr, p_filesz),
+                        || !try_get_word_ptrace(pid, elf_phdr + offsetof(Elf32_Phdr, p_filesz),
                                 &elf_phdr_filesz)) {
                     break;
                 }
                 *out_exidx_start = mi->start + elf_phdr_offset;
-                *out_exidx_size = elf_phdr_filesz;
+                *out_exidx_size = elf_phdr_filesz / 8;
+                ALOGV("Parsed EXIDX header info for %s: start=0x%08x, size=%d", mi->name,
+                        *out_exidx_start, *out_exidx_size);
                 return;
             }
         }
diff --git a/libcorkscrew/arch-x86/backtrace-x86.c b/libcorkscrew/arch-x86/backtrace-x86.c
index 1324899..24fadcb 100644
--- a/libcorkscrew/arch-x86/backtrace-x86.c
+++ b/libcorkscrew/arch-x86/backtrace-x86.c
@@ -73,14 +73,21 @@
     uint32_t esp;
 } unwind_state_t;
 
-static ssize_t unwind_backtrace_common(pid_t tid, const ptrace_context_t* context,
+uintptr_t rewind_pc_arch(const memory_t* memory, uintptr_t pc) {
+    // TODO: Implement for x86.
+    return pc;
+}
+
+static ssize_t unwind_backtrace_common(const memory_t* memory,
+        const map_info_t* map_info_list,
         unwind_state_t* state, backtrace_frame_t* backtrace,
         size_t ignore_depth, size_t max_depth) {
     size_t ignored_frames = 0;
     size_t returned_frames = 0;
 
-    while (state->ebp && returned_frames < max_depth) {
-        backtrace_frame_t* frame = add_backtrace_entry(state->eip,
+    for (size_t index = 0; state->ebp && returned_frames < max_depth; index++) {
+        backtrace_frame_t* frame = add_backtrace_entry(
+                index ? rewind_pc_arch(memory, state->eip) : state->eip,
                 backtrace, ignore_depth, max_depth,
                 &ignored_frames, &returned_frames);
         uint32_t next_esp = state->ebp + 8;
@@ -91,8 +98,8 @@
             }
         }
         state->esp = next_esp;
-        if (!try_get_word(tid, state->ebp + 4, &state->eip)
-                || !try_get_word(tid, state->ebp, &state->ebp)
+        if (!try_get_word(memory, state->ebp + 4, &state->eip)
+                || !try_get_word(memory, state->ebp, &state->ebp)
                 || !state->eip) {
             break;
         }
@@ -102,6 +109,7 @@
 }
 
 ssize_t unwind_backtrace_signal_arch(siginfo_t* siginfo, void* sigcontext,
+        const map_info_t* map_info_list,
         backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) {
     const ucontext_t* uc = (const ucontext_t*)sigcontext;
 
@@ -110,7 +118,10 @@
     state.eip = uc->uc_mcontext.eip;
     state.esp = uc->uc_mcontext.esp;
 
-    return unwind_backtrace_common(-1, NULL, &state, backtrace, ignore_depth, max_depth);
+    memory_t memory;
+    init_memory(&memory, map_info_list);
+    return unwind_backtrace_common(&memory, map_info_list,
+            &state, backtrace, ignore_depth, max_depth);
 }
 
 ssize_t unwind_backtrace_ptrace_arch(pid_t tid, const ptrace_context_t* context,
@@ -125,5 +136,8 @@
     state.eip = regs.eip;
     state.esp = regs.esp;
 
-    return unwind_backtrace_common(tid, context, &state, backtrace, ignore_depth, max_depth);
+    memory_t memory;
+    init_memory_ptrace(&memory, tid);
+    return unwind_backtrace_common(&memory, context->map_info_list,
+            &state, backtrace, ignore_depth, max_depth);
 }
diff --git a/libcorkscrew/backtrace-arch.h b/libcorkscrew/backtrace-arch.h
index 80bca2b..a46f80b 100644
--- a/libcorkscrew/backtrace-arch.h
+++ b/libcorkscrew/backtrace-arch.h
@@ -28,7 +28,11 @@
 extern "C" {
 #endif
 
+/* Rewind the program counter by one instruction. */
+uintptr_t rewind_pc_arch(const memory_t* memory, uintptr_t pc);
+
 ssize_t unwind_backtrace_signal_arch(siginfo_t* siginfo, void* sigcontext,
+        const map_info_t* map_info_list,
         backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth);
 
 ssize_t unwind_backtrace_ptrace_arch(pid_t tid, const ptrace_context_t* context,
diff --git a/libcorkscrew/backtrace.c b/libcorkscrew/backtrace.c
index b03c43f..f9a49ec 100644
--- a/libcorkscrew/backtrace.c
+++ b/libcorkscrew/backtrace.c
@@ -31,6 +31,7 @@
 #include <unwind.h>
 #include <sys/exec_elf.h>
 #include <cutils/log.h>
+#include <cutils/atomic.h>
 
 #if HAVE_DLADDR
 #include <dlfcn.h>
@@ -42,6 +43,7 @@
     size_t max_depth;
     size_t ignored_frames;
     size_t returned_frames;
+    memory_t memory;
 } backtrace_state_t;
 
 static _Unwind_Reason_Code unwind_backtrace_callback(struct _Unwind_Context* context, void* arg) {
@@ -52,7 +54,7 @@
         //       This will require a new architecture-specific function to query
         //       the appropriate registers.  Current callers of unwind_backtrace
         //       don't need this information, so we won't bother collecting it just yet.
-        add_backtrace_entry(pc, state->backtrace,
+        add_backtrace_entry(rewind_pc_arch(&state->memory, pc), state->backtrace,
                 state->ignore_depth, state->max_depth,
                 &state->ignored_frames, &state->returned_frames);
     }
@@ -60,14 +62,22 @@
 }
 
 ssize_t unwind_backtrace(backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) {
+    ALOGV("Unwinding current thread %d.", gettid());
+
+    map_info_t* milist = acquire_my_map_info_list();
+
     backtrace_state_t state;
     state.backtrace = backtrace;
     state.ignore_depth = ignore_depth;
     state.max_depth = max_depth;
     state.ignored_frames = 0;
     state.returned_frames = 0;
+    init_memory(&state.memory, milist);
 
     _Unwind_Reason_Code rc =_Unwind_Backtrace(unwind_backtrace_callback, &state);
+
+    release_my_map_info_list(milist);
+
     if (state.returned_frames) {
         return state.returned_frames;
     }
@@ -77,28 +87,39 @@
 #ifdef CORKSCREW_HAVE_ARCH
 static pthread_mutex_t g_unwind_signal_mutex = PTHREAD_MUTEX_INITIALIZER;
 static volatile struct {
+    int32_t tid;
+    const map_info_t* map_info_list;
     backtrace_frame_t* backtrace;
     size_t ignore_depth;
     size_t max_depth;
     size_t returned_frames;
-    bool done;
 } g_unwind_signal_state;
 
 static void unwind_backtrace_thread_signal_handler(int n, siginfo_t* siginfo, void* sigcontext) {
-    backtrace_frame_t* backtrace = g_unwind_signal_state.backtrace;
-    if (backtrace) {
-        g_unwind_signal_state.backtrace = NULL;
+    int32_t tid = android_atomic_acquire_load(&g_unwind_signal_state.tid);
+    if (tid == gettid()) {
         g_unwind_signal_state.returned_frames = unwind_backtrace_signal_arch(
-                siginfo, sigcontext, backtrace,
+                siginfo, sigcontext,
+                g_unwind_signal_state.map_info_list,
+                g_unwind_signal_state.backtrace,
                 g_unwind_signal_state.ignore_depth,
                 g_unwind_signal_state.max_depth);
-        g_unwind_signal_state.done = true;
+        android_atomic_release_store(-1, &g_unwind_signal_state.tid);
+    } else {
+        ALOGV("Received spurious SIGURG on thread %d that was intended for thread %d.",
+                gettid(), tid);
     }
 }
 #endif
 
 ssize_t unwind_backtrace_thread(pid_t tid, backtrace_frame_t* backtrace,
         size_t ignore_depth, size_t max_depth) {
+    if (tid == gettid()) {
+        return unwind_backtrace(backtrace, ignore_depth + 1, max_depth);
+    }
+
+    ALOGV("Unwinding thread %d from thread %d.", tid, gettid());
+
 #ifdef CORKSCREW_HAVE_ARCH
     struct sigaction act;
     struct sigaction oact;
@@ -108,26 +129,32 @@
     sigemptyset(&act.sa_mask);
 
     pthread_mutex_lock(&g_unwind_signal_mutex);
-
-    g_unwind_signal_state.backtrace = backtrace;
-    g_unwind_signal_state.ignore_depth = ignore_depth;
-    g_unwind_signal_state.max_depth = max_depth;
-    g_unwind_signal_state.returned_frames = 0;
-    g_unwind_signal_state.done = false;
+    map_info_t* milist = acquire_my_map_info_list();
 
     ssize_t frames = -1;
     if (!sigaction(SIGURG, &act, &oact)) {
-        if (!kill(tid, SIGURG)) {
-            while (!g_unwind_signal_state.done) {
+        g_unwind_signal_state.map_info_list = milist;
+        g_unwind_signal_state.backtrace = backtrace;
+        g_unwind_signal_state.ignore_depth = ignore_depth;
+        g_unwind_signal_state.max_depth = max_depth;
+        g_unwind_signal_state.returned_frames = 0;
+        android_atomic_release_store(tid, &g_unwind_signal_state.tid);
+
+        if (kill(tid, SIGURG)) {
+            ALOGV("Failed to send SIGURG to thread %d.", tid);
+            android_atomic_release_store(-1, &g_unwind_signal_state.tid);
+        } else {
+            while (android_atomic_acquire_load(&g_unwind_signal_state.tid) == tid) {
+                ALOGV("Waiting for response from thread %d...", tid);
                 usleep(1000);
             }
             frames = g_unwind_signal_state.returned_frames;
         }
+
         sigaction(SIGURG, &oact, NULL);
     }
 
-    g_unwind_signal_state.backtrace = NULL;
-
+    release_my_map_info_list(milist);
     pthread_mutex_unlock(&g_unwind_signal_mutex);
     return frames;
 #else
@@ -146,14 +173,14 @@
 
 static void init_backtrace_symbol(backtrace_symbol_t* symbol, uintptr_t pc) {
     symbol->relative_pc = pc;
-    symbol->map_info = NULL;
+    symbol->map_name = NULL;
     symbol->name = NULL;
     symbol->demangled_name = NULL;
 }
 
 void get_backtrace_symbols(const backtrace_frame_t* backtrace, size_t frames,
         backtrace_symbol_t* backtrace_symbols) {
-    const map_info_t* milist = my_map_info_list();
+    map_info_t* milist = acquire_my_map_info_list();
     for (size_t i = 0; i < frames; i++) {
         const backtrace_frame_t* frame = &backtrace[i];
         backtrace_symbol_t* symbol = &backtrace_symbols[i];
@@ -162,16 +189,19 @@
         const map_info_t* mi = find_map_info(milist, frame->absolute_pc);
         if (mi) {
             symbol->relative_pc = frame->absolute_pc - mi->start;
-            symbol->map_info = mi;
+            if (mi->name[0]) {
+                symbol->map_name = strdup(mi->name);
+            }
 #if HAVE_DLADDR
             Dl_info info;
             if (dladdr((const void*)frame->absolute_pc, &info) && info.dli_sname) {
-                symbol->name = info.dli_sname;
+                symbol->name = strdup(info.dli_sname);
                 symbol->demangled_name = demangle_symbol_name(symbol->name);
             }
 #endif
         }
     }
+    release_my_map_info_list(milist);
 }
 
 void get_backtrace_symbols_ptrace(const ptrace_context_t* context,
@@ -187,10 +217,12 @@
         find_symbol_ptrace(context, frame->absolute_pc, &mi, &s);
         if (mi) {
             symbol->relative_pc = frame->absolute_pc - mi->start;
-            symbol->map_info = mi;
+            if (mi->name[0]) {
+                symbol->map_name = strdup(mi->name);
+            }
         }
         if (s) {
-            symbol->name = s->name;
+            symbol->name = strdup(s->name);
             symbol->demangled_name = demangle_symbol_name(symbol->name);
         }
     }
@@ -199,6 +231,8 @@
 void free_backtrace_symbols(backtrace_symbol_t* backtrace_symbols, size_t frames) {
     for (size_t i = 0; i < frames; i++) {
         backtrace_symbol_t* symbol = &backtrace_symbols[i];
+        free(symbol->map_name);
+        free(symbol->name);
         free(symbol->demangled_name);
         init_backtrace_symbol(symbol, 0);
     }
diff --git a/libcorkscrew/map_info.c b/libcorkscrew/map_info.c
index 60ed9b4..f33378f 100644
--- a/libcorkscrew/map_info.c
+++ b/libcorkscrew/map_info.c
@@ -26,6 +26,7 @@
 #include <pthread.h>
 #include <unistd.h>
 #include <cutils/log.h>
+#include <sys/time.h>
 
 // 6f000000-6f01e000 rwxp 00000000 00:0c 16389419   /system/lib/libcomposer.so\n
 // 012345678901234567890123456789012345678901234567890123456789
@@ -54,10 +55,14 @@
     if (mi) {
         mi->start = start;
         mi->end = end;
+        mi->is_readable = strlen(permissions) == 4 && permissions[0] == 'r';
         mi->is_executable = strlen(permissions) == 4 && permissions[2] == 'x';
         mi->data = NULL;
         memcpy(mi->name, name, name_len);
         mi->name[name_len] = '\0';
+        ALOGV("Parsed map: start=0x%08x, end=0x%08x, "
+                "is_readable=%d, is_executable=%d, name=%s",
+                mi->start, mi->end, mi->is_readable, mi->is_executable, mi->name);
     }
     return mi;
 }
@@ -99,14 +104,87 @@
     return mi;
 }
 
-static pthread_once_t g_my_milist_once = PTHREAD_ONCE_INIT;
-static map_info_t* g_my_milist = NULL;
-
-static void init_my_milist_once() {
-    g_my_milist = load_map_info_list(getpid());
+bool is_readable_map(const map_info_t* milist, uintptr_t addr) {
+    const map_info_t* mi = find_map_info(milist, addr);
+    return mi && mi->is_readable;
 }
 
-const map_info_t* my_map_info_list() {
-    pthread_once(&g_my_milist_once, init_my_milist_once);
-    return g_my_milist;
+bool is_executable_map(const map_info_t* milist, uintptr_t addr) {
+    const map_info_t* mi = find_map_info(milist, addr);
+    return mi && mi->is_executable;
+}
+
+static pthread_mutex_t g_my_map_info_list_mutex = PTHREAD_MUTEX_INITIALIZER;
+static map_info_t* g_my_map_info_list = NULL;
+
+static const int64_t MAX_CACHE_AGE = 5 * 1000 * 1000000LL;
+
+typedef struct {
+    uint32_t refs;
+    int64_t timestamp;
+} my_map_info_data_t;
+
+static int64_t now() {
+    struct timespec t;
+    t.tv_sec = t.tv_nsec = 0;
+    clock_gettime(CLOCK_MONOTONIC, &t);
+    return t.tv_sec * 1000000000LL + t.tv_nsec;
+}
+
+static void dec_ref(map_info_t* milist, my_map_info_data_t* data) {
+    if (!--data->refs) {
+        ALOGV("Freed my_map_info_list %p.", milist);
+        free(data);
+        free_map_info_list(milist);
+    }
+}
+
+map_info_t* acquire_my_map_info_list() {
+    pthread_mutex_lock(&g_my_map_info_list_mutex);
+
+    int64_t time = now();
+    if (g_my_map_info_list) {
+        my_map_info_data_t* data = (my_map_info_data_t*)g_my_map_info_list->data;
+        int64_t age = time - data->timestamp;
+        if (age >= MAX_CACHE_AGE) {
+            ALOGV("Invalidated my_map_info_list %p, age=%lld.", g_my_map_info_list, age);
+            dec_ref(g_my_map_info_list, data);
+            g_my_map_info_list = NULL;
+        } else {
+            ALOGV("Reusing my_map_info_list %p, age=%lld.", g_my_map_info_list, age);
+        }
+    }
+
+    if (!g_my_map_info_list) {
+        my_map_info_data_t* data = (my_map_info_data_t*)malloc(sizeof(my_map_info_data_t));
+        g_my_map_info_list = load_map_info_list(getpid());
+        if (g_my_map_info_list) {
+            ALOGV("Loaded my_map_info_list %p.", g_my_map_info_list);
+            g_my_map_info_list->data = data;
+            data->refs = 1;
+            data->timestamp = time;
+        } else {
+            free(data);
+        }
+    }
+
+    map_info_t* milist = g_my_map_info_list;
+    if (milist) {
+        my_map_info_data_t* data = (my_map_info_data_t*)g_my_map_info_list->data;
+        data->refs += 1;
+    }
+
+    pthread_mutex_unlock(&g_my_map_info_list_mutex);
+    return milist;
+}
+
+void release_my_map_info_list(map_info_t* milist) {
+    if (milist) {
+        pthread_mutex_lock(&g_my_map_info_list_mutex);
+
+        my_map_info_data_t* data = (my_map_info_data_t*)milist->data;
+        dec_ref(milist, data);
+
+        pthread_mutex_unlock(&g_my_map_info_list_mutex);
+    }
 }
diff --git a/libcorkscrew/ptrace.c b/libcorkscrew/ptrace.c
index a308bb5..cbea8ca 100644
--- a/libcorkscrew/ptrace.c
+++ b/libcorkscrew/ptrace.c
@@ -34,44 +34,55 @@
 #define PAGE_MASK (~(PAGE_SIZE - 1))
 #endif
 
-bool try_get_word(pid_t tid, uintptr_t ptr, uint32_t* out_value) {
+void init_memory(memory_t* memory, const map_info_t* map_info_list) {
+    memory->tid = -1;
+    memory->map_info_list = map_info_list;
+}
+
+void init_memory_ptrace(memory_t* memory, pid_t tid) {
+    memory->tid = tid;
+    memory->map_info_list = NULL;
+}
+
+bool try_get_word(const memory_t* memory, uintptr_t ptr, uint32_t* out_value) {
+    ALOGV("try_get_word: reading word at 0x%08x", ptr);
     if (ptr & 3) {
         ALOGV("try_get_word: invalid pointer 0x%08x", ptr);
-        *out_value = 0;
+        *out_value = 0xffffffffL;
         return false;
     }
-    if (tid < 0) {
-#if 0 /*unreliable, unclear whether this is safe from a signal handler context*/
-        // Determine whether the pointer is likely to be valid before dereferencing it.
-        unsigned char vec[1];
-        while (mincore((void*)(ptr & PAGE_MASK), sizeof(uint32_t), vec)) {
-            if (errno != EAGAIN && errno != EINTR) {
-                ALOGV("try_get_word: invalid pointer 0x%08x, mincore() errno=%d", ptr, errno);
-                *out_value = 0;
-                return false;
-            }
+    if (memory->tid < 0) {
+        if (!is_readable_map(memory->map_info_list, ptr)) {
+            ALOGV("try_get_word: pointer 0x%08x not in a readable map", ptr);
+            *out_value = 0xffffffffL;
+            return false;
         }
-#endif
         *out_value = *(uint32_t*)ptr;
         return true;
     } else {
         // ptrace() returns -1 and sets errno when the operation fails.
         // To disambiguate -1 from a valid result, we clear errno beforehand.
         errno = 0;
-        *out_value = ptrace(PTRACE_PEEKTEXT, tid, (void*)ptr, NULL);
+        *out_value = ptrace(PTRACE_PEEKTEXT, memory->tid, (void*)ptr, NULL);
         if (*out_value == 0xffffffffL && errno) {
-            ALOGV("try_get_word: invalid pointer 0x%08x, ptrace() errno=%d", ptr, errno);
-            *out_value = 0;
+            ALOGV("try_get_word: invalid pointer 0x%08x reading from tid %d, "
+                    "ptrace() errno=%d", ptr, memory->tid, errno);
             return false;
         }
         return true;
     }
 }
 
+bool try_get_word_ptrace(pid_t tid, uintptr_t ptr, uint32_t* out_value) {
+    memory_t memory;
+    init_memory_ptrace(&memory, tid);
+    return try_get_word(&memory, ptr, out_value);
+}
+
 static void load_ptrace_map_info_data(pid_t pid, map_info_t* mi) {
-    if (mi->is_executable) {
+    if (mi->is_executable && mi->is_readable) {
         uint32_t elf_magic;
-        if (try_get_word(pid, mi->start, &elf_magic) && elf_magic == ELF_MAGIC) {
+        if (try_get_word_ptrace(pid, mi->start, &elf_magic) && elf_magic == ELF_MAGIC) {
             map_info_data_t* data = (map_info_data_t*)calloc(1, sizeof(map_info_data_t));
             if (data) {
                 mi->data = data;
diff --git a/libdiskconfig/Android.mk b/libdiskconfig/Android.mk
index 9decbb9..b5d83fa 100644
--- a/libdiskconfig/Android.mk
+++ b/libdiskconfig/Android.mk
@@ -19,7 +19,6 @@
 LOCAL_SRC_FILES := $(commonSources)
 LOCAL_MODULE := libdiskconfig_host
 LOCAL_MODULE_TAGS := optional
-LOCAL_SYSTEM_SHARED_LIBRARIES := libcutils
 LOCAL_CFLAGS := -O2 -g -W -Wall -Werror -D_LARGEFILE64_SOURCE
 include $(BUILD_HOST_STATIC_LIBRARY)
 endif # HOST_OS == linux
diff --git a/libnetutils/dhcp_utils.c b/libnetutils/dhcp_utils.c
index 40f5495..d18931b 100644
--- a/libnetutils/dhcp_utils.c
+++ b/libnetutils/dhcp_utils.c
@@ -301,7 +301,7 @@
 int dhcp_do_request_renew(const char *interface,
                     in_addr_t *ipaddr,
                     in_addr_t *gateway,
-                    in_addr_t *mask,
+                    uint32_t *prefixLength,
                     in_addr_t *dns1,
                     in_addr_t *dns2,
                     in_addr_t *server,
@@ -341,7 +341,7 @@
         return -1;
     }
     if (strcmp(prop_value, "ok") == 0) {
-        fill_ip_info(interface, ipaddr, gateway, mask, dns1, dns2, server, lease);
+        fill_ip_info(interface, ipaddr, gateway, prefixLength, dns1, dns2, server, lease);
         return 0;
     } else {
         snprintf(errmsg, sizeof(errmsg), "DHCP Renew result was %s", prop_value);
diff --git a/libnl_2/handlers.c b/libnl_2/handlers.c
index ec8d512..48dcab4 100644
--- a/libnl_2/handlers.c
+++ b/libnl_2/handlers.c
@@ -39,16 +39,14 @@
 struct nl_cb *nl_cb_clone(struct nl_cb *orig)
 {
 	struct nl_cb *new_cb;
-	int new_refcnt;
 
 	new_cb = nl_cb_alloc(NL_CB_DEFAULT);
 	if (new_cb == NULL)
 		goto fail;
 
-	/* Preserve reference count and copy original */
-	new_refcnt = new_cb->cb_refcnt;
+	/* Copy original and set refcount to 1 */
 	memcpy(new_cb, orig, sizeof(*orig));
-	new_cb->cb_refcnt = new_refcnt;
+	new_cb->cb_refcnt = 1;
 
 	return new_cb;
 fail:
@@ -84,9 +82,9 @@
 
 void nl_cb_put(struct nl_cb *cb)
 {
+	if (!cb)
+		return;
 	cb->cb_refcnt--;
 	if (cb->cb_refcnt <= 0)
 		free(cb);
-
 }
-
diff --git a/libnl_2/netlink.c b/libnl_2/netlink.c
index cc2f88e..ee3d600 100644
--- a/libnl_2/netlink.c
+++ b/libnl_2/netlink.c
@@ -59,15 +59,14 @@
 {
 	int rc = -1;
 	int sk_flags;
-	int RECV_BUF_SIZE;
+	int RECV_BUF_SIZE = getpagesize();
 	int errsv;
 	struct iovec recvmsg_iov;
 	struct msghdr msg;
 
 	/* Allocate buffer */
-	RECV_BUF_SIZE = getpagesize();
 	*buf = (unsigned char *) malloc(RECV_BUF_SIZE);
-	if (!buf) {
+	if (!(*buf)) {
 		rc = -ENOMEM;
 		goto fail;
 	}
@@ -91,8 +90,11 @@
 	errsv = errno;
 	fcntl(sk->s_fd, F_SETFL, sk_flags);
 
-	if (rc < 0)
+	if (rc < 0) {
 		rc = -errsv;
+		free(*buf);
+		*buf = NULL;
+	}
 
 fail:
 	return rc;
@@ -108,7 +110,6 @@
 	int rc, cb_rc = NL_OK, done = 0;
 
 	do {
-
 		unsigned char *buf;
 		int i, rem, flags;
 		struct nlmsghdr *nlh;
@@ -127,7 +128,7 @@
 
 			/* Check for callbacks */
 
-			msg = (struct nl_msg *)malloc(sizeof(struct nl_msg));
+			msg = (struct nl_msg *) malloc(sizeof(struct nl_msg));
 			memset(msg, 0, sizeof(*msg));
 			msg->nm_nlh = nlh;
 
@@ -187,7 +188,6 @@
 			if (done)
 				break;
 		}
-
 		free(buf);
 		buf = NULL;
 
@@ -197,7 +197,7 @@
 
 success:
 fail:
-	return	rc;
+	return rc;
 }
 
 /* Send raw data over netlink socket */
diff --git a/libnl_2/socket.c b/libnl_2/socket.c
index ce54f19..d906cac 100644
--- a/libnl_2/socket.c
+++ b/libnl_2/socket.c
@@ -31,7 +31,7 @@
 }
 
 /* Allocate new netlink socket. */
-struct nl_sock *nl_socket_alloc(void)
+static struct nl_sock *_nl_socket_alloc(void)
 {
 	struct nl_sock *sk;
 	struct timeval tv;
@@ -39,13 +39,13 @@
 
 	sk = (struct nl_sock *) malloc(sizeof(struct nl_sock));
 	if (!sk)
-		goto fail;
+		return NULL;
 	memset(sk, 0, sizeof(*sk));
 
 	/* Get current time */
 
 	if (gettimeofday(&tv, NULL))
-		return NULL;
+		goto fail;
 	else
 		sk->s_seq_next = (int) tv.tv_sec;
 
@@ -59,24 +59,36 @@
 	sk->s_peer.nl_pid = 0; /* Kernel */
 	sk->s_peer.nl_groups = 0; /* No groups */
 
-	cb = (struct nl_cb *) malloc(sizeof(struct nl_cb));
+	return sk;
+fail:
+	free(sk);
+	return NULL;
+}
+
+/* Allocate new netlink socket. */
+struct nl_sock *nl_socket_alloc(void)
+{
+	struct nl_sock *sk = _nl_socket_alloc();
+	struct nl_cb *cb;
+
+	if (!sk)
+		return NULL;
+
+	cb = nl_cb_alloc(NL_CB_DEFAULT);
 	if (!cb)
 		goto cb_fail;
-	memset(cb, 0, sizeof(*cb));
-	sk->s_cb = nl_cb_alloc(NL_CB_DEFAULT);
-
-
+	sk->s_cb = cb;
 	return sk;
 cb_fail:
 	free(sk);
-fail:
 	return NULL;
 }
 
 /* Allocate new socket with custom callbacks. */
 struct nl_sock *nl_socket_alloc_cb(struct nl_cb *cb)
 {
-	struct nl_sock *sk = nl_socket_alloc();
+	struct nl_sock *sk = _nl_socket_alloc();
+
 	if (!sk)
 		return NULL;
 
@@ -84,7 +96,6 @@
 	nl_cb_get(cb);
 
 	return sk;
-
 }
 
 /* Free a netlink socket. */
@@ -116,5 +127,3 @@
 {
 	return sk->s_fd;
 }
-
-
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index fe96976..4beebb7 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -122,7 +122,6 @@
             }
             pm = (ulog_packet_msg_t *)NLMSG_DATA(nh);
             devname = pm->indev_name[0] ? pm->indev_name : pm->outdev_name;
-            SLOGD("QLOG prefix=%s dev=%s\n", pm->prefix, devname);
             asprintf(&mParams[0], "ALERT_NAME=%s", pm->prefix);
             asprintf(&mParams[1], "INTERFACE=%s", devname);
             mSubsystem = strdup("qlog");
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 3af0943..f44d89a 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -1,4 +1,7 @@
 on early-init
+    # Set init and its forked children's oom_adj.
+    write /proc/1/oom_adj -16
+
     start ueventd
 
 # create mountpoints
@@ -65,6 +68,8 @@
     write /proc/sys/kernel/sched_compat_yield 1
     write /proc/sys/kernel/sched_child_runs_first 0
     write /proc/sys/kernel/randomize_va_space 2
+    write /proc/sys/kernel/kptr_restrict 2
+    write /proc/sys/kernel/dmesg_restrict 1
 
 # Create cgroup mount points for process groups
     mkdir /dev/cpuctl
@@ -205,9 +210,6 @@
     chown root system /sys/module/lowmemorykiller/parameters/minfree
     chmod 0664 /sys/module/lowmemorykiller/parameters/minfree
 
-    # Set init and its forked children's oom_adj.
-    write /proc/1/oom_adj -16
-
     # Tweak background writeout
     write /proc/sys/vm/dirty_expire_centisecs 200
     write /proc/sys/vm/dirty_background_ratio  5
@@ -380,6 +382,8 @@
     critical
     onrestart restart zygote
     onrestart restart media
+    onrestart restart surfaceflinger
+    onrestart restart drm
 
 service vold /system/bin/vold
     class core