am ba5f5fdd: am e21d03ce: Merge "Dwarf backtrace for MIPS"

* commit 'ba5f5fdd774223b3a801626cde014637e0fb04ec':
  Dwarf backtrace for MIPS
diff --git a/libcorkscrew/arch-mips/backtrace-mips.c b/libcorkscrew/arch-mips/backtrace-mips.c
index 1abdd83..57cb324 100644
--- a/libcorkscrew/arch-mips/backtrace-mips.c
+++ b/libcorkscrew/arch-mips/backtrace-mips.c
@@ -23,20 +23,34 @@
 
 #include "../backtrace-arch.h"
 #include "../backtrace-helper.h"
+#include "../ptrace-arch.h"
 #include <corkscrew/ptrace.h>
+#include "dwarf.h"
 
 #include <stdlib.h>
 #include <signal.h>
 #include <stdbool.h>
 #include <limits.h>
 #include <errno.h>
+#include <string.h>
 #include <sys/ptrace.h>
-#include <sys/exec_elf.h>
 #include <cutils/log.h>
 
+#if defined(__BIONIC__)
+
+#if defined(__BIONIC_HAVE_UCONTEXT_T)
+
+// Bionic offers the Linux kernel headers.
+#include <asm/sigcontext.h>
+#include <asm/ucontext.h>
+typedef struct ucontext ucontext_t;
+
+#else /* __BIONIC_HAVE_UCONTEXT_T */
+
+/* Old versions of the Android <signal.h> didn't define ucontext_t. */
+
 /* For PTRACE_GETREGS */
 typedef struct {
-    /* FIXME: check this definition */
     uint64_t regs[32];
     uint64_t lo;
     uint64_t hi;
@@ -46,22 +60,48 @@
     uint64_t cause;
 } user_regs_struct;
 
+enum {
+    REG_ZERO = 0, REG_AT, REG_V0, REG_V1,
+    REG_A0, REG_A1, REG_A2, REG_A3,
+    REG_T0, REG_T1, REG_T2, REG_T3,
+    REG_T4, REG_T5, REG_T6, REG_T7,
+    REG_S0, REG_S1, REG_S2, REG_S3,
+    REG_S4, REG_S5, REG_S6, REG_S7,
+    REG_T8, REG_T9, REG_K0, REG_K1,
+    REG_GP, REG_SP, REG_S8, REG_RA,
+};
+
 /* Machine context at the time a signal was raised. */
 typedef struct ucontext {
-    /* FIXME: use correct definition */
-    uint32_t sp;
-    uint32_t ra;
-    uint32_t pc;
+    unsigned int sc_regmask;
+    unsigned int sc_status;
+    unsigned long long sc_pc;
+    unsigned long long sc_regs[32];
+    unsigned long long sc_fpregs[32];
+    unsigned int sc_acx;
+    unsigned int sc_fpc_csr;
+    unsigned int sc_fpc_eir;
+    unsigned int sc_used_math;
+    unsigned int sc_dsp;
+    unsigned long long sc_mdhi;
+    unsigned long long sc_mdlo;
+    unsigned long sc_hi1;
+    unsigned long sc_lo1;
+    unsigned long sc_hi2;
+    unsigned long sc_lo2;
+    unsigned long sc_hi3;
+    unsigned long sc_lo3;
 } ucontext_t;
 
+#endif /* __BIONIC_HAVE_UCONTEXT_T */
+#endif
+
 /* Unwind state. */
 typedef struct {
-    uint32_t sp;
-    uint32_t ra;
-    uint32_t pc;
+    uint32_t reg[DWARF_REGISTERS];
 } unwind_state_t;
 
-uintptr_t rewind_pc_arch(const memory_t* memory, uintptr_t pc) {
+uintptr_t rewind_pc_arch(const memory_t* memory __attribute__((unused)), uintptr_t pc) {
     if (pc == 0)
         return pc;
     if ((pc & 1) == 0)
@@ -69,108 +109,806 @@
     return pc;
 }
 
+/* Read byte through 4 byte cache. Usually we read byte by byte and updating cursor. */
+static bool try_get_byte(const memory_t* memory, uintptr_t ptr, uint8_t* out_value, uint32_t* cursor) {
+    static uintptr_t lastptr;
+    static uint32_t buf;
+
+    ptr += *cursor;
+
+    if (ptr < lastptr || lastptr + 3 < ptr) {
+        lastptr = (ptr >> 2) << 2;
+        if (!try_get_word(memory, lastptr, &buf)) {
+            return false;
+        }
+    }
+    *out_value = (uint8_t)((buf >> ((ptr & 3) * 8)) & 0xff);
+    ++*cursor;
+    return true;
+}
+
+/* Getting X bytes. 4 is maximum for now. */
+static bool try_get_xbytes(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint8_t bytes, uint32_t* cursor) {
+    uint32_t data = 0;
+    if (bytes > 4) {
+        ALOGE("can't read more than 4 bytes, trying to read %d", bytes);
+        return false;
+    }
+    for (int i = 0; i < bytes; i++) {
+        uint8_t buf;
+        if (!try_get_byte(memory, ptr, &buf, cursor)) {
+            return false;
+        }
+        data |= (uint32_t)buf << (i * 8);
+    }
+    *out_value = data;
+    return true;
+}
+
+/* Reads signed/unsigned LEB128 encoded data. From 1 to 4 bytes. */
+static bool try_get_leb128(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint32_t* cursor, bool sign_extend) {
+    uint8_t buf = 0;
+    uint32_t val = 0;
+    uint8_t c = 0;
+    do {
+        if (!try_get_byte(memory, ptr, &buf, cursor)) {
+            return false;
+        }
+        val |= ((uint32_t)buf & 0x7f) << (c * 7);
+        c++;
+    } while (buf & 0x80 && (c * 7) <= 32);
+    if (c * 7 > 32) {
+        ALOGE("%s: data exceeds expected 4 bytes maximum", __FUNCTION__);
+        return false;
+    }
+    if (sign_extend) {
+        if (buf & 0x40) {
+            val |= ((uint32_t)-1 << (c * 7));
+        }
+    }
+    *out_value = val;
+    return true;
+}
+
+/* Reads signed LEB128 encoded data. From 1 to 4 bytes. */
+static bool try_get_sleb128(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint32_t* cursor) {
+    return try_get_leb128(memory, ptr, out_value, cursor, true);
+}
+
+/* Reads unsigned LEB128 encoded data. From 1 to 4 bytes. */
+static bool try_get_uleb128(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint32_t* cursor) {
+    return try_get_leb128(memory, ptr, out_value, cursor, false);
+}
+
+/* Getting data encoded by dwarf encodings. */
+static bool read_dwarf(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint8_t encoding, uint32_t* cursor) {
+    uint32_t data = 0;
+    bool issigned = true;
+    uintptr_t addr = ptr + *cursor;
+    /* Lower 4 bits is data type/size */
+    /* TODO: add more encodings if it becomes necessary */
+    switch (encoding & 0xf) {
+    case DW_EH_PE_absptr:
+        if (!try_get_xbytes(memory, ptr, &data, 4, cursor)) {
+            return false;
+        }
+        *out_value = data;
+        return true;
+    case DW_EH_PE_udata4:
+        issigned = false;
+    case DW_EH_PE_sdata4:
+        if (!try_get_xbytes(memory, ptr, &data, 4, cursor)) {
+            return false;
+        }
+        break;
+    default:
+        ALOGE("unrecognized dwarf lower part encoding: 0x%x", encoding);
+        return false;
+    }
+    /* Higher 4 bits is modifier */
+    /* TODO: add more encodings if it becomes necessary */
+    switch (encoding & 0xf0) {
+    case 0:
+        *out_value = data;
+        break;
+    case DW_EH_PE_pcrel:
+        if (issigned) {
+            *out_value = addr + (int32_t)data;
+        } else {
+            *out_value = addr + data;
+        }
+        break;
+        /* Assuming ptr is correct base to calculate datarel */
+    case DW_EH_PE_datarel:
+        if (issigned) {
+            *out_value = ptr + (int32_t)data;
+        } else {
+            *out_value = ptr + data;
+        }
+        break;
+    default:
+        ALOGE("unrecognized dwarf higher part encoding: 0x%x", encoding);
+        return false;
+    }
+    return true;
+}
+
+/* Having PC find corresponding FDE by reading .eh_frame_hdr section data. */
+static uintptr_t find_fde(const memory_t* memory,
+                          const map_info_t* map_info_list, uintptr_t pc) {
+    if (!pc) {
+        ALOGV("find_fde: pc is zero, no eh_frame");
+        return 0;
+    }
+    const map_info_t* mi = find_map_info(map_info_list, pc);
+    if (!mi) {
+        ALOGV("find_fde: no map info for pc:0x%x", pc);
+        return 0;
+    }
+    const map_info_data_t* midata = mi->data;
+    if (!midata) {
+        ALOGV("find_fde: no eh_frame_hdr for map: start=0x%x, end=0x%x", mi->start, mi->end);
+        return 0;
+    }
+
+    eh_frame_hdr_info_t eh_hdr_info;
+    memset(&eh_hdr_info, 0, sizeof(eh_frame_hdr_info_t));
+
+    /* Getting the first word of eh_frame_hdr:
+       1st byte is version;
+       2nd byte is encoding of pointer to eh_frames;
+       3rd byte is encoding of count of FDEs in lookup table;
+       4th byte is encoding of lookup table entries.
+    */
+    uintptr_t eh_frame_hdr = midata->eh_frame_hdr;
+    uint32_t c = 0;
+    if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.version, &c)) return 0;
+    if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.eh_frame_ptr_enc, &c)) return 0;
+    if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.fde_count_enc, &c)) return 0;
+    if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.fde_table_enc, &c)) return 0;
+
+    /* TODO: 3rd byte can be DW_EH_PE_omit, that means no lookup table available and we should
+       try to parse eh_frame instead. Not sure how often it may occur, skipping now.
+    */
+    if (eh_hdr_info.version != 1) {
+        ALOGV("find_fde: eh_frame_hdr version %d is not supported", eh_hdr_info.version);
+        return 0;
+    }
+    /* Getting the data:
+       2nd word is eh_frame pointer (normally not used, because lookup table has all we need);
+       3rd word is count of FDEs in the lookup table;
+       starting from 4 word there is FDE lookup table (pairs of PC and FDE pointer) sorted by PC;
+    */
+    if (!read_dwarf(memory, eh_frame_hdr, &eh_hdr_info.eh_frame_ptr, eh_hdr_info.eh_frame_ptr_enc, &c)) return 0;
+    if (!read_dwarf(memory, eh_frame_hdr, &eh_hdr_info.fde_count, eh_hdr_info.fde_count_enc, &c)) return 0;
+    ALOGV("find_fde: found %d FDEs", eh_hdr_info.fde_count);
+
+    int32_t low = 0;
+    int32_t high = eh_hdr_info.fde_count;
+    uintptr_t start = 0;
+    uintptr_t fde = 0;
+    /* eh_frame_hdr + c points to lookup table at this point. */
+    while (low <= high) {
+        uint32_t mid = (high + low)/2;
+        uint32_t entry = c + mid * 8;
+        if (!read_dwarf(memory, eh_frame_hdr, &start, eh_hdr_info.fde_table_enc, &entry)) return 0;
+        if (pc <= start) {
+            high = mid - 1;
+        } else {
+            low = mid + 1;
+        }
+    }
+    /* Value found is at high. */
+    if (high < 0) {
+        ALOGV("find_fde: pc %x is out of FDE bounds: %x", pc, start);
+        return 0;
+    }
+    c += high * 8;
+    if (!read_dwarf(memory, eh_frame_hdr, &start, eh_hdr_info.fde_table_enc, &c)) return 0;
+    if (!read_dwarf(memory, eh_frame_hdr, &fde, eh_hdr_info.fde_table_enc, &c)) return 0;
+    ALOGV("pc 0x%x, ENTRY %d: start=0x%x, fde=0x%x", pc, high, start, fde);
+    return fde;
+}
+
+/* Execute single dwarf instruction and update dwarf state accordingly. */
+static bool execute_dwarf(const memory_t* memory, uintptr_t ptr, cie_info_t* cie_info,
+                          dwarf_state_t* dstate, uint32_t* cursor,
+                          dwarf_state_t* stack, uint8_t* stack_ptr) {
+    uint8_t inst;
+    uint8_t op = 0;
+
+    if (!try_get_byte(memory, ptr, &inst, cursor)) {
+        return false;
+    }
+    ALOGV("DW_CFA inst: 0x%x", inst);
+
+    /* For some instructions upper 2 bits is opcode and lower 6 bits is operand. See dwarf-2.0 7.23. */
+    if (inst & 0xc0) {
+        op = inst & 0x3f;
+        inst &= 0xc0;
+    }
+
+    switch ((dwarf_CFA)inst) {
+        uint32_t reg = 0;
+        uint32_t offset = 0;
+    case DW_CFA_advance_loc:
+        dstate->loc += op * cie_info->code_align;
+        ALOGV("DW_CFA_advance_loc: %d to 0x%x", op, dstate->loc);
+        break;
+    case DW_CFA_offset:
+        if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false;
+        dstate->regs[op].rule = 'o';
+        dstate->regs[op].value = offset * cie_info->data_align;
+        ALOGV("DW_CFA_offset: r%d = o(%d)", op, dstate->regs[op].value);
+        break;
+    case DW_CFA_restore:
+        dstate->regs[op].rule = stack->regs[op].rule;
+        dstate->regs[op].value = stack->regs[op].value;
+        ALOGV("DW_CFA_restore: r%d = %c(%d)", op, dstate->regs[op].rule, dstate->regs[op].value);
+        break;
+    case DW_CFA_nop:
+        break;
+    case DW_CFA_set_loc: // probably we don't have it on mips.
+        if (!try_get_xbytes(memory, ptr, &offset, 4, cursor)) return false;
+        if (offset < dstate->loc) {
+            ALOGE("DW_CFA_set_loc: attempt to move location backward");
+            return false;
+        }
+        dstate->loc = offset * cie_info->code_align;
+        ALOGV("DW_CFA_set_loc: %d to 0x%x", offset * cie_info->code_align, dstate->loc);
+        break;
+    case DW_CFA_advance_loc1:
+        if (!try_get_byte(memory, ptr, (uint8_t*)&offset, cursor)) return false;
+        dstate->loc += (uint8_t)offset * cie_info->code_align;
+        ALOGV("DW_CFA_advance_loc1: %d to 0x%x", (uint8_t)offset * cie_info->code_align, dstate->loc);
+        break;
+    case DW_CFA_advance_loc2:
+        if (!try_get_xbytes(memory, ptr, &offset, 2, cursor)) return false;
+        dstate->loc += (uint16_t)offset * cie_info->code_align;
+        ALOGV("DW_CFA_advance_loc2: %d to 0x%x", (uint16_t)offset * cie_info->code_align, dstate->loc);
+        break;
+    case DW_CFA_advance_loc4:
+        if (!try_get_xbytes(memory, ptr, &offset, 4, cursor)) return false;
+        dstate->loc += offset * cie_info->code_align;
+        ALOGV("DW_CFA_advance_loc4: %d to 0x%x", offset * cie_info->code_align, dstate->loc);
+        break;
+    case DW_CFA_offset_extended: // probably we don't have it on mips.
+        if (!try_get_uleb128(memory, ptr, &reg, cursor)) return false;
+        if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false;
+        if (reg >= DWARF_REGISTERS) {
+            ALOGE("DW_CFA_offset_extended: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS);
+            return false;
+        }
+        dstate->regs[reg].rule = 'o';
+        dstate->regs[reg].value = offset * cie_info->data_align;
+        ALOGV("DW_CFA_offset_extended: r%d = o(%d)", reg, dstate->regs[reg].value);
+        break;
+    case DW_CFA_restore_extended: // probably we don't have it on mips.
+        if (!try_get_uleb128(memory, ptr, &reg, cursor)) return false;
+        if (reg >= DWARF_REGISTERS) {
+            ALOGE("DW_CFA_restore_extended: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS);
+            return false;
+        }
+        dstate->regs[reg].rule = stack->regs[reg].rule;
+        dstate->regs[reg].value = stack->regs[reg].value;
+        ALOGV("DW_CFA_restore: r%d = %c(%d)", reg, dstate->regs[reg].rule, dstate->regs[reg].value);
+        break;
+    case DW_CFA_undefined: // probably we don't have it on mips.
+        if (!try_get_uleb128(memory, ptr, &reg, cursor)) return false;
+        if (reg >= DWARF_REGISTERS) {
+            ALOGE("DW_CFA_undefined: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS);
+            return false;
+        }
+        dstate->regs[reg].rule = 'u';
+        dstate->regs[reg].value = 0;
+        ALOGV("DW_CFA_undefined: r%d", reg);
+        break;
+    case DW_CFA_same_value: // probably we don't have it on mips.
+        if (!try_get_uleb128(memory, ptr, &reg, cursor)) return false;
+        if (reg >= DWARF_REGISTERS) {
+            ALOGE("DW_CFA_undefined: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS);
+            return false;
+        }
+        dstate->regs[reg].rule = 's';
+        dstate->regs[reg].value = 0;
+        ALOGV("DW_CFA_same_value: r%d", reg);
+        break;
+    case DW_CFA_register: // probably we don't have it on mips.
+        if (!try_get_uleb128(memory, ptr, &reg, cursor)) return false;
+        /* that's new register actually, not offset */
+        if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false;
+        if (reg >= DWARF_REGISTERS || offset >= DWARF_REGISTERS) {
+            ALOGE("DW_CFA_register: r%d or r%d exceeds supported number of registers (%d)", reg, offset, DWARF_REGISTERS);
+            return false;
+        }
+        dstate->regs[reg].rule = 'r';
+        dstate->regs[reg].value = offset;
+        ALOGV("DW_CFA_register: r%d = r(%d)", reg, dstate->regs[reg].value);
+        break;
+    case DW_CFA_remember_state:
+        if (*stack_ptr == DWARF_STATES_STACK) {
+            ALOGE("DW_CFA_remember_state: states stack overflow %d", *stack_ptr);
+            return false;
+        }
+        stack[(*stack_ptr)++] = *dstate;
+        ALOGV("DW_CFA_remember_state: stacktop moves to %d", *stack_ptr);
+        break;
+    case DW_CFA_restore_state:
+        /* We have CIE state saved at 0 position. It's not supposed to be taken
+           by DW_CFA_restore_state. */
+        if (*stack_ptr == 1) {
+            ALOGE("DW_CFA_restore_state: states stack is empty");
+            return false;
+        }
+        /* Don't touch location on restore. */
+        uintptr_t saveloc = dstate->loc;
+        *dstate = stack[--*stack_ptr];
+        dstate->loc = saveloc;
+        ALOGV("DW_CFA_restore_state: stacktop moves to %d", *stack_ptr);
+        break;
+    case DW_CFA_def_cfa:
+        if (!try_get_uleb128(memory, ptr, &reg, cursor)) return false;
+        if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false;
+        dstate->cfa_reg = reg;
+        dstate->cfa_off = offset;
+        ALOGV("DW_CFA_def_cfa: %x(r%d)", offset, reg);
+        break;
+    case DW_CFA_def_cfa_register:
+        if (!try_get_uleb128(memory, ptr, &reg, cursor)) {
+            return false;
+        }
+        dstate->cfa_reg = reg;
+        ALOGV("DW_CFA_def_cfa_register: r%d", reg);
+        break;
+    case DW_CFA_def_cfa_offset:
+        if (!try_get_uleb128(memory, ptr, &offset, cursor)) {
+            return false;
+        }
+        dstate->cfa_off = offset;
+        ALOGV("DW_CFA_def_cfa_offset: %x", offset);
+        break;
+    default:
+        ALOGE("unrecognized DW_CFA_* instruction: 0x%x", inst);
+        return false;
+    }
+    return true;
+}
+
+/* Restoring particular register value based on dwarf state. */
+static bool get_old_register_value(const memory_t* memory, uint32_t cfa,
+                                   dwarf_state_t* dstate, uint8_t reg,
+                                   unwind_state_t* state, unwind_state_t* newstate) {
+    uint32_t addr;
+    switch (dstate->regs[reg].rule) {
+    case 0:
+        /* We don't have dstate updated for this register, so assuming value kept the same.
+           Normally we should look into state and return current value as the old one
+           but we don't have all registers in state to handle this properly */
+        ALOGV("get_old_register_value: value of r%d is the same", reg);
+        // for SP if it's not updated by dwarf rule we assume it's equal to CFA
+        // for PC if it's not updated by dwarf rule we assume it's equal to RA
+        if (reg == DWARF_SP) {
+            ALOGV("get_old_register_value: adjusting sp to CFA: 0x%x", cfa);
+            newstate->reg[reg] = cfa;
+        } else if (reg == DWARF_PC) {
+            ALOGV("get_old_register_value: adjusting PC to RA: 0x%x", newstate->reg[DWARF_RA]);
+            newstate->reg[reg] = newstate->reg[DWARF_RA];
+        } else {
+            newstate->reg[reg] = state->reg[reg];
+        }
+        break;
+    case 'o':
+        addr = cfa + (int32_t)dstate->regs[reg].value;
+        if (!try_get_word(memory, addr, &newstate->reg[reg])) {
+            ALOGE("get_old_register_value: can't read from 0x%x", addr);
+            return false;
+        }
+        ALOGV("get_old_register_value: r%d at 0x%x is 0x%x", reg, addr, newstate->reg[reg]);
+        break;
+    case 'r':
+        /* We don't have all registers in state so don't even try to look at 'r' */
+        ALOGE("get_old_register_value: register lookup not implemented yet");
+        break;
+    default:
+        ALOGE("get_old_register_value: unexpected rule:%c value:%d for register %d",
+              dstate->regs[reg].rule, (int32_t)dstate->regs[reg].value, reg);
+        return false;
+    }
+    return true;
+}
+
+/* Updaing state based on dwarf state. */
+static bool update_state(const memory_t* memory, unwind_state_t* state,
+                         dwarf_state_t* dstate) {
+    unwind_state_t newstate;
+    /* We can restore more registers here if we need them. Meanwile doing minimal work here. */
+    /* Getting CFA. */
+    uintptr_t cfa = 0;
+    if (dstate->cfa_reg == DWARF_SP) {
+        cfa = state->reg[DWARF_SP] + dstate->cfa_off;
+    } else if (dstate->cfa_reg == DWARF_FP) {
+        cfa = state->reg[DWARF_FP] + dstate->cfa_off;
+    } else {
+        ALOGE("update_state: unexpected CFA register: %d", dstate->cfa_reg);
+        return false;
+    }
+    ALOGV("update_state: new CFA: 0x%x", cfa);
+
+    /* Update registers. Order is important to allow RA to propagate to PC */
+    /* Getting FP. */
+    if (!get_old_register_value(memory, cfa, dstate, DWARF_FP, state, &newstate)) return false;
+    /* Getting SP. */
+    if (!get_old_register_value(memory, cfa, dstate, DWARF_SP, state, &newstate)) return false;
+    /* Getting RA. */
+    if (!get_old_register_value(memory, cfa, dstate, DWARF_RA, state, &newstate)) return false;
+    /* Getting PC. */
+    if (!get_old_register_value(memory, cfa, dstate, DWARF_PC, state, &newstate)) return false;
+
+    ALOGV("update_state: PC: 0x%x; restore PC: 0x%x", state->reg[DWARF_PC], newstate.reg[DWARF_PC]);
+    ALOGV("update_state: RA: 0x%x; restore RA: 0x%x", state->reg[DWARF_RA], newstate.reg[DWARF_RA]);
+    ALOGV("update_state: FP: 0x%x; restore FP: 0x%x", state->reg[DWARF_FP], newstate.reg[DWARF_FP]);
+    ALOGV("update_state: SP: 0x%x; restore SP: 0x%x", state->reg[DWARF_SP], newstate.reg[DWARF_SP]);
+
+    if (newstate.reg[DWARF_PC] == 0)
+        return false;
+
+    /* End backtrace if registers do not change */
+    if ((state->reg[DWARF_PC] == newstate.reg[DWARF_PC]) &&
+        (state->reg[DWARF_RA] == newstate.reg[DWARF_RA]) &&
+        (state->reg[DWARF_FP] == newstate.reg[DWARF_FP]) &&
+        (state->reg[DWARF_SP] == newstate.reg[DWARF_SP]))
+        return false;
+
+    *state = newstate;
+    return true;
+}
+
+/* Execute CIE and FDE instructions for FDE found with find_fde. */
+static bool execute_fde(const memory_t* memory,
+                        uintptr_t fde,
+                        unwind_state_t* state) {
+    uint32_t fde_length = 0;
+    uint32_t cie_length = 0;
+    uintptr_t cie = 0;
+    uintptr_t cie_offset = 0;
+    cie_info_t cie_i;
+    cie_info_t* cie_info = &cie_i;
+    fde_info_t fde_i;
+    fde_info_t* fde_info = &fde_i;
+    dwarf_state_t dwarf_state;
+    dwarf_state_t* dstate = &dwarf_state;
+    dwarf_state_t stack[DWARF_STATES_STACK];
+    uint8_t stack_ptr = 0;
+
+    memset(dstate, 0, sizeof(dwarf_state_t));
+    memset(cie_info, 0, sizeof(cie_info_t));
+    memset(fde_info, 0, sizeof(fde_info_t));
+
+    /* Read common CIE or FDE area:
+       1st word is length;
+       2nd word is ID: 0 for CIE, CIE pointer for FDE.
+    */
+    if (!try_get_word(memory, fde, &fde_length)) {
+        return false;
+    }
+    if ((int32_t)fde_length == -1) {
+        ALOGV("execute_fde: 64-bit dwarf detected, not implemented yet");
+        return false;
+    }
+    if (!try_get_word(memory, fde + 4, &cie_offset)) {
+        return false;
+    }
+    if (cie_offset == 0) {
+        /* This is CIE. We shouldn't be here normally. */
+        cie = fde;
+        cie_length = fde_length;
+    } else {
+        /* Find CIE. */
+        /* Positive cie_offset goes backward from current field. */
+        cie = fde + 4 - cie_offset;
+        if (!try_get_word(memory, cie, &cie_length)) {
+            return false;
+        }
+        if ((int32_t)cie_length == -1) {
+            ALOGV("execute_fde: 64-bit dwarf detected, not implemented yet");
+            return false;
+        }
+        if (!try_get_word(memory, cie + 4, &cie_offset)) {
+            return false;
+        }
+        if (cie_offset != 0) {
+            ALOGV("execute_fde: can't find CIE");
+            return false;
+        }
+    }
+    ALOGV("execute_fde: FDE length: %d", fde_length);
+    ALOGV("execute_fde: CIE pointer: %x", cie);
+    ALOGV("execute_fde: CIE length: %d", cie_length);
+
+    /* Read CIE:
+       Augmentation independent:
+       1st byte is version;
+       next x bytes is /0 terminated augmentation string;
+       next x bytes is unsigned LEB128 encoded code alignment factor;
+       next x bytes is signed LEB128 encoded data alignment factor;
+       next 1 (CIE version 1) or x (CIE version 3 unsigned LEB128) bytes is return register column;
+       Augmentation dependent:
+       if 'z' next x bytes is unsigned LEB128 encoded augmentation data size;
+       if 'L' next 1 byte is LSDA encoding;
+       if 'R' next 1 byte is FDE encoding;
+       if 'S' CIE represents signal handler stack frame;
+       if 'P' next 1 byte is personality encoding folowed by personality function pointer;
+       Next x bytes is CIE program.
+    */
+
+    uint32_t c = 8;
+    if (!try_get_byte(memory, cie, &cie_info->version, &c)) {
+        return false;
+    }
+    ALOGV("execute_fde: CIE version: %d", cie_info->version);
+    uint8_t ch;
+    do {
+        if (!try_get_byte(memory, cie, &ch, &c)) {
+            return false;
+        }
+        switch (ch) {
+        case '\0': break;
+        case 'z': cie_info->aug_z = 1; break;
+        case 'L': cie_info->aug_L = 1; break;
+        case 'R': cie_info->aug_R = 1; break;
+        case 'S': cie_info->aug_S = 1; break;
+        case 'P': cie_info->aug_P = 1; break;
+        default:
+            ALOGV("execute_fde: Unrecognized CIE augmentation char: '%c'", ch);
+            return false;
+            break;
+        }
+    } while (ch);
+    if (!try_get_uleb128(memory, cie, &cie_info->code_align, &c)) {
+        return false;
+    }
+    if (!try_get_sleb128(memory, cie, &cie_info->data_align, &c)) {
+        return false;
+    }
+    if (cie_info->version >= 3) {
+        if (!try_get_uleb128(memory, cie, &cie_info->reg, &c)) {
+            return false;
+        }
+    } else {
+        if (!try_get_byte(memory, cie, (uint8_t*)&cie_info->reg, &c)) {
+            return false;
+        }
+    }
+    ALOGV("execute_fde: CIE code alignment factor: %d", cie_info->code_align);
+    ALOGV("execute_fde: CIE data alignment factor: %d", cie_info->data_align);
+    if (cie_info->aug_z) {
+        if (!try_get_uleb128(memory, cie, &cie_info->aug_z, &c)) {
+            return false;
+        }
+    }
+    if (cie_info->aug_L) {
+        if (!try_get_byte(memory, cie, &cie_info->aug_L, &c)) {
+            return false;
+        }
+    } else {
+        /* Default encoding. */
+        cie_info->aug_L = DW_EH_PE_absptr;
+    }
+    if (cie_info->aug_R) {
+        if (!try_get_byte(memory, cie, &cie_info->aug_R, &c)) {
+            return false;
+        }
+    } else {
+        /* Default encoding. */
+        cie_info->aug_R = DW_EH_PE_absptr;
+    }
+    if (cie_info->aug_P) {
+        /* Get encoding of personality routine pointer. We don't use it now. */
+        if (!try_get_byte(memory, cie, (uint8_t*)&cie_info->aug_P, &c)) {
+            return false;
+        }
+        /* Get routine pointer. */
+        if (!read_dwarf(memory, cie, &cie_info->aug_P, (uint8_t)cie_info->aug_P, &c)) {
+            return false;
+        }
+    }
+    /* CIE program. */
+    /* Length field itself (4 bytes) is not included into length. */
+    stack[0] = *dstate;
+    stack_ptr = 1;
+    while (c < cie_length + 4) {
+        if (!execute_dwarf(memory, cie, cie_info, dstate, &c, stack, &stack_ptr)) {
+            return false;
+        }
+    }
+
+    /* We went directly to CIE. Normally it shouldn't occur. */
+    if (cie == fde) return true;
+
+    /* Go back to FDE. */
+    c = 8;
+    /* Read FDE:
+       Augmentation independent:
+       next x bytes (encoded as specified in CIE) is FDE starting address;
+       next x bytes (encoded as specified in CIE) is FDE number of instructions covered;
+       Augmentation dependent:
+       if 'z' next x bytes is unsigned LEB128 encoded augmentation data size;
+       if 'L' next x bytes is LSDA pointer (encoded as specified in CIE);
+       Next x bytes is FDE program.
+    */
+    if (!read_dwarf(memory, fde, &fde_info->start, (uint8_t)cie_info->aug_R, &c)) {
+        return false;
+    }
+    dstate->loc = fde_info->start;
+    ALOGV("execute_fde: FDE start: %x", dstate->loc);
+    if (!read_dwarf(memory, fde, &fde_info->length, 0, &c)) {
+        return false;
+    }
+    ALOGV("execute_fde: FDE length: %x", fde_info->length);
+    if (cie_info->aug_z) {
+        if (!try_get_uleb128(memory, fde, &fde_info->aug_z, &c)) {
+            return false;
+        }
+    }
+    if (cie_info->aug_L && cie_info->aug_L != DW_EH_PE_omit) {
+        if (!read_dwarf(memory, fde, &fde_info->aug_L, cie_info->aug_L, &c)) {
+            return false;
+        }
+    }
+    /* FDE program. */
+    /* Length field itself (4 bytes) is not included into length. */
+    /* Save CIE state as 0 element of stack. Used by DW_CFA_restore. */
+    stack[0] = *dstate;
+    stack_ptr = 1;
+    while (c < fde_length + 4 && state->reg[DWARF_PC] >= dstate->loc) {
+        if (!execute_dwarf(memory, fde, cie_info, dstate, &c, stack, &stack_ptr)) {
+            return false;
+        }
+        ALOGV("PC: %x, LOC: %x", state->reg[DWARF_PC], dstate->loc);
+    }
+
+    return update_state(memory, state, dstate);
+}
+
+static bool heuristic_state_update(const memory_t* memory, unwind_state_t* state)
+{
+    bool found_start = false;
+    int maxcheck = 1024;
+    int32_t stack_size = 0;
+    int32_t ra_offset = 0;
+    dwarf_state_t dwarf_state;
+    dwarf_state_t* dstate = &dwarf_state;
+
+    static struct {
+        uint32_t insn;
+        uint32_t mask;
+    } frame0sig[] = {
+        {0x3c1c0000, 0xffff0000}, /* lui     gp,xxxx */
+        {0x279c0000, 0xffff0000}, /* addiu   gp,gp,xxxx */
+        {0x039fe021, 0xffffffff}, /* addu    gp,gp,ra */
+    };
+    const int nframe0sig = sizeof(frame0sig)/sizeof(frame0sig[0]);
+    int f0 = nframe0sig;
+    memset(dstate, 0, sizeof(dwarf_state_t));
+
+    /* Search code backwards looking for function prologue */
+    for (uint32_t pc = state->reg[DWARF_PC]-4; maxcheck-- > 0 && !found_start; pc -= 4) {
+        uint32_t op;
+        int32_t immediate;
+
+        if (!try_get_word(memory, pc, &op))
+            return false;
+
+        // ALOGV("@0x%08x: 0x%08x\n", pc, op);
+
+        // Check for frame 0 signature
+        if ((op & frame0sig[f0].mask) == frame0sig[f0].insn) {
+            if (f0 == 0)
+                return false;
+            f0--;
+        }
+        else {
+            f0 = nframe0sig;
+        }
+
+        switch (op & 0xffff0000) {
+        case 0x27bd0000: // addiu sp, imm
+            // looking for stack being decremented
+            immediate = (((int32_t)op) << 16) >> 16;
+            if (immediate < 0) {
+                stack_size = -immediate;
+                ALOGV("@0x%08x: found stack adjustment=%d\n", pc, stack_size);
+            }
+            break;
+        case 0x039f0000: // e021
+
+        case 0xafbf0000: // sw ra, imm(sp)
+            ra_offset = (((int32_t)op) << 16) >> 16;
+            ALOGV("@0x%08x: found ra offset=%d\n", pc, ra_offset);
+            break;
+        case 0x3c1c0000: // lui gp
+            ALOGV("@0x%08x: found function boundary", pc);
+            found_start = true;
+            break;
+        default:
+            break;
+        }
+    }
+
+    dstate->cfa_reg = DWARF_SP;
+    dstate->cfa_off = stack_size;
+
+    if (ra_offset) {
+        dstate->regs[DWARF_RA].rule = 'o';
+        dstate->regs[DWARF_RA].value = -stack_size + ra_offset;
+    }
+
+    return update_state(memory, state, dstate);
+}
+
 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;
 
+    ALOGV("Unwinding tid: %d", memory->tid);
+    ALOGV("PC: %x", state->reg[DWARF_PC]);
+    ALOGV("RA: %x", state->reg[DWARF_RA]);
+    ALOGV("FP: %x", state->reg[DWARF_FP]);
+    ALOGV("SP: %x", state->reg[DWARF_SP]);
+
     for (size_t index = 0; returned_frames < max_depth; index++) {
-        uintptr_t pc = index ? rewind_pc_arch(memory, state->pc) : state->pc;
-        backtrace_frame_t* frame;
-        uintptr_t addr;
-        int maxcheck = 1024;
-        int stack_size = 0, ra_offset = 0;
-        bool found_start = false;
+        uintptr_t fde = find_fde(memory, map_info_list, state->reg[DWARF_PC]);
+        backtrace_frame_t* frame = add_backtrace_entry(
+            index ? rewind_pc_arch(memory, state->reg[DWARF_PC]) : state->reg[DWARF_PC],
+            backtrace, ignore_depth, max_depth,
+            &ignored_frames, &returned_frames);
+        uint32_t stack_top = state->reg[DWARF_SP];
 
-        frame = add_backtrace_entry(pc, backtrace, ignore_depth,
-                                    max_depth, &ignored_frames, &returned_frames);
+        if (fde) {
+            /* Use FDE to update state */
+            if (!execute_fde(memory, fde, state))
+                break;
+        }
+        else {
+            /* FDE is not found, update state heuristically */
+            if (!heuristic_state_update(memory, state))
+                break;
+        }
 
-        if (frame)
-            frame->stack_top = state->sp;
-
-        ALOGV("#%d: frame=%p pc=%08x sp=%08x\n",
-              index, frame, frame->absolute_pc, frame->stack_top);
-
-        for (addr = state->pc; maxcheck-- > 0 && !found_start; addr -= 4) {
-            uint32_t op;
-            if (!try_get_word(memory, addr, &op))
-                break;
-
-            // ALOGV("@0x%08x: 0x%08x\n", addr, op);
-            switch (op & 0xffff0000) {
-            case 0x27bd0000: // addiu sp, imm
-                {
-                    // looking for stack being decremented
-                    int32_t immediate = ((((int)op) << 16) >> 16);
-                    if (immediate < 0) {
-                        stack_size = -immediate;
-                        found_start = true;
-                        ALOGV("@0x%08x: found stack adjustment=%d\n", addr, stack_size);
-                    }
-                }
-                break;
-            case 0xafbf0000: // sw ra, imm(sp)
-                ra_offset = ((((int)op) << 16) >> 16);
-                ALOGV("@0x%08x: found ra offset=%d\n", addr, ra_offset);
-                break;
-            case 0x3c1c0000: // lui gp
-                ALOGV("@0x%08x: found function boundary\n", addr);
-                found_start = true;
-                break;
-            default:
-                break;
+        if (frame) {
+            frame->stack_top = stack_top;
+            if (stack_top < state->reg[DWARF_SP]) {
+                frame->stack_size = state->reg[DWARF_SP] - stack_top;
             }
         }
-
-        if (ra_offset) {
-            uint32_t next_ra;
-            if (!try_get_word(memory, state->sp + ra_offset, &next_ra))
-                break;
-            state->ra = next_ra;
-            ALOGV("New ra: 0x%08x\n", state->ra);
-        }
-
-        if (stack_size) {
-            if (frame)
-                frame->stack_size = stack_size;
-            state->sp += stack_size;
-            ALOGV("New sp: 0x%08x\n", state->sp);
-        }
-
-        if (state->pc == state->ra && stack_size == 0)
-            break;
-
-        if (state->ra == 0)
-            break;
-
-        state->pc = state->ra;
+        ALOGV("Stack: 0x%x ... 0x%x - %d bytes", frame->stack_top, state->reg[DWARF_SP], frame->stack_size);
     }
-
-    ALOGV("returning %d frames\n", returned_frames);
-
     return returned_frames;
 }
 
-ssize_t unwind_backtrace_signal_arch(siginfo_t* siginfo, void* sigcontext,
+ssize_t unwind_backtrace_signal_arch(siginfo_t* siginfo __attribute__((unused)), 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;
 
     unwind_state_t state;
-    state.sp = uc->sp;
-    state.pc = uc->pc;
-    state.ra = uc->ra;
+    state.reg[DWARF_PC] = uc->sc_pc;
+    state.reg[DWARF_RA] = uc->sc_regs[REG_RA];
+    state.reg[DWARF_FP] = uc->sc_regs[REG_S8];
+    state.reg[DWARF_SP] = uc->sc_regs[REG_SP];
 
     ALOGV("unwind_backtrace_signal_arch: "
           "ignore_depth=%d max_depth=%d pc=0x%08x sp=0x%08x ra=0x%08x\n",
-          ignore_depth, max_depth, state.pc, state.sp, state.ra);
+          ignore_depth, max_depth, state.reg[DWARF_PC], state.reg[DWARF_SP], state.reg[DWARF_RA]);
 
     memory_t memory;
     init_memory(&memory, map_info_list);
     return unwind_backtrace_common(&memory, map_info_list,
-            &state, backtrace, ignore_depth, max_depth);
+                                   &state, backtrace, ignore_depth, max_depth);
 }
 
 ssize_t unwind_backtrace_ptrace_arch(pid_t tid, const ptrace_context_t* context,
@@ -182,16 +920,17 @@
     }
 
     unwind_state_t state;
-    state.sp = regs.regs[29];
-    state.ra = regs.regs[31];
-    state.pc = regs.epc;
+    state.reg[DWARF_PC] = regs.epc;
+    state.reg[DWARF_RA] = regs.regs[REG_RA];
+    state.reg[DWARF_FP] = regs.regs[REG_S8];
+    state.reg[DWARF_SP] = regs.regs[REG_SP];
 
     ALOGV("unwind_backtrace_ptrace_arch: "
           "ignore_depth=%d max_depth=%d pc=0x%08x sp=0x%08x ra=0x%08x\n",
-          ignore_depth, max_depth, state.pc, state.sp, state.ra);
+          ignore_depth, max_depth, state.reg[DWARF_PC], state.reg[DWARF_SP], state.reg[DWARF_RA]);
 
     memory_t memory;
     init_memory_ptrace(&memory, tid);
     return unwind_backtrace_common(&memory, context->map_info_list,
-            &state, backtrace, ignore_depth, max_depth);
+                                   &state, backtrace, ignore_depth, max_depth);
 }
diff --git a/libcorkscrew/arch-mips/dwarf.h b/libcorkscrew/arch-mips/dwarf.h
new file mode 100644
index 0000000..8504ea0
--- /dev/null
+++ b/libcorkscrew/arch-mips/dwarf.h
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Dwarf2 data encoding flags.
+ */
+
+#define DW_EH_PE_absptr         0x00
+#define DW_EH_PE_omit           0xff
+#define DW_EH_PE_uleb128        0x01
+#define DW_EH_PE_udata2         0x02
+#define DW_EH_PE_udata4         0x03
+#define DW_EH_PE_udata8         0x04
+#define DW_EH_PE_sleb128        0x09
+#define DW_EH_PE_sdata2         0x0A
+#define DW_EH_PE_sdata4         0x0B
+#define DW_EH_PE_sdata8         0x0C
+#define DW_EH_PE_signed         0x08
+#define DW_EH_PE_pcrel          0x10
+#define DW_EH_PE_textrel        0x20
+#define DW_EH_PE_datarel        0x30
+#define DW_EH_PE_funcrel        0x40
+#define DW_EH_PE_aligned        0x50
+#define DW_EH_PE_indirect       0x80
+
+/*
+ * Dwarf2 call frame instructions.
+ */
+
+typedef enum {
+    DW_CFA_advance_loc = 0x40,
+    DW_CFA_offset = 0x80,
+    DW_CFA_restore = 0xc0,
+    DW_CFA_nop = 0x00,
+    DW_CFA_set_loc = 0x01,
+    DW_CFA_advance_loc1 = 0x02,
+    DW_CFA_advance_loc2 = 0x03,
+    DW_CFA_advance_loc4 = 0x04,
+    DW_CFA_offset_extended = 0x05,
+    DW_CFA_restore_extended = 0x06,
+    DW_CFA_undefined = 0x07,
+    DW_CFA_same_value = 0x08,
+    DW_CFA_register = 0x09,
+    DW_CFA_remember_state = 0x0a,
+    DW_CFA_restore_state = 0x0b,
+    DW_CFA_def_cfa = 0x0c,
+    DW_CFA_def_cfa_register = 0x0d,
+    DW_CFA_def_cfa_offset = 0x0e
+} dwarf_CFA;
+
+/*
+ * eh_frame_hdr information.
+*/
+
+typedef struct {
+      uint8_t version;
+      uint8_t eh_frame_ptr_enc;
+      uint8_t fde_count_enc;
+      uint8_t fde_table_enc;
+      uintptr_t eh_frame_ptr;
+      uint32_t fde_count;
+} eh_frame_hdr_info_t;
+
+/*
+ * CIE information.
+*/
+
+typedef struct {
+      uint8_t version;
+      uint32_t code_align;
+      uint32_t data_align;
+      uint32_t reg;
+      uint32_t aug_z;
+      uint8_t aug_L;
+      uint8_t aug_R;
+      uint8_t aug_S;
+      uint32_t aug_P;
+} cie_info_t;
+
+/*
+ * FDE information.
+*/
+
+typedef struct {
+      uint32_t start;
+      uint32_t length; // number of instructions covered by FDE
+      uint32_t aug_z;
+      uint32_t aug_L;
+} fde_info_t;
+
+/*
+ * Dwarf state.
+*/
+
+/* Stack of states: required for DW_CFA_remember_state/DW_CFA_restore_state
+   30 should be enough */
+#define DWARF_STATES_STACK 30
+
+typedef struct {
+    char rule;         // rule: o - offset(value); r - register(value)
+    uint32_t value;    // value
+} reg_rule_t;
+
+/* Dwarf preserved number of registers for mips */
+typedef enum
+  {
+    UNW_MIPS_R0,
+    UNW_MIPS_R1,
+    UNW_MIPS_R2,
+    UNW_MIPS_R3,
+    UNW_MIPS_R4,
+    UNW_MIPS_R5,
+    UNW_MIPS_R6,
+    UNW_MIPS_R7,
+    UNW_MIPS_R8,
+    UNW_MIPS_R9,
+    UNW_MIPS_R10,
+    UNW_MIPS_R11,
+    UNW_MIPS_R12,
+    UNW_MIPS_R13,
+    UNW_MIPS_R14,
+    UNW_MIPS_R15,
+    UNW_MIPS_R16,
+    UNW_MIPS_R17,
+    UNW_MIPS_R18,
+    UNW_MIPS_R19,
+    UNW_MIPS_R20,
+    UNW_MIPS_R21,
+    UNW_MIPS_R22,
+    UNW_MIPS_R23,
+    UNW_MIPS_R24,
+    UNW_MIPS_R25,
+    UNW_MIPS_R26,
+    UNW_MIPS_R27,
+    UNW_MIPS_R28,
+    UNW_MIPS_R29,
+    UNW_MIPS_R30,
+    UNW_MIPS_R31,
+
+    UNW_MIPS_PC = 34,
+
+    /* FIXME: Other registers!  */
+
+    /* For MIPS, the CFA is the value of SP (r29) at the call site in the
+       previous frame.  */
+    UNW_MIPS_CFA,
+
+    UNW_TDEP_LASTREG,
+
+    UNW_TDEP_LAST_REG = UNW_MIPS_R31,
+
+    UNW_TDEP_IP = UNW_MIPS_R31,
+    UNW_TDEP_SP = UNW_MIPS_R29,
+    UNW_TDEP_EH = UNW_MIPS_R0   /* FIXME.  */
+
+  }
+mips_regnum_t;
+
+#define DWARF_REGISTERS UNW_TDEP_LASTREG
+
+typedef struct {
+    uintptr_t loc;     // location (ip)
+    uint8_t cfa_reg;   // index of register where CFA location stored
+    intptr_t cfa_off;  // offset
+    reg_rule_t regs[DWARF_REGISTERS]; // dwarf preserved registers for mips
+} dwarf_state_t;
+
+/* DWARF registers we are caring about. */
+
+
+#define DWARF_SP      UNW_MIPS_R29
+#define DWARF_RA      UNW_MIPS_R31
+#define DWARF_PC      UNW_MIPS_PC
+#define DWARF_FP      UNW_MIPS_CFA /* FIXME is this correct? */
diff --git a/libcorkscrew/arch-mips/ptrace-mips.c b/libcorkscrew/arch-mips/ptrace-mips.c
index f0ea110..ba3b60a 100644
--- a/libcorkscrew/arch-mips/ptrace-mips.c
+++ b/libcorkscrew/arch-mips/ptrace-mips.c
@@ -19,10 +19,59 @@
 
 #include "../ptrace-arch.h"
 
+#include <stddef.h>
+#include <elf.h>
 #include <cutils/log.h>
 
-void load_ptrace_map_info_data_arch(pid_t pid, map_info_t* mi, map_info_data_t* data) {
+static void load_eh_frame_hdr(pid_t pid, map_info_t* mi, uintptr_t *eh_frame_hdr) {
+    uint32_t elf_phoff;
+    uint32_t elf_phentsize_ehsize;
+    uint32_t elf_shentsize_phnum;
+
+
+    try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phoff), &elf_phoff);
+    ALOGV("reading 0x%08x elf_phoff:%x",  mi->start + offsetof(Elf32_Ehdr, e_phoff), elf_phoff);
+    try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_ehsize), &elf_phentsize_ehsize);
+    ALOGV("reading 0x%08x elf_phentsize_ehsize:%x", mi->start + offsetof(Elf32_Ehdr, e_ehsize), elf_phentsize_ehsize);
+    try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phnum), &elf_shentsize_phnum);
+    ALOGV("reading 0x%08x elf_shentsize_phnum:%x", mi->start + offsetof(Elf32_Ehdr, e_phnum), elf_shentsize_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_ehsize),
+                    &elf_phentsize_ehsize)
+            && try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phnum),
+                    &elf_shentsize_phnum)) {
+        uint32_t elf_phentsize = elf_phentsize_ehsize >> 16;
+        uint32_t elf_phnum = elf_shentsize_phnum & 0xffff;
+        for (uint32_t i = 0; i < elf_phnum; i++) {
+            uintptr_t elf_phdr = mi->start + elf_phoff + i * elf_phentsize;
+            uint32_t 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_GNU_EH_FRAME) {
+                uint32_t elf_phdr_offset;
+                if (!try_get_word_ptrace(pid, elf_phdr + offsetof(Elf32_Phdr, p_offset),
+                        &elf_phdr_offset)) {
+                    break;
+                }
+                *eh_frame_hdr = mi->start + elf_phdr_offset;
+                ALOGV("Parsed .eh_frame_hdr info for %s: start=0x%08x", mi->name, *eh_frame_hdr);
+                return;
+            }
+        }
+    }
+    *eh_frame_hdr = 0;
 }
 
-void free_ptrace_map_info_data_arch(map_info_t* mi, map_info_data_t* data) {
+void load_ptrace_map_info_data_arch(pid_t pid, map_info_t* mi, map_info_data_t* data) {
+    ALOGV("load_ptrace_map_info_data_arch");
+    load_eh_frame_hdr(pid, mi, &data->eh_frame_hdr);
+}
+
+void free_ptrace_map_info_data_arch(map_info_t* mi __attribute__((unused)),
+                                    map_info_data_t* data __attribute__((unused))) {
+    ALOGV("free_ptrace_map_info_data_arch");
 }
diff --git a/libcorkscrew/ptrace-arch.h b/libcorkscrew/ptrace-arch.h
index 4451c29..0bcff63 100755
--- a/libcorkscrew/ptrace-arch.h
+++ b/libcorkscrew/ptrace-arch.h
@@ -33,6 +33,8 @@
 #ifdef __arm__
     uintptr_t exidx_start;
     size_t exidx_size;
+#elif __mips__
+    uintptr_t eh_frame_hdr;
 #elif __i386__
     uintptr_t eh_frame_hdr;
 #endif