From patchwork Mon Aug 15 18:23:14 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ken Werner X-Patchwork-Id: 3459 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 1037623F4F for ; Mon, 15 Aug 2011 18:23:32 +0000 (UTC) Received: from mail-ew0-f52.google.com (mail-ew0-f52.google.com [209.85.215.52]) by fiordland.canonical.com (Postfix) with ESMTP id 00B76A1838D for ; Mon, 15 Aug 2011 18:23:31 +0000 (UTC) Received: by ewy28 with SMTP id 28so2749805ewy.11 for ; Mon, 15 Aug 2011 11:23:31 -0700 (PDT) Received: by 10.213.3.155 with SMTP id 27mr592547ebn.18.1313432609702; Mon, 15 Aug 2011 11:23:29 -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.213.102.5 with SMTP id e5cs109377ebo; Mon, 15 Aug 2011 11:23:29 -0700 (PDT) Received: from mr.google.com ([10.227.7.92]) by 10.227.7.92 with SMTP id c28mr5196333wbc.62.1313432608472 (num_hops = 1); Mon, 15 Aug 2011 11:23:28 -0700 (PDT) Received: by 10.227.7.92 with SMTP id c28mr3758468wbc.62.1313432604996; Mon, 15 Aug 2011 11:23:24 -0700 (PDT) Received: from mtagate2.uk.ibm.com (mtagate2.uk.ibm.com [194.196.100.162]) by mx.google.com with ESMTPS id fw18si16528755wbb.142.2011.08.15.11.23.23 (version=TLSv1/SSLv3 cipher=OTHER); Mon, 15 Aug 2011 11:23:23 -0700 (PDT) Received-SPF: neutral (google.com: 194.196.100.162 is neither permitted nor denied by best guess record for domain of ken.werner@linaro.org) client-ip=194.196.100.162; Authentication-Results: mx.google.com; spf=neutral (google.com: 194.196.100.162 is neither permitted nor denied by best guess record for domain of ken.werner@linaro.org) smtp.mail=ken.werner@linaro.org Received: from d06nrmr1707.portsmouth.uk.ibm.com (d06nrmr1707.portsmouth.uk.ibm.com [9.149.39.225]) by mtagate2.uk.ibm.com (8.13.1/8.13.1) with ESMTP id p7FING1Z032436 for ; Mon, 15 Aug 2011 18:23:16 GMT Received: from d06av11.portsmouth.uk.ibm.com (d06av11.portsmouth.uk.ibm.com [9.149.37.252]) by d06nrmr1707.portsmouth.uk.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id p7FINGk02490472 for ; Mon, 15 Aug 2011 19:23:16 +0100 Received: from d06av11.portsmouth.uk.ibm.com (loopback [127.0.0.1]) by d06av11.portsmouth.uk.ibm.com (8.14.4/8.13.1/NCO v10.0 AVout) with ESMTP id p7FINFrZ007355 for ; Mon, 15 Aug 2011 12:23:15 -0600 Received: from leonard.boeblingen.de.ibm.com (dyn-9-152-224-36.boeblingen.de.ibm.com [9.152.224.36]) by d06av11.portsmouth.uk.ibm.com (8.14.4/8.13.1/NCO v10.0 AVin) with ESMTP id p7FINFRv007316; Mon, 15 Aug 2011 12:23:15 -0600 From: Ken Werner To: libunwind-devel@nongnu.org Subject: [PATCH 4/4] ARM: Initial support for remote unwinding using libunwind-ptrace Date: Mon, 15 Aug 2011 20:23:14 +0200 Message-Id: <1313432594-23980-5-git-send-email-ken.werner@linaro.org> X-Mailer: git-send-email 1.7.4.1 In-Reply-To: <1313432594-23980-1-git-send-email-ken.werner@linaro.org> References: <1313432594-23980-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 | 142 +++++++++++++++++++++++++++----------- src/arm/Gstep.c | 4 +- src/ptrace/_UPT_find_proc_info.c | 45 ++++++++++++- src/ptrace/_UPT_internal.h | 3 + 6 files changed, 150 insertions(+), 51 deletions(-) diff --git a/include/libunwind-arm.h b/include/libunwind-arm.h index 211ae50..492331e 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 fc18088..e6ac3ef 100644 --- a/src/arm/Gex_tables.c +++ b/src/arm/Gex_tables.c @@ -22,6 +22,12 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/* This file contains functionality for parsing and interpreting the ARM +specific unwind information. Documentation about the exception handling +ABI for the ARM architecture can be found at: +http://infocenter.arm.com/help/topic/com.arm.doc.ihi0038a/IHI0038A_ehabi.pdf +*/ + #include "libunwind_i.h" #define ARM_EXBUF_START(x) (((x) >> 4) & 0x0f) @@ -53,11 +59,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; } /** @@ -267,17 +281,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"); @@ -285,16 +313,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; @@ -302,7 +337,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; @@ -311,19 +346,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; @@ -345,41 +389,59 @@ tdep_search_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];