From patchwork Mon Aug 15 18:23:12 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ken Werner X-Patchwork-Id: 3457 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 727FE23E51 for ; Mon, 15 Aug 2011 18:23:30 +0000 (UTC) Received: from mail-ey0-f170.google.com (mail-ey0-f170.google.com [209.85.215.170]) by fiordland.canonical.com (Postfix) with ESMTP id 63715A183B1 for ; Mon, 15 Aug 2011 18:23:30 +0000 (UTC) Received: by mail-ey0-f170.google.com with SMTP id 10so4104936eyd.29 for ; Mon, 15 Aug 2011 11:23:30 -0700 (PDT) Received: by 10.213.29.147 with SMTP id q19mr595935ebc.132.1313432609237; 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 e5cs109376ebo; Mon, 15 Aug 2011 11:23:29 -0700 (PDT) Received: from mr.google.com ([10.216.29.208]) by 10.216.29.208 with SMTP id i58mr3612840wea.42.1313432608156 (num_hops = 1); Mon, 15 Aug 2011 11:23:28 -0700 (PDT) Received: by 10.216.29.208 with SMTP id i58mr2403990wea.42.1313432604972; Mon, 15 Aug 2011 11:23:24 -0700 (PDT) Received: from mtagate1.uk.ibm.com (mtagate1.uk.ibm.com [194.196.100.161]) by mx.google.com with ESMTPS id w44si16545837wec.86.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.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 d06nrmr1806.portsmouth.uk.ibm.com (d06nrmr1806.portsmouth.uk.ibm.com [9.149.39.193]) by mtagate1.uk.ibm.com (8.13.1/8.13.1) with ESMTP id p7FINGjF016495 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 d06nrmr1806.portsmouth.uk.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id p7FINFdU2514966 for ; Mon, 15 Aug 2011 19:23:15 +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 p7FINFAQ007339 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 p7FINFRt007316; Mon, 15 Aug 2011 12:23:15 -0600 From: Ken Werner To: libunwind-devel@nongnu.org Subject: [PATCH 2/4] Rework the lookup of the ARM specific unwind info Date: Mon, 15 Aug 2011 20:23:12 +0200 Message-Id: <1313432594-23980-3-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> Implement routines for finding the proc_info and searching the unwind table for the ARM backend. Signed-off-by: Ken Werner --- include/libunwind-arm.h | 8 -- include/libunwind-dynamic.h | 3 +- include/tdep-arm/ex_tables.h | 10 -- include/tdep-arm/libunwind_i.h | 13 ++- src/arm/Gex_tables.c | 254 +++++++++++++++++++++++++--------------- src/arm/Ginit.c | 10 +-- src/arm/Ginit_local.c | 3 - src/arm/Gstep.c | 10 +- src/dwarf/Gparser.c | 5 + 9 files changed, 183 insertions(+), 133 deletions(-) diff --git a/include/libunwind-arm.h b/include/libunwind-arm.h index 3e7ebf3..211ae50 100644 --- a/include/libunwind-arm.h +++ b/include/libunwind-arm.h @@ -285,14 +285,6 @@ struct arm_exidx_entry { uint32_t data; }; -struct arm_exidx_table { - const char *name; - struct arm_exidx_entry *start; - struct arm_exidx_entry *end; - unw_word_t start_addr; - unw_word_t end_addr; -}; - typedef struct { /* no arm-specific auxiliary proc-info */ diff --git a/include/libunwind-dynamic.h b/include/libunwind-dynamic.h index 6c94f05..584f392 100644 --- a/include/libunwind-dynamic.h +++ b/include/libunwind-dynamic.h @@ -75,7 +75,8 @@ typedef enum { UNW_INFO_FORMAT_DYNAMIC, /* unw_dyn_proc_info_t */ UNW_INFO_FORMAT_TABLE, /* unw_dyn_table_t */ - UNW_INFO_FORMAT_REMOTE_TABLE /* unw_dyn_remote_table_t */ + UNW_INFO_FORMAT_REMOTE_TABLE, /* unw_dyn_remote_table_t */ + UNW_INFO_FORMAT_ARM_EXIDX /* ARM specific unwind info */ } unw_dyn_info_format_t; diff --git a/include/tdep-arm/ex_tables.h b/include/tdep-arm/ex_tables.h index b5e6dcf..6ea3414 100644 --- a/include/tdep-arm/ex_tables.h +++ b/include/tdep-arm/ex_tables.h @@ -44,20 +44,10 @@ struct arm_exbuf_data uint32_t data; }; -#define arm_exidx_init_local UNW_OBJ(arm_exidx_init_local) -#define arm_exidx_table_add UNW_OBJ(arm_exidx_table_add) -#define arm_exidx_table_find UNW_OBJ(arm_exidx_table_find) -#define arm_exidx_table_lookup UNW_OBJ(arm_exidx_table_lookup) #define arm_exidx_extract UNW_OBJ(arm_exidx_extract) #define arm_exidx_decode UNW_OBJ(arm_exidx_decode) #define arm_exidx_apply_cmd UNW_OBJ(arm_exidx_apply_cmd) -int arm_exidx_init_local (void); -int arm_exidx_table_add (const char *name, struct arm_exidx_entry *start, - struct arm_exidx_entry *end); -struct arm_exidx_table *arm_exidx_table_find (unw_word_t pc); -struct arm_exidx_entry *arm_exidx_table_lookup (struct arm_exidx_table *table, - unw_word_t pc); int arm_exidx_extract (struct arm_exidx_entry *entry, 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/include/tdep-arm/libunwind_i.h b/include/tdep-arm/libunwind_i.h index 508f6c8..7a5a394 100644 --- a/include/tdep-arm/libunwind_i.h +++ b/include/tdep-arm/libunwind_i.h @@ -229,9 +229,11 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val) #define tdep_getcontext_trace unw_getcontext #define tdep_needs_initialization UNW_OBJ(needs_initialization) #define tdep_init UNW_OBJ(init) +#define arm_find_proc_info UNW_OBJ(find_proc_info) +#define arm_put_unwind_info UNW_OBJ(put_unwind_info) /* Platforms that support UNW_INFO_FORMAT_TABLE need to define tdep_search_unwind_table. */ -#define tdep_search_unwind_table dwarf_search_unwind_table +#define tdep_search_unwind_table UNW_OBJ(search_unwind_table) #define tdep_uc_addr UNW_ARCH_OBJ(uc_addr) #define tdep_get_elf_image UNW_ARCH_OBJ(get_elf_image) #define tdep_access_reg UNW_OBJ(access_reg) @@ -244,10 +246,10 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val) #ifdef UNW_LOCAL_ONLY # define tdep_find_proc_info(c,ip,n) \ - dwarf_find_proc_info((c)->as, (ip), &(c)->pi, (n), \ + arm_find_proc_info((c)->as, (ip), &(c)->pi, (n), \ (c)->as_arg) # define tdep_put_unwind_info(as,pi,arg) \ - dwarf_put_unwind_info((as), (pi), (arg)) + arm_put_unwind_info((as), (pi), (arg)) #else # define tdep_find_proc_info(c,ip,n) \ (*(c)->as->acc.find_proc_info)((c)->as, (ip), &(c)->pi, (n), \ @@ -264,6 +266,11 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val) extern int tdep_needs_initialization; extern void tdep_init (void); +extern int arm_find_proc_info (unw_addr_space_t as, unw_word_t ip, + unw_proc_info_t *pi, int need_unwind_info, + void *arg); +extern void arm_put_unwind_info (unw_addr_space_t as, + unw_proc_info_t *pi, void *arg); extern int tdep_search_unwind_table (unw_addr_space_t as, unw_word_t ip, unw_dyn_info_t *di, unw_proc_info_t *pi, int need_unwind_info, void *arg); diff --git a/src/arm/Gex_tables.c b/src/arm/Gex_tables.c index b72dabb..fc18088 100644 --- a/src/arm/Gex_tables.c +++ b/src/arm/Gex_tables.c @@ -33,21 +33,19 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #define ARM_EXTBL_OP_FINISH 0xb0 -#define ARM_EXIDX_TABLE_LIMIT 32 - -#define arm_exidx_tables UNW_OBJ(arm_exidx_tables) -#define arm_exidx_table_count UNW_OBJ(arm_exidx_table_count) -#define arm_exidx_table_find UNW_OBJ(arm_exidx_table_find) -#define arm_exidx_table_reset_all UNW_OBJ(arm_exidx_table_reset_all) -#define arm_exidx_init_local_cb UNW_OBJ(arm_exidx_init_local_cb) - enum arm_exbuf_cmd_flags { ARM_EXIDX_VFP_SHIFT_16 = 1 << 16, ARM_EXIDX_VFP_DOUBLE = 1 << 17, }; -static struct arm_exidx_table arm_exidx_tables[ARM_EXIDX_TABLE_LIMIT]; -static unsigned arm_exidx_table_count = 0; +struct arm_cb_data + { + /* in: */ + unw_word_t ip; /* instruction-pointer we're looking for */ + unw_proc_info_t *pi; /* proc-info pointer */ + /* out: */ + unw_dyn_info_t di; /* info about the ARM exidx segment */ + }; static inline uint32_t prel31_read (uint32_t prel31) @@ -62,68 +60,6 @@ prel31_to_addr (uint32_t *addr) return (uint32_t)addr + offset; } -static void -arm_exidx_table_reset_all (void) -{ - arm_exidx_table_count = 0; -} - -HIDDEN int -arm_exidx_table_add (const char *name, - struct arm_exidx_entry *start, struct arm_exidx_entry *end) -{ - if (arm_exidx_table_count >= ARM_EXIDX_TABLE_LIMIT) - return -1; - struct arm_exidx_table *table = &arm_exidx_tables[arm_exidx_table_count++]; - table->name = name; - table->start = start; - table->end = end; - table->start_addr = prel31_to_addr (&start->addr); - table->end_addr = prel31_to_addr (&(end - 1)->addr); - Debug (2, "name=%s, range=%p-%p, addr=%p-%p\n", - name, start, end, (void *)table->start_addr, (void *)table->end_addr); - return 0; -} - -/** - * Locate the appropriate unwind table from the given PC. - */ -HIDDEN struct arm_exidx_table * -arm_exidx_table_find (unw_word_t pc) -{ - struct arm_exidx_table *table; - unsigned i; - for (i = 0; i < arm_exidx_table_count; i++) - { - table = &arm_exidx_tables[i]; - if (pc >= table->start_addr && pc < table->end_addr) - return table; - } - return NULL; -} - -/** - * Finds the corresponding arm_exidx_entry from a given index table and PC. - */ -HIDDEN struct arm_exidx_entry * -arm_exidx_table_lookup (struct arm_exidx_table *table, unw_word_t pc) -{ - struct arm_exidx_entry *first = table->start, *last = table->end - 1; - if (pc < prel31_to_addr (&first->addr)) - return NULL; - else if (pc >= prel31_to_addr (&last->addr)) - return last; - while (first < last - 1) - { - struct arm_exidx_entry *mid = first + ((last - first + 1) >> 1); - if (pc < prel31_to_addr (&mid->addr)) - last = mid; - else - first = mid; - } - return first; -} - /** * Applies the given command onto the new state to the given dwarf_cursor. */ @@ -401,39 +337,169 @@ arm_exidx_extract (struct arm_exidx_entry *entry, uint8_t *buf) return nbuf; } +PROTECTED int +tdep_search_unwind_table (unw_addr_space_t as, unw_word_t ip, + unw_dyn_info_t *di, unw_proc_info_t *pi, + int need_unwind_info, void *arg) +{ + 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)) + return -UNW_ENOINFO; + else if (ip >= prel31_to_addr (&last->addr)) + { + entry = last; + pi->start_ip = prel31_to_addr (&last->addr); + pi->end_ip = di->end_ip - 1; + } + else + { + while (first < last - 1) + { + entry = first + ((last - first + 1) >> 1); + if (ip < prel31_to_addr (&entry->addr)) + 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 (need_unwind_info) + { + pi->unwind_info_size = 8; + pi->unwind_info = entry; + pi->format = UNW_INFO_FORMAT_ARM_EXIDX; + } + return 0; + } + else if (UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF) + && di->format != UNW_INFO_FORMAT_ARM_EXIDX) + return dwarf_search_unwind_table (as, ip, di, pi, need_unwind_info, arg); + + return -UNW_ENOINFO; +} + /** - * Callback to dl_iterate_phdr to find the unwind tables. - * If found, calls arm_exidx_table_add to remember it for later lookups. + * Callback to dl_iterate_phdr to find infos about the ARM exidx segment. */ static int -arm_exidx_init_local_cb (struct dl_phdr_info *info, size_t size, void *data) +arm_phdr_cb (struct dl_phdr_info *info, size_t size, void *data) { - unsigned i; + struct arm_cb_data *cb_data = data; + const Elf_W(Phdr) *p_text = NULL; + const Elf_W(Phdr) *p_arm_exidx = NULL; + const Elf_W(Phdr) *phdr = info->dlpi_phdr; + long n; - for (i = 0; i < info->dlpi_phnum; i++) + for (n = info->dlpi_phnum; --n >= 0; phdr++) { - const ElfW (Phdr) *phdr = info->dlpi_phdr + i; - if (phdr->p_type != PT_ARM_EXIDX) - continue; - - ElfW (Addr) addr = info->dlpi_addr + phdr->p_vaddr; - ElfW (Word) size = phdr->p_filesz; + switch (phdr->p_type) + { + case PT_LOAD: + if (cb_data->ip >= phdr->p_vaddr + info->dlpi_addr && + cb_data->ip < phdr->p_vaddr + info->dlpi_addr + phdr->p_memsz) + p_text = phdr; + break; + + case PT_ARM_EXIDX: + p_arm_exidx = phdr; + break; + + default: + break; + } + } - arm_exidx_table_add (info->dlpi_name, - (struct arm_exidx_entry *)addr, - (struct arm_exidx_entry *)(addr + size)); - break; + if (p_text && p_arm_exidx) + { + cb_data->di.format = UNW_INFO_FORMAT_ARM_EXIDX; + cb_data->di.start_ip = p_text->p_vaddr + info->dlpi_addr; + cb_data->di.end_ip = cb_data->di.start_ip + p_text->p_memsz; + cb_data->di.u.rti.name_ptr = (unw_word_t) info->dlpi_name; + cb_data->di.u.rti.table_data = p_arm_exidx->p_vaddr + info->dlpi_addr; + cb_data->di.u.rti.table_len = p_arm_exidx->p_memsz; + return 1; } + return 0; } -/** - * Traverse the program headers of the executable and its loaded - * shared objects to collect the unwind tables. - */ HIDDEN int -arm_exidx_init_local (void) +arm_find_proc_info (unw_addr_space_t as, unw_word_t ip, + unw_proc_info_t *pi, int need_unwind_info, void *arg) +{ + int ret = 0; + intrmask_t saved_mask; + + Debug (14, "looking for IP=0x%lx\n", (long) ip); + + if (UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX)) + { + struct arm_cb_data cb_data; + + memset (&cb_data, 0, sizeof (cb_data)); + cb_data.ip = ip; + cb_data.pi = pi; + cb_data.di.format = -1; + + SIGPROCMASK (SIG_SETMASK, &unwi_full_mask, &saved_mask); + ret = dl_iterate_phdr (arm_phdr_cb, &cb_data); + SIGPROCMASK (SIG_SETMASK, &saved_mask, NULL); + + if (cb_data.di.format != -1) + ret = tdep_search_unwind_table (as, ip, &cb_data.di, pi, + need_unwind_info, arg); + else + ret = -UNW_ENOINFO; + } + + if (ret < 0 && UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF)) + { + struct dwarf_callback_data cb_data; + + memset (&cb_data, 0, sizeof (cb_data)); + cb_data.ip = ip; + cb_data.pi = pi; + cb_data.need_unwind_info = need_unwind_info; + cb_data.di.format = -1; + cb_data.di_debug.format = -1; + + SIGPROCMASK (SIG_SETMASK, &unwi_full_mask, &saved_mask); + ret = dl_iterate_phdr (dwarf_callback, &cb_data); + SIGPROCMASK (SIG_SETMASK, &saved_mask, NULL); + + if (cb_data.single_fde) + /* already got the result in *pi */ + return 0; + + if (cb_data.di_debug.format != -1) + ret = tdep_search_unwind_table (as, ip, &cb_data.di_debug, pi, + need_unwind_info, arg); + else + ret = -UNW_ENOINFO; + } + + if (ret < 0) + Debug (14, "IP=0x%lx not found\n", (long) ip); + + return ret; +} + +HIDDEN void +arm_put_unwind_info (unw_addr_space_t as, unw_proc_info_t *proc_info, void *arg) { - arm_exidx_table_reset_all(); - return dl_iterate_phdr (&arm_exidx_init_local_cb, NULL); + /* it's a no-op */ } + diff --git a/src/arm/Ginit.c b/src/arm/Ginit.c index 5d7dcd6..b1e5228 100644 --- a/src/arm/Ginit.c +++ b/src/arm/Ginit.c @@ -85,12 +85,6 @@ HIDDEN unw_dyn_info_list_t _U_dyn_info_list; unwind-table entry. Perhaps something similar can be done with DWARF2 unwind info. */ -static void -put_unwind_info (unw_addr_space_t as, unw_proc_info_t *proc_info, void *arg) -{ - /* it's a no-op */ -} - static int get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr, void *arg) @@ -193,8 +187,8 @@ arm_local_addr_space_init (void) { memset (&local_addr_space, 0, sizeof (local_addr_space)); local_addr_space.caching_policy = UNW_CACHE_GLOBAL; - local_addr_space.acc.find_proc_info = dwarf_find_proc_info; - local_addr_space.acc.put_unwind_info = put_unwind_info; + local_addr_space.acc.find_proc_info = arm_find_proc_info; + local_addr_space.acc.put_unwind_info = arm_put_unwind_info; local_addr_space.acc.get_dyn_info_list_addr = get_dyn_info_list_addr; local_addr_space.acc.access_mem = access_mem; local_addr_space.acc.access_reg = access_reg; diff --git a/src/arm/Ginit_local.c b/src/arm/Ginit_local.c index f8ee19c..b0f73cd 100644 --- a/src/arm/Ginit_local.c +++ b/src/arm/Ginit_local.c @@ -49,9 +49,6 @@ unw_init_local (unw_cursor_t *cursor, ucontext_t *uc) c->dwarf.as = unw_local_addr_space; c->dwarf.as_arg = uc; - if (UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX)) - arm_exidx_init_local (); - return common_init (c, 1); } diff --git a/src/arm/Gstep.c b/src/arm/Gstep.c index cfe01c0..60cbf2c 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_table *table; struct arm_exidx_entry *entry; uint8_t buf[32]; int ret; @@ -46,14 +45,13 @@ arm_exidx_step (struct cursor *c) /* mark PC unsaved */ c->dwarf.loc[UNW_ARM_R15] = DWARF_NULL_LOC; - table = arm_exidx_table_find (c->dwarf.ip); - if (NULL == table) - return -UNW_ENOINFO; + if ((ret = tdep_find_proc_info (&c->dwarf, c->dwarf.ip, 1)) < 0) + return -UNW_ENOINFO; - entry = arm_exidx_table_lookup (table, c->dwarf.ip); - if (NULL == entry) + 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); if (ret < 0) return ret; diff --git a/src/dwarf/Gparser.c b/src/dwarf/Gparser.c index 1db1546..e82df6b 100644 --- a/src/dwarf/Gparser.c +++ b/src/dwarf/Gparser.c @@ -423,6 +423,11 @@ fetch_proc_info (struct dwarf_cursor *c, unw_word_t ip, int need_unwind_info) return ret; } + if (c->pi.format != UNW_INFO_FORMAT_DYNAMIC + && c->pi.format != UNW_INFO_FORMAT_TABLE + && c->pi.format != UNW_INFO_FORMAT_REMOTE_TABLE) + return -UNW_ENOINFO; + c->pi_valid = 1; c->pi_is_dynamic = dynamic;