Improve stack unwinder robustness.

Keep track of whether memory maps are readable.  Use the information
in try_get_word to try to avoid accidentally dereferencing an invalid
pointer within the current process.  (Note that I haven't ever
seen that happen during normal unwinding, but it pays to be
a little more careful.)

Refactored try_get_word a little to make it easier to pass it the
needed state for validation checks by way of a little memory_t struct.

Improved how the memory map for the current process is cached.  This is
important because we need up to date information about readable maps.
Use a 5 second cache expiration.

Improved the PC -> LR fallback logic in the unwinder so we can
eke out an extra frame sometimes.

Fixed a bug reading ELF program headers.  The phnum & phentsize
fields are half-words.  We were incorrectly interpreting
phnum as a whole word.

Used android_atomic_* operations carefully in the unwinder
to prevent possible memory races between the dumper and the dumpee.
This was highly unlikely (or even impossible due to the presence
of other barriers along the way) but the code is clearer now about
its invariants.

Fixed a bug in debuggerd where the pid was being passed to have
its stack dump taken instead of the tid, resulting in short
stacks because ptrace couldn't read the data if pid != tid.
Did a full sweep to ensure that we use pid / tid correctly everywhere.

Ported old code from debuggerd to rewind the program counter back
one instruction so that it points to the branch instruction itself
instead of the return address.

Change-Id: Icc4eb08320052975a4ae7f0f5f0ac9308a2d33d7
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..20ffc13 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);
 
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;