diff --git a/include/dwarf.h b/include/dwarf.h index 633868b..3fe3a3f 100644 --- a/include/dwarf.h +++ b/include/dwarf.h @@ -311,6 +311,7 @@ typedef struct dwarf_cursor unw_word_t ret_addr_column; /* column for return-address */ unw_word_t eh_args[UNW_TDEP_NUM_EH_REGS]; unsigned int eh_valid_mask; + unsigned int frame; dwarf_loc_t loc[DWARF_NUM_PRESERVED_REGS]; diff --git a/include/libunwind-aarch64.h b/include/libunwind-aarch64.h index cd01e57..3be8251 100644 --- a/include/libunwind-aarch64.h +++ b/include/libunwind-aarch64.h @@ -168,15 +168,37 @@ typedef struct unw_tdep_save_loc } unw_tdep_save_loc_t; - /* On AArch64, we can directly use ucontext_t as the unwind context. */ typedef ucontext_t unw_tdep_context_t; #include "libunwind-common.h" #include "libunwind-dynamic.h" -#define unw_tdep_getcontext(uc) (getcontext (uc), 0) -#define unw_tdep_is_fpreg UNW_ARCH_OBJ(is_fpreg) +/* There is no getcontext in Android. */ +#define unw_tdep_getcontext(uc) (({ \ + unw_tdep_context_t *unw_ctx = (uc); \ + register uint64_t *unw_base asm ("x0") = (uint64_t*) unw_ctx->uc_mcontext.regs; \ + __asm__ __volatile__ ( \ + "stp x0, x1, [%[base], #0]\n" \ + "stp x2, x3, [%[base], #16]\n" \ + "stp x4, x5, [%[base], #32]\n" \ + "stp x6, x7, [%[base], #48]\n" \ + "stp x8, x9, [%[base], #64]\n" \ + "stp x10, x11, [%[base], #80]\n" \ + "stp x12, x13, [%[base], #96]\n" \ + "stp x14, x13, [%[base], #112]\n" \ + "stp x16, x17, [%[base], #128]\n" \ + "stp x18, x19, [%[base], #144]\n" \ + "stp x20, x21, [%[base], #160]\n" \ + "stp x22, x23, [%[base], #176]\n" \ + "stp x24, x25, [%[base], #192]\n" \ + "stp x26, x27, [%[base], #208]\n" \ + "stp x28, x29, [%[base], #224]\n" \ + "str x30, [%[base], #240]\n" \ + "mov x1, sp\n" \ + "stp x1, x30, [%[base], #248]\n" \ + : [base] "+r" (unw_base) : : "x1", "memory"); \ + }), 0) extern int unw_tdep_is_fpreg (int); diff --git a/src/aarch64/Gstep.c b/src/aarch64/Gstep.c index 0c35f98..2fe00ba 100644 --- a/src/aarch64/Gstep.c +++ b/src/aarch64/Gstep.c @@ -121,6 +121,30 @@ unw_step (unw_cursor_t *cursor) ret = dwarf_step (&c->dwarf); Debug(1, "dwarf_step()=%d\n", ret); + if (ret < 0 && c->dwarf.frame == 0) + { + /* If this is the first frame, the code may be executing garbage + * in the middle of nowhere. In this case, try using the lr as + * the pc. + */ + unw_word_t lr; + if (dwarf_get(&c->dwarf, c->dwarf.loc[UNW_AARCH64_X30], &lr) >= 0) + { + if (lr != c->dwarf.ip) + { + ret = 1; + c->dwarf.ip = lr; + } + } + } + + if (ret >= 0) + { + if (c->dwarf.ip >= 4) + c->dwarf.ip -= 4; + c->dwarf.frame++; + } + if (unlikely (ret == -UNW_ESTOPUNWIND)) return ret; diff --git a/src/aarch64/init.h b/src/aarch64/init.h index 0cedc1a..c418932 100644 --- a/src/aarch64/init.h +++ b/src/aarch64/init.h @@ -122,6 +122,7 @@ common_init (struct cursor *c, unsigned use_prev_instr) c->dwarf.pi_is_dynamic = 0; c->dwarf.hint = 0; c->dwarf.prev_rs = 0; + c->dwarf.frame = 0; return 0; } diff --git a/src/arm/Gstep.c b/src/arm/Gstep.c index 79f2dd2..28aa4c0 100644 --- a/src/arm/Gstep.c +++ b/src/arm/Gstep.c @@ -73,6 +73,39 @@ arm_exidx_step (struct cursor *c) return (c->dwarf.ip == 0) ? 0 : 1; } +/* When taking a step back up the stack, the pc will point to the next + * instruction to execute, not the currently executing instruction. This + * function adjusts the pc to the currently executing instruction. + */ +static void adjust_ip(struct cursor *c) +{ + unw_word_t ip, value; + ip = c->dwarf.ip; + + if (ip) + { + int adjust = 4; + if (ip & 1) + { + /* Thumb instructions, the currently executing instruction could be + * 2 or 4 bytes, so adjust appropriately. + */ + unw_addr_space_t as; + unw_accessors_t *a; + void *arg; + + as = c->dwarf.as; + a = unw_get_accessors (as); + arg = c->dwarf.as_arg; + + if (ip < 5 || (*a->access_mem) (as, ip-5, &value, 0, arg) < 0 || + (value & 0xe000f000) != 0xe000f000) + adjust = 2; + } + c->dwarf.ip -= adjust; + } +} + PROTECTED int unw_handle_signal_frame (unw_cursor_t *cursor) { @@ -268,5 +301,28 @@ unw_step (unw_cursor_t *cursor) } } } + + if (ret < 0 && c->dwarf.frame == 0) + { + /* If this is the first frame, the code may be executing garbage + * in the middle of nowhere. In this case, try using the lr as + * the pc. + */ + unw_word_t lr; + if (dwarf_get(&c->dwarf, c->dwarf.loc[UNW_ARM_R14], &lr) >= 0) + { + if (lr != c->dwarf.ip) + { + ret = 1; + c->dwarf.ip = lr; + } + } + } + + if (ret >= 0) + { + c->dwarf.frame++; + } + return ret == -UNW_ENOINFO ? 0 : 1; } diff --git a/src/arm/init.h b/src/arm/init.h index 6379d8e..8222c02 100644 --- a/src/arm/init.h +++ b/src/arm/init.h @@ -73,6 +73,7 @@ common_init (struct cursor *c, unsigned use_prev_instr) c->dwarf.pi_is_dynamic = 0; c->dwarf.hint = 0; c->dwarf.prev_rs = 0; + c->dwarf.frame = 0; return 0; } diff --git a/src/ptrace/_UPT_access_fpreg.c b/src/ptrace/_UPT_access_fpreg.c index e90ec47d..ae61d093 100644 --- a/src/ptrace/_UPT_access_fpreg.c +++ b/src/ptrace/_UPT_access_fpreg.c @@ -46,8 +46,8 @@ _UPT_access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val, #ifdef HAVE_TTRACE # warning No support for ttrace() yet. #else - ptrace (PTRACE_POKEUSER, pid, _UPT_reg_offset[reg] + i * sizeof(wp[i]), - wp[i]); + ptrace (PTRACE_POKEUSER, pid, (void*) (_UPT_reg_offset[reg] + i * sizeof(wp[i])), + (void*) wp[i]); #endif if (errno) return -UNW_EBADREG; @@ -59,14 +59,14 @@ _UPT_access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val, # warning No support for ttrace() yet. #else wp[i] = ptrace (PTRACE_PEEKUSER, pid, - _UPT_reg_offset[reg] + i * sizeof(wp[i]), 0); + (void*) (_UPT_reg_offset[reg] + i * sizeof(wp[i])), 0); #endif if (errno) return -UNW_EBADREG; } return 0; } -#elif HAVE_DECL_PT_GETFPREGS +#elif HAVE_DECL_PT_GETFPREGS && !defined(__aarch64__) int _UPT_access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val, int write, void *arg) @@ -100,6 +100,14 @@ _UPT_access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val, #endif return 0; } +#elif defined(__aarch64__) +int +_UPT_access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val, + int write, void *arg) +{ +# pragma message("_UPT_access_fpreg is not implemented and not currently used.") + return -UNW_EBADREG; +} #else #error Fix me #endif diff --git a/src/ptrace/_UPT_access_reg.c b/src/ptrace/_UPT_access_reg.c index ae71608..8d40bcc 100644 --- a/src/ptrace/_UPT_access_reg.c +++ b/src/ptrace/_UPT_access_reg.c @@ -32,6 +32,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ # include # endif # include "tdep-ia64/rse.h" +#elif defined(__aarch64__) +# include #endif #if HAVE_DECL_PTRACE_POKEUSER || HAVE_TTRACE @@ -239,13 +241,13 @@ _UPT_access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, #else errno = 0; if (write) - ptrace (PTRACE_POKEUSER, pid, _UPT_reg_offset[reg], *val); + ptrace (PTRACE_POKEUSER, pid, (void*) (uintptr_t) _UPT_reg_offset[reg], (void*) *val); else { #if UNW_DEBUG Debug(16, "ptrace PEEKUSER pid: %lu , reg: %lu , offs: %lu\n", (unsigned long)pid, (unsigned long)reg, (unsigned long)_UPT_reg_offset[reg]); #endif - *val = ptrace (PTRACE_PEEKUSER, pid, _UPT_reg_offset[reg], 0); + *val = ptrace (PTRACE_PEEKUSER, pid, (void*) (uintptr_t) _UPT_reg_offset[reg], 0); } if (errno) { #if UNW_DEBUG @@ -304,6 +306,60 @@ _UPT_access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, Debug (1, "bad register %s [%u] (error: %s)\n", unw_regname(reg), reg, strerror (errno)); return -UNW_EBADREG; } +#elif HAVE_DECL_PT_GETREGSET +int +_UPT_access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, + int write, void *arg) +{ + struct UPT_info *ui = arg; + pid_t pid = ui->pid; +#if defined(__aarch64__) + struct user_pt_regs regs; + struct iovec io; + io.iov_base = ®s; + io.iov_len = sizeof(regs); + +#if UNW_DEBUG + Debug(16, "using getregset: reg: %s [%u], val: %lx, write: %u\n", unw_regname(reg), (unsigned) reg, (long) val, write); + + if (write) + Debug (16, "%s [%u] <- %lx\n", unw_regname (reg), (unsigned) reg, (long) *val); +#endif + if (ptrace(PTRACE_GETREGSET, pid, (void*)NT_PRSTATUS, (void*)&io) == -1) + goto badreg; + if (write) + { + if (reg == UNW_AARCH64_SP) + regs.sp = *val; + else if (reg == UNW_AARCH64_PC) + regs.pc = *val; + else if (reg < UNW_AARCH64_SP) + regs.regs[reg] = *val; + else + goto badreg; + if (ptrace(PTRACE_SETREGSET, pid, (void*)NT_PRSTATUS, (void*)&io) == -1) + goto badreg; + } + else + { + if (reg == UNW_AARCH64_SP) + *val = regs.sp; + else if (reg == UNW_AARCH64_PC) + *val = regs.pc; + else if (reg < UNW_AARCH64_SP) + *val = regs.regs[reg]; + else + goto badreg; + } +#else +#error Unsupported architecture for getregset +#endif + return 0; + + badreg: + Debug (1, "bad register %s [%u] (error: %s)\n", unw_regname(reg), reg, strerror (errno)); + return -UNW_EBADREG; +} #else #error Port me #endif diff --git a/src/x86/Gos-linux.c b/src/x86/Gos-linux.c index 17aebc2..bafca96 100644 --- a/src/x86/Gos-linux.c +++ b/src/x86/Gos-linux.c @@ -283,7 +283,9 @@ HIDDEN int x86_local_resume (unw_addr_space_t as, unw_cursor_t *cursor, void *arg) { struct cursor *c = (struct cursor *) cursor; +#if !defined(__ANDROID__) ucontext_t *uc = c->uc; +#endif /* Ensure c->pi is up-to-date. On x86, it's relatively common to be missing DWARF unwind info. We don't want to fail in that case, @@ -296,12 +298,16 @@ x86_local_resume (unw_addr_space_t as, unw_cursor_t *cursor, void *arg) struct sigcontext *sc = (struct sigcontext *) c->sigcontext_addr; Debug (8, "resuming at ip=%x via sigreturn(%p)\n", c->dwarf.ip, sc); +#if !defined(__ANDROID__) sigreturn (sc); +#endif } else { Debug (8, "resuming at ip=%x via setcontext()\n", c->dwarf.ip); +#if !defined(__ANDROID__) setcontext (uc); +#endif } return -UNW_EINVAL; } diff --git a/src/x86/Gstep.c b/src/x86/Gstep.c index 10e2cbc..49a830d 100644 --- a/src/x86/Gstep.c +++ b/src/x86/Gstep.c @@ -110,6 +110,20 @@ unw_step (unw_cursor_t *cursor) else c->dwarf.ip = 0; } + + if (ret >= 0) + { + if (c->dwarf.ip) + { + /* Adjust the pc to the instruction before. */ + c->dwarf.ip--; + } + c->dwarf.frame++; + } + + if (unlikely (ret <= 0)) + return 0; + ret = (c->dwarf.ip == 0) ? 0 : 1; Debug (2, "returning %d\n", ret); return ret; diff --git a/src/x86/init.h b/src/x86/init.h index 027aedc..4218aa3 100644 --- a/src/x86/init.h +++ b/src/x86/init.h @@ -65,6 +65,7 @@ common_init (struct cursor *c, unsigned use_prev_instr) c->dwarf.pi_is_dynamic = 0; c->dwarf.hint = 0; c->dwarf.prev_rs = 0; + c->dwarf.frame = 0; return 0; } diff --git a/src/x86_64/init.h b/src/x86_64/init.h index 442b2bf..737b9f7 100644 --- a/src/x86_64/init.h +++ b/src/x86_64/init.h @@ -84,6 +84,7 @@ common_init (struct cursor *c, unsigned use_prev_instr) c->dwarf.pi_is_dynamic = 0; c->dwarf.hint = 0; c->dwarf.prev_rs = 0; + c->dwarf.frame = 0; return 0; }