diff --git a/src/aarch64/Gstep.c b/src/aarch64/Gstep.c index 44fbb04c..e38ff979 100644 --- a/src/aarch64/Gstep.c +++ b/src/aarch64/Gstep.c @@ -27,6 +27,30 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "unwind_i.h" #include "offsets.h" +/* Recognise PLT entries such as: + 40ddf0: b0000570 adrp x16, 4ba000 <_GLOBAL_OFFSET_TABLE_+0x2a8> + 40ddf4: f9433611 ldr x17, [x16,#1640] + 40ddf8: 9119a210 add x16, x16, #0x668 + 40ddfc: d61f0220 br x17 */ +static int +is_plt_entry (struct dwarf_cursor *c) +{ + unw_word_t w0, w1; + unw_accessors_t *a; + int ret; + + a = unw_get_accessors (c->as); + if ((ret = (*a->access_mem) (c->as, c->ip, &w0, 0, c->as_arg)) < 0 + || (ret = (*a->access_mem) (c->as, c->ip + 8, &w1, 0, c->as_arg)) < 0) + return 0; + + ret = (((w0 & 0xff0000009f000000) == 0xf900000090000000) + && ((w1 & 0xffffffffff000000) == 0xd61f022091000000)); + + Debug (14, "ip=0x%lx => 0x%016lx 0x%016lx, ret = %d\n", c->ip, w0, w1, ret); + return ret; +} + PROTECTED int unw_handle_signal_frame (unw_cursor_t *cursor) { @@ -101,6 +125,7 @@ unw_handle_signal_frame (unw_cursor_t *cursor) dwarf_get (&c->dwarf, c->dwarf.loc[UNW_AARCH64_PC], &c->dwarf.ip); c->dwarf.pi_valid = 0; + c->dwarf.use_prev_instr = 0; return 1; } @@ -125,7 +150,40 @@ unw_step (unw_cursor_t *cursor) return ret; if (unlikely (ret < 0)) - return 0; + { + /* DWARF failed. */ + if (is_plt_entry (&c->dwarf)) + { + Debug (2, "found plt entry\n"); + c->frame_info.frame_type = UNW_AARCH64_FRAME_STANDARD; + } + else + { + Debug (2, "fallback\n"); + c->frame_info.frame_type = UNW_AARCH64_FRAME_GUESSED; + } + /* Use link register (X30). */ + c->frame_info.cfa_reg_offset = 0; + c->frame_info.cfa_reg_sp = 0; + c->frame_info.fp_cfa_offset = -1; + c->frame_info.lr_cfa_offset = -1; + c->frame_info.sp_cfa_offset = -1; + c->dwarf.loc[UNW_AARCH64_PC] = c->dwarf.loc[UNW_AARCH64_X30]; + c->dwarf.loc[UNW_AARCH64_X30] = DWARF_NULL_LOC; + if (!DWARF_IS_NULL_LOC (c->dwarf.loc[UNW_AARCH64_PC])) + { + ret = dwarf_get (&c->dwarf, c->dwarf.loc[UNW_AARCH64_PC], &c->dwarf.ip); + if (ret < 0) + { + Debug (2, "failed to get pc from link register: %d\n", ret); + return ret; + } + Debug (2, "link register (x30) = 0x%016lx\n", c->dwarf.ip); + ret = 1; + } + else + c->dwarf.ip = 0; + } return (c->dwarf.ip == 0) ? 0 : 1; } diff --git a/src/dwarf/Gfde.c b/src/dwarf/Gfde.c index 55d8da84..49484eee 100644 --- a/src/dwarf/Gfde.c +++ b/src/dwarf/Gfde.c @@ -32,7 +32,7 @@ is_cie_id (unw_word_t val, int is_debug_frame) 0xffffffffffffffff (for 64-bit ELF). However, .eh_frame uses 0. */ if (is_debug_frame) - return (val == - (uint32_t) 1 || val == - (uint64_t) 1); + return (val == (uint32_t)(-1) || val == (uint64_t)(-1)); else return (val == 0); } diff --git a/src/dwarf/Gfind_proc_info-lsb.c b/src/dwarf/Gfind_proc_info-lsb.c index 30f8b68f..d8cbc3e0 100644 --- a/src/dwarf/Gfind_proc_info-lsb.c +++ b/src/dwarf/Gfind_proc_info-lsb.c @@ -199,7 +199,7 @@ locate_debug_info (unw_addr_space_t as, unw_word_t addr, const char *dlname, name = (char*) dlname; err = load_debug_frame (name, &buf, &bufsize, as == unw_local_addr_space); - + if (!err) { fdesc = malloc (sizeof (struct unw_debug_frame_list)); @@ -210,10 +210,10 @@ locate_debug_info (unw_addr_space_t as, unw_word_t addr, const char *dlname, fdesc->debug_frame_size = bufsize; fdesc->index = NULL; fdesc->next = as->debug_frames; - + as->debug_frames = fdesc; } - + return fdesc; } @@ -235,10 +235,10 @@ debug_frame_tab_append (struct debug_frame_tab *tab, tab->size *= 2; tab->tab = realloc (tab->tab, sizeof (struct table_entry) * tab->size); } - + tab->tab[length].fde_offset = fde_offset; tab->tab[length].start_ip_offset = start_ip; - + tab->length = length + 1; } @@ -256,7 +256,7 @@ static int debug_frame_tab_compare (const void *a, const void *b) { const struct table_entry *fa = a, *fb = b; - + if (fa->start_ip_offset > fb->start_ip_offset) return 1; else if (fa->start_ip_offset < fb->start_ip_offset) @@ -522,7 +522,7 @@ dwarf_callback (struct dl_phdr_info *info, size_t size, void *ptr) else if (phdr->p_type == PT_DYNAMIC) p_dynamic = phdr; } - + if (!p_text) return 0;