From patchwork Wed Aug 3 12:12:05 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ken Werner X-Patchwork-Id: 3236 Return-Path: X-Original-To: patchwork@peony.canonical.com Delivered-To: patchwork@peony.canonical.com Received: from fiordland.canonical.com (fiordland.canonical.com [91.189.94.145]) by peony.canonical.com (Postfix) with ESMTP id 27E8123F3D for ; Wed, 3 Aug 2011 12:12:12 +0000 (UTC) Received: from mail-qy0-f180.google.com (mail-qy0-f180.google.com [209.85.216.180]) by fiordland.canonical.com (Postfix) with ESMTP id B082DA182F2 for ; Wed, 3 Aug 2011 12:12:11 +0000 (UTC) Received: by qyk30 with SMTP id 30so585833qyk.11 for ; Wed, 03 Aug 2011 05:12:11 -0700 (PDT) Received: by 10.229.241.19 with SMTP id lc19mr3460651qcb.45.1312373530358; Wed, 03 Aug 2011 05:12:10 -0700 (PDT) X-Forwarded-To: linaro-patchwork@canonical.com X-Forwarded-For: patch@linaro.org linaro-patchwork@canonical.com Delivered-To: patches@linaro.org Received: by 10.229.6.73 with SMTP id 9cs156119qcy; Wed, 3 Aug 2011 05:12:09 -0700 (PDT) Received: by 10.227.28.129 with SMTP id m1mr1990007wbc.80.1312373528831; Wed, 03 Aug 2011 05:12:08 -0700 (PDT) Received: from mtagate1.uk.ibm.com (mtagate1.uk.ibm.com [194.196.100.161]) by mx.google.com with ESMTPS id p16si1461843wbh.26.2011.08.03.05.12.08 (version=TLSv1/SSLv3 cipher=OTHER); Wed, 03 Aug 2011 05:12:08 -0700 (PDT) Received-SPF: neutral (google.com: 194.196.100.161 is neither permitted nor denied by best guess record for domain of ken.werner@linaro.org) client-ip=194.196.100.161; Authentication-Results: mx.google.com; spf=neutral (google.com: 194.196.100.161 is neither permitted nor denied by best guess record for domain of ken.werner@linaro.org) smtp.mail=ken.werner@linaro.org Received: from d06nrmr1307.portsmouth.uk.ibm.com (d06nrmr1307.portsmouth.uk.ibm.com [9.149.38.129]) by mtagate1.uk.ibm.com (8.13.1/8.13.1) with ESMTP id p73CC7Bn006684 for ; Wed, 3 Aug 2011 12:12:07 GMT Received: from d06av05.portsmouth.uk.ibm.com (d06av05.portsmouth.uk.ibm.com [9.149.37.229]) by d06nrmr1307.portsmouth.uk.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id p73CC7cB2367692 for ; Wed, 3 Aug 2011 13:12:07 +0100 Received: from d06av05.portsmouth.uk.ibm.com (loopback [127.0.0.1]) by d06av05.portsmouth.uk.ibm.com (8.14.4/8.13.1/NCO v10.0 AVout) with ESMTP id p73CC7kO012168 for ; Wed, 3 Aug 2011 06:12:07 -0600 Received: from leonard.boeblingen.de.ibm.com (dyn-9-152-224-36.boeblingen.de.ibm.com [9.152.224.36]) by d06av05.portsmouth.uk.ibm.com (8.14.4/8.13.1/NCO v10.0 AVin) with ESMTP id p73CC6Bw012117; Wed, 3 Aug 2011 06:12:07 -0600 From: Ken Werner To: libunwind-devel@nongnu.org Subject: [PATCH 5/5] ARM: Initial support for remote unwinding using libunwind-ptrace Date: Wed, 3 Aug 2011 14:12:05 +0200 Message-Id: <1312373525-20741-6-git-send-email-ken.werner@linaro.org> X-Mailer: git-send-email 1.7.4.1 In-Reply-To: <1312373525-20741-1-git-send-email-ken.werner@linaro.org> References: <1312373525-20741-1-git-send-email-ken.werner@linaro.org> Change _UPTi_find_unwind_table to also look for the ARM specific unwind information. Adjust the ARM unwind code to read memory using the accessor routines. Signed-off-by: Ken Werner --- include/libunwind-arm.h | 5 -- include/tdep-arm/ex_tables.h | 2 +- src/arm/Gex_tables.c | 136 +++++++++++++++++++++++++++----------- src/arm/Gstep.c | 4 +- src/ptrace/_UPT_find_proc_info.c | 45 ++++++++++++- src/ptrace/_UPT_internal.h | 3 + 6 files changed, 144 insertions(+), 51 deletions(-) diff --git a/include/libunwind-arm.h b/include/libunwind-arm.h index 3338b42..63b0138 100644 --- a/include/libunwind-arm.h +++ b/include/libunwind-arm.h @@ -280,11 +280,6 @@ typedef ucontext_t unw_tdep_context_t; #include "libunwind-dynamic.h" -struct arm_exidx_entry { - uint32_t addr; - uint32_t data; -}; - typedef struct { /* no arm-specific auxiliary proc-info */ diff --git a/include/tdep-arm/ex_tables.h b/include/tdep-arm/ex_tables.h index 6ea3414..8906f13 100644 --- a/include/tdep-arm/ex_tables.h +++ b/include/tdep-arm/ex_tables.h @@ -48,7 +48,7 @@ struct arm_exbuf_data #define arm_exidx_decode UNW_OBJ(arm_exidx_decode) #define arm_exidx_apply_cmd UNW_OBJ(arm_exidx_apply_cmd) -int arm_exidx_extract (struct arm_exidx_entry *entry, uint8_t *buf); +int arm_exidx_extract (struct dwarf_cursor *c, uint8_t *buf); int arm_exidx_decode (const uint8_t *buf, uint8_t len, struct dwarf_cursor *c); int arm_exidx_apply_cmd (struct arm_exbuf_data *edata, struct dwarf_cursor *c); diff --git a/src/arm/Gex_tables.c b/src/arm/Gex_tables.c index c90f675..88f800e 100644 --- a/src/arm/Gex_tables.c +++ b/src/arm/Gex_tables.c @@ -44,11 +44,19 @@ prel31_read (uint32_t prel31) return ((int32_t)prel31 << 1) >> 1; } -static inline uint32_t -prel31_to_addr (uint32_t *addr) +static inline int +prel31_to_addr (unw_addr_space_t as, void *arg, unw_word_t prel31, + unw_word_t *val) { - uint32_t offset = ((long)*addr << 1) >> 1; - return (uint32_t)addr + offset; + unw_word_t offset; + + if ((*as->acc.access_mem)(as, prel31, &offset, 0, arg) < 0) + return -UNW_EINVAL; + + offset = ((long)offset << 1) >> 1; + *val = prel31 + offset; + + return 0; } /** @@ -258,17 +266,31 @@ arm_exidx_decode (const uint8_t *buf, uint8_t len, struct dwarf_cursor *c) } /** - * Reads the given entry and extracts the unwind instructions into buf. - * Returns the number of the extracted unwind insns or -UNW_ESTOPUNWIND - * if the special bit pattern ARM_EXIDX_CANT_UNWIND (0x1) was found. + * Reads the entry from the given cursor and extracts the unwind instructions + * into buf. Returns the number of the extracted unwind insns or + * -UNW_ESTOPUNWIND if the special bit pattern ARM_EXIDX_CANT_UNWIND (0x1) was + * found. */ HIDDEN int -arm_exidx_extract (struct arm_exidx_entry *entry, uint8_t *buf) +arm_exidx_extract (struct dwarf_cursor *c, uint8_t *buf) { int nbuf = 0; - unw_word_t addr = prel31_to_addr (&entry->addr); + unw_word_t entry = (unw_word_t) c->pi.unwind_info; + unw_word_t addr; + uint32_t data; + + /* An ARM unwind entry consists of a prel31 offset to the start of a + function followed by 31bits of data: + * if set to 0x1: the function cannot be unwound (EXIDX_CANTUNWIND) + * if bit 31 is one: this is a table entry itself (ARM_EXIDX_COMPACT) + * if bit 31 is zero: this is a prel31 offset of the start of the + table entry for this function */ + if (prel31_to_addr(c->as, c->as_arg, entry, &addr) < 0) + return -UNW_EINVAL; + + if ((*c->as->acc.access_mem)(c->as, entry + 4, &data, 0, c->as_arg) < 0) + return -UNW_EINVAL; - uint32_t data = entry->data; if (data == ARM_EXIDX_CANT_UNWIND) { Debug (2, "0x1 [can't unwind]\n"); @@ -276,16 +298,23 @@ arm_exidx_extract (struct arm_exidx_entry *entry, uint8_t *buf) } else if (data & ARM_EXIDX_COMPACT) { - Debug (2, "%p compact model %d [%8.8x]\n", (void *)addr, (data >> 24) & 0x7f, data); + Debug (2, "%p compact model %d [%8.8x]\n", (void *)addr, + (data >> 24) & 0x7f, data); buf[nbuf++] = data >> 16; buf[nbuf++] = data >> 8; buf[nbuf++] = data; } else { - uint32_t *extbl_data = (uint32_t *)prel31_to_addr (&entry->data); - data = (unw_word_t)extbl_data[0]; + unw_word_t extbl_data; unsigned int n_table_words = 0; + + if (prel31_to_addr(c->as, c->as_arg, entry + 4, &extbl_data) < 0) + return -UNW_EINVAL; + + if ((*c->as->acc.access_mem)(c->as, extbl_data, &data, 0, c->as_arg) < 0) + return -UNW_EINVAL; + if (data & ARM_EXIDX_COMPACT) { int pers = (data >> 24) & 0x0f; @@ -293,7 +322,7 @@ arm_exidx_extract (struct arm_exidx_entry *entry, uint8_t *buf) if (pers == 1 || pers == 2) { n_table_words = (data >> 16) & 0xff; - extbl_data += 1; + extbl_data += 4; } else buf[nbuf++] = data >> 16; @@ -302,19 +331,28 @@ arm_exidx_extract (struct arm_exidx_entry *entry, uint8_t *buf) } else { - unw_word_t pers = prel31_to_addr (extbl_data); - Debug (2, "%p Personality routine: %8p\n", (void *)addr, (void *)pers); - n_table_words = extbl_data[1] >> 24; - buf[nbuf++] = extbl_data[1] >> 16; - buf[nbuf++] = extbl_data[1] >> 8; - buf[nbuf++] = extbl_data[1]; - extbl_data += 2; + unw_word_t pers; + if (prel31_to_addr (c->as, c->as_arg, extbl_data, &pers) < 0) + return -UNW_EINVAL; + Debug (2, "%p Personality routine: %8p\n", (void *)addr, + (void *)pers); + if ((*c->as->acc.access_mem)(c->as, extbl_data + 4, &data, 0, + c->as_arg) < 0) + return -UNW_EINVAL; + n_table_words = data >> 24; + buf[nbuf++] = data >> 16; + buf[nbuf++] = data >> 8; + buf[nbuf++] = data; + extbl_data += 8; } assert (n_table_words <= 5); unsigned j; for (j = 0; j < n_table_words; j++) { - data = *extbl_data++; + if ((*c->as->acc.access_mem)(c->as, extbl_data, &data, 0, + c->as_arg) < 0) + return -UNW_EINVAL; + extbl_data += 4; buf[nbuf++] = data >> 24; buf[nbuf++] = data >> 16; buf[nbuf++] = data >> 8; @@ -336,41 +374,59 @@ unw_search_arm_unwind_table (unw_addr_space_t as, unw_word_t ip, if (UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX) && di->format == UNW_INFO_FORMAT_ARM_EXIDX) { - struct arm_exidx_entry *first = - (struct arm_exidx_entry *) di->u.rti.table_data; - struct arm_exidx_entry *last = - ((struct arm_exidx_entry *) (di->u.rti.table_data + - di->u.rti.table_len)) - 1; - struct arm_exidx_entry *entry; - - if (ip < prel31_to_addr (&first->addr)) + /* The .ARM.exidx section contains a sorted list of key-value pairs - + the unwind entries. The 'key' is a prel31 offset to the start of a + function. We binary search this section in order to find the + appropriate unwind entry. */ + unw_word_t first = di->u.rti.table_data; + unw_word_t last = di->u.rti.table_data + di->u.rti.table_len - 8; + unw_word_t entry, val; + + if (prel31_to_addr (as, arg, first, &val) < 0 && ip < val) return -UNW_ENOINFO; - else if (ip >= prel31_to_addr (&last->addr)) + + if (prel31_to_addr (as, arg, last, &val) < 0) + return -UNW_EINVAL; + + if (ip >= val) { - entry = last; - pi->start_ip = prel31_to_addr (&last->addr); - pi->end_ip = di->end_ip - 1; + entry = last; + + if (prel31_to_addr (as, arg, last, &pi->start_ip) < 0) + return -UNW_EINVAL; + + pi->end_ip = di->end_ip -1; } else { - while (first < last - 1) + while (first < last - 8) { - entry = first + ((last - first + 1) >> 1); - if (ip < prel31_to_addr (&entry->addr)) + entry = first + (((last - first) / 8 + 1) >> 1) * 8; + + if (prel31_to_addr (as, arg, entry, &val) < 0) + return -UNW_EINVAL; + + if (ip < val) last = entry; else first = entry; } entry = first; - pi->start_ip = prel31_to_addr (&entry->addr); - pi->end_ip = prel31_to_addr (&(entry + 1)->addr) - 1; + + if (prel31_to_addr (as, arg, entry, &pi->start_ip) < 0) + return -UNW_EINVAL; + + if (prel31_to_addr (as, arg, entry + 8, &pi->end_ip) < 0) + return -UNW_EINVAL; + + pi->end_ip--; } if (need_unwind_info) { pi->unwind_info_size = 8; - pi->unwind_info = entry; + pi->unwind_info = (void *) entry; pi->format = UNW_INFO_FORMAT_ARM_EXIDX; } return 0; diff --git a/src/arm/Gstep.c b/src/arm/Gstep.c index 60cbf2c..270058b 100644 --- a/src/arm/Gstep.c +++ b/src/arm/Gstep.c @@ -35,7 +35,6 @@ static inline int arm_exidx_step (struct cursor *c) { unw_word_t old_ip, old_cfa; - struct arm_exidx_entry *entry; uint8_t buf[32]; int ret; @@ -51,8 +50,7 @@ arm_exidx_step (struct cursor *c) if (c->dwarf.pi.format != UNW_INFO_FORMAT_ARM_EXIDX) return -UNW_ENOINFO; - entry = (struct arm_exidx_entry *)c->dwarf.pi.unwind_info; - ret = arm_exidx_extract (entry, buf); + ret = arm_exidx_extract (&c->dwarf, buf); if (ret < 0) return ret; diff --git a/src/ptrace/_UPT_find_proc_info.c b/src/ptrace/_UPT_find_proc_info.c index e98a344..44feb34 100644 --- a/src/ptrace/_UPT_find_proc_info.c +++ b/src/ptrace/_UPT_find_proc_info.c @@ -176,6 +176,9 @@ _UPTi_find_unwind_table (struct UPT_info *ui, unw_addr_space_t as, unw_proc_info_t pi; unw_accessors_t *a; Elf_W(Ehdr) *ehdr; +#if UNW_TARGET_ARM + const Elf_W(Phdr) *parm_exidx = NULL; +#endif int i, ret, found = 0; /* XXX: Much of this code is Linux/LSB-specific. */ @@ -205,6 +208,12 @@ _UPTi_find_unwind_table (struct UPT_info *ui, unw_addr_space_t as, pdyn = phdr + i; break; +#if UNW_TARGET_ARM + case PT_ARM_EXIDX: + parm_exidx = phdr + i; + break; +#endif + default: break; } @@ -316,6 +325,20 @@ _UPTi_find_unwind_table (struct UPT_info *ui, unw_addr_space_t as, found = 1; } +#if UNW_TARGET_ARM + if (parm_exidx) + { + ui->di_arm.format = UNW_INFO_FORMAT_ARM_EXIDX; + ui->di_arm.start_ip = segbase; + ui->di_arm.end_ip = ui->di_arm.start_ip + ptxt->p_memsz; + ui->di_arm.u.rti.name_ptr = (unw_word_t) path; + ui->di_arm.u.rti.table_data = parm_exidx->p_vaddr + segbase - + ptxt->p_vaddr; + ui->di_arm.u.rti.table_len = parm_exidx->p_memsz; + found = 1; + } +#endif + #ifdef CONFIG_DEBUG_FRAME { /* Try .debug_frame. */ @@ -362,6 +385,10 @@ get_unwind_info (struct UPT_info *ui, unw_addr_space_t as, unw_word_t ip) if ((ui->di_cache.format != -1 && ip >= ui->di_cache.start_ip && ip < ui->di_cache.end_ip) +#if UNW_TARGET_ARM + || (ui->di_debug.format != -1 + && ip >= ui->di_arm.start_ip && ip < ui->di_arm.end_ip) +#endif || (ui->di_debug.format != -1 && ip >= ui->di_debug.start_ip && ip < ui->di_debug.end_ip)) return 0; @@ -377,6 +404,10 @@ get_unwind_info (struct UPT_info *ui, unw_addr_space_t as, unw_word_t ip) ui->di_debug.start_ip = ui->di_debug.end_ip = 0; ui->di_cache.format = -1; ui->di_debug.format = -1; +#if UNW_TARGET_ARM + ui->di_arm.start_ip = ui->di_arm.end_ip = 0; + ui->di_arm.format = -1; +#endif } if (tdep_get_elf_image (&ui->ei, ui->pid, ip, &segbase, &mapoff, path, @@ -400,7 +431,11 @@ get_unwind_info (struct UPT_info *ui, unw_addr_space_t as, unw_word_t ip) && (ip < ui->di_debug.start_ip || ip >= ui->di_debug.end_ip)) ui->di_debug.format = -1; - if (ui->di_cache.format == -1 && ui->di_debug.format == -1) + if (ui->di_cache.format == -1 +#if UNW_TARGET_ARM + && ui->di_arm.format == -1 +#endif + && ui->di_debug.format == -1) return -UNW_ENOINFO; return 0; @@ -444,10 +479,16 @@ _UPT_find_proc_info (unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi, } #endif - if (ret == -UNW_ENOINFO && ui->di_cache.format == -1) + if (ret == -UNW_ENOINFO && ui->di_cache.format != -1) ret = tdep_search_unwind_table (as, ip, &ui->di_cache, pi, need_unwind_info, arg); +#if UNW_TARGET_ARM + if (ret == -UNW_ENOINFO && ui->di_arm.format != -1) + ret = tdep_search_unwind_table (as, ip, &ui->di_arm, pi, + need_unwind_info, arg); +#endif + if (ret == -UNW_ENOINFO && ui->di_debug.format != -1) ret = tdep_search_unwind_table (as, ip, &ui->di_debug, pi, need_unwind_info, arg); diff --git a/src/ptrace/_UPT_internal.h b/src/ptrace/_UPT_internal.h index 54d5fc9..a7a0481 100644 --- a/src/ptrace/_UPT_internal.h +++ b/src/ptrace/_UPT_internal.h @@ -57,6 +57,9 @@ struct UPT_info #if UNW_TARGET_IA64 unw_dyn_info_t ktab; #endif +#if UNW_TARGET_ARM + unw_dyn_info_t di_arm; /* additional table info for .ARM.exidx */ +#endif }; extern int _UPT_reg_offset[UNW_REG_LAST + 1];