From c5a27d9ca51b32accd04503e79c8dd01fc47eed2 Mon Sep 17 00:00:00 2001 From: Jeff Johnston Date: Wed, 8 Jun 2005 21:54:23 +0000 Subject: [PATCH] 2005-06-08 Jeff Johnston David Mosberger * ia64-tdep.c (KERNEL_START): New macro. (ia64_pseudo_register_read): Fix code to use libunwind to properly get the rse registers. (ia64_frame_this_id): Mark outermost frame with null frame id. (ia64_sigtramp_frame_prev_register): Fix up debug output. (ia64_access_rse_reg): New accessor function. (ia64_access_mem): Add special logic for accessing the kernel's unwind table. (getunwind_table): Fix for corefiles. (get_kernel_table): Fix to handle vDSO. (ia64_libunwind_frame_this_id): Check for null frame id and don't unwind past pc value of 0. Fix debugging output. (ia64_libunwind_sigtramp_frame_this_id): New function. (ia64_libunwind_sigtramp_frame_prev_register): Ditto. (ia64_libunwind_sigtramp_frame_sniffer): Ditto. (ia64_libunwind_sigtramp_frame_unwind): New unwinder. (ia64_unw_rse_accessors): New libunwind accessors. (ia64_libunwind_descr): Add ia64_unw_rse_accessors. (ia64_gdbarch_init)[HAVE_LIBUNWIND_IA64_H]: Use ia64_libunwind_sigtramp_frame_sniffer instead of ia64_sigtramp_frame_sniffer. * libunwind-frame.c (libunwind_frame_set_descr): Add special register accessors. (libunwind_frame_cache): Add special logic to check for 0 pc value. Check for a signal trampoline frame. (libunwind_frame_this_id): Check if libunwind_frame_cache returns NULL. (libunwind_frame_prev_register): Check for NULL cache. (libunwind_frame_base_address): Ditto. (libunwind_sigtramp_frame_sniffer): New function. (libunwind_get_reg_special): Ditto. (libunwind_load): Add unw_is_signal_frame support. * libunwind-frame.h (struct libunwind_descr): Add special_accessors field. (libunwind_sigtramp_frame_sniffer): New prototype. (libunwind_get_reg_special): Ditto. --- gdb/ChangeLog | 40 +++++ gdb/ia64-tdep.c | 404 ++++++++++++++++++++++++++++++++++++------ gdb/libunwind-frame.c | 123 ++++++++++++- gdb/libunwind-frame.h | 5 + 4 files changed, 517 insertions(+), 55 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index d6f43e2ea66..f94831461e3 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,43 @@ +2005-06-08 Jeff Johnston + David Mosberger + + * ia64-tdep.c (KERNEL_START): New macro. + (ia64_pseudo_register_read): Fix code to use libunwind to properly + get the rse registers. + (ia64_frame_this_id): Mark outermost frame with null frame id. + (ia64_sigtramp_frame_prev_register): Fix up debug output. + (ia64_access_rse_reg): New accessor function. + (ia64_access_mem): Add special logic for accessing the + kernel's unwind table. + (getunwind_table): Fix for corefiles. + (get_kernel_table): Fix to handle vDSO. + (ia64_libunwind_frame_this_id): Check for null frame id and + don't unwind past pc value of 0. Fix debugging output. + (ia64_libunwind_sigtramp_frame_this_id): New function. + (ia64_libunwind_sigtramp_frame_prev_register): Ditto. + (ia64_libunwind_sigtramp_frame_sniffer): Ditto. + (ia64_libunwind_sigtramp_frame_unwind): New unwinder. + (ia64_unw_rse_accessors): New libunwind accessors. + (ia64_libunwind_descr): Add ia64_unw_rse_accessors. + (ia64_gdbarch_init)[HAVE_LIBUNWIND_IA64_H]: Use + ia64_libunwind_sigtramp_frame_sniffer instead of + ia64_sigtramp_frame_sniffer. + * libunwind-frame.c (libunwind_frame_set_descr): Add + special register accessors. + (libunwind_frame_cache): Add special logic to check for + 0 pc value. Check for a signal trampoline frame. + (libunwind_frame_this_id): Check if libunwind_frame_cache + returns NULL. + (libunwind_frame_prev_register): Check for NULL cache. + (libunwind_frame_base_address): Ditto. + (libunwind_sigtramp_frame_sniffer): New function. + (libunwind_get_reg_special): Ditto. + (libunwind_load): Add unw_is_signal_frame support. + * libunwind-frame.h (struct libunwind_descr): Add special_accessors + field. + (libunwind_sigtramp_frame_sniffer): New prototype. + (libunwind_get_reg_special): Ditto. + 2005-06-08 Wu Zhou * expression.h (enum exp_opcode): Fix a comment typo. diff --git a/gdb/ia64-tdep.c b/gdb/ia64-tdep.c index a69343aa445..ab9a3402a5e 100644 --- a/gdb/ia64-tdep.c +++ b/gdb/ia64-tdep.c @@ -45,6 +45,34 @@ #include "elf/ia64.h" /* for PT_IA_64_UNWIND value */ #include "libunwind-frame.h" #include "libunwind-ia64.h" + +/* Note: KERNEL_START is supposed to be an address which is not going + to ever contain any valid unwind info. For ia64 linux, the choice + of 0xc000000000000000 is fairly safe since that's uncached space. + + We use KERNEL_START as follows: after obtaining the kernel's + unwind table via getunwind(), we project its unwind data into + address-range KERNEL_START-(KERNEL_START+ktab_size) and then + when ia64_access_mem() sees a memory access to this + address-range, we redirect it to ktab instead. + + None of this hackery is needed with a modern kernel/libcs + which uses the kernel virtual DSO to provide access to the + kernel's unwind info. In that case, ktab_size remains 0 and + hence the value of KERNEL_START doesn't matter. */ + +#define KERNEL_START 0xc000000000000000ULL + +static size_t ktab_size = 0; +struct ia64_table_entry + { + uint64_t start_offset; + uint64_t end_offset; + uint64_t info_offset; + }; + +static struct ia64_table_entry *ktab = NULL; + #endif /* An enumeration of the different IA-64 instruction types. */ @@ -649,24 +677,33 @@ ia64_pseudo_register_read (struct gdbarch *gdbarch, struct regcache *regcache, { if (regnum >= V32_REGNUM && regnum <= V127_REGNUM) { - ULONGEST bsp; - ULONGEST cfm; - CORE_ADDR reg; - regcache_cooked_read_unsigned (regcache, IA64_BSP_REGNUM, &bsp); - regcache_cooked_read_unsigned (regcache, IA64_CFM_REGNUM, &cfm); - - /* The bsp points at the end of the register frame so we - subtract the size of frame from it to get start of register frame. */ - bsp = rse_address_add (bsp, -(cfm & 0x7f)); - - if ((cfm & 0x7f) > regnum - V32_REGNUM) + /* First try and use the libunwind special reg accessor, otherwise fallback to + standard logic. */ + if (!libunwind_is_initialized () + || libunwind_get_reg_special (gdbarch, regnum, buf) != 0) { - ULONGEST reg_addr = rse_address_add (bsp, (regnum - V32_REGNUM)); - reg = read_memory_integer ((CORE_ADDR)reg_addr, 8); - store_unsigned_integer (buf, register_size (current_gdbarch, regnum), reg); + /* The fallback position is to assume that r32-r127 are found sequentially + in memory starting at $bof. This isn't always true, but without libunwind, + this is the best we can do. */ + ULONGEST cfm; + ULONGEST bsp; + CORE_ADDR reg; + regcache_cooked_read_unsigned (regcache, IA64_BSP_REGNUM, &bsp); + regcache_cooked_read_unsigned (regcache, IA64_CFM_REGNUM, &cfm); + + /* The bsp points at the end of the register frame so we + subtract the size of frame from it to get start of register frame. */ + bsp = rse_address_add (bsp, -(cfm & 0x7f)); + + if ((cfm & 0x7f) > regnum - V32_REGNUM) + { + ULONGEST reg_addr = rse_address_add (bsp, (regnum - V32_REGNUM)); + reg = read_memory_integer ((CORE_ADDR)reg_addr, 8); + store_unsigned_integer (buf, register_size (current_gdbarch, regnum), reg); + } + else + store_unsigned_integer (buf, register_size (current_gdbarch, regnum), 0); } - else - store_unsigned_integer (buf, register_size (current_gdbarch, regnum), 0); } else if (IA64_NAT0_REGNUM <= regnum && regnum <= IA64_NAT31_REGNUM) { @@ -1522,11 +1559,11 @@ ia64_frame_this_id (struct frame_info *next_frame, void **this_cache, struct ia64_frame_cache *cache = ia64_frame_cache (next_frame, this_cache); - /* This marks the outermost frame. */ + /* If outermost frame, mark with null frame id. */ if (cache->base == 0) - return; - - (*this_id) = frame_id_build_special (cache->base, cache->pc, cache->bsp); + (*this_id) = null_frame_id; + else + (*this_id) = frame_id_build_special (cache->base, cache->pc, cache->bsp); if (gdbarch_debug >= 1) fprintf_unfiltered (gdb_stdlog, "regular frame id: code 0x%s, stack 0x%s, special 0x%s, next_frame %p\n", @@ -2008,8 +2045,13 @@ ia64_sigtramp_frame_prev_register (struct frame_info *next_frame, if (gdbarch_debug >= 1) fprintf_unfiltered (gdb_stdlog, "sigtramp prev register <%s> is 0x%s\n", - (((unsigned) regnum <= IA64_NAT127_REGNUM) - ? ia64_register_names[regnum] : "r??"), + (regnum < IA64_GR32_REGNUM + || (regnum > IA64_GR127_REGNUM + && regnum < LAST_PSEUDO_REGNUM)) + ? ia64_register_names[regnum] + : (regnum < LAST_PSEUDO_REGNUM + ? ia64_register_names[regnum-IA64_GR32_REGNUM+V32_REGNUM] + : "OUT_OF_RANGE"), paddr_nz (extract_unsigned_integer (valuep, 8))); } @@ -2278,12 +2320,131 @@ ia64_access_fpreg (unw_addr_space_t as, unw_regnum_t uw_regnum, unw_fpreg_t *val return 0; } +/* Libunwind callback accessor function for top-level rse registers. */ +static int +ia64_access_rse_reg (unw_addr_space_t as, unw_regnum_t uw_regnum, unw_word_t *val, + int write, void *arg) +{ + int regnum = ia64_uw2gdb_regnum (uw_regnum); + unw_word_t bsp, sof, sol, cfm, psr, ip; + long new_sof, old_sof; + + if (write) + { + if (regnum < 0) + /* ignore writes to pseudo-registers such as UNW_IA64_PROC_STARTI. */ + return 0; + + switch (uw_regnum) + { + case UNW_REG_IP: + ia64_write_pc (*val, inferior_ptid); + break; + + case UNW_IA64_AR_BSPSTORE: + write_register (IA64_BSP_REGNUM, *val); + break; + + case UNW_IA64_AR_BSP: + case UNW_IA64_BSP: + /* Account for the fact that ptrace() expects bsp to point + after the current register frame. */ + cfm = read_register (IA64_CFM_REGNUM); + sof = (cfm & 0x7f); + bsp = ia64_rse_skip_regs (*val, sof); + write_register (IA64_BSP_REGNUM, bsp); + break; + + case UNW_IA64_CFM: + /* If we change CFM, we need to adjust ptrace's notion of + bsp accordingly, so that the real bsp remains + unchanged. */ + bsp = read_register (IA64_BSP_REGNUM); + cfm = read_register (IA64_CFM_REGNUM); + old_sof = (cfm & 0x7f); + new_sof = (*val & 0x7f); + if (old_sof != new_sof) + { + bsp = ia64_rse_skip_regs (bsp, -old_sof + new_sof); + write_register (IA64_BSP_REGNUM, bsp); + } + write_register (IA64_CFM_REGNUM, *val); + break; + + default: + write_register (regnum, *val); + break; + } + if (gdbarch_debug >= 1) + fprintf_unfiltered (gdb_stdlog, + " access_rse_reg: to cache: %4s=0x%s\n", + (((unsigned) regnum <= IA64_NAT127_REGNUM) + ? ia64_register_names[regnum] : "r??"), + paddr_nz (*val)); + } + else + { + switch (uw_regnum) + { + case UNW_REG_IP: + /* Libunwind expects to see the pc value which means the slot number + from the psr must be merged with the ip word address. */ + ip = read_register (IA64_IP_REGNUM); + psr = read_register (IA64_PSR_REGNUM); + *val = ip | ((psr >> 41) & 0x3); + break; + + case UNW_IA64_AR_BSP: + /* Libunwind expects to see the beginning of the current register + frame so we must account for the fact that ptrace() will return a value + for bsp that points *after* the current register frame. */ + bsp = read_register (IA64_BSP_REGNUM); + cfm = read_register (IA64_CFM_REGNUM); + sof = (cfm & 0x7f); + *val = ia64_rse_skip_regs (bsp, -sof); + break; + + case UNW_IA64_AR_BSPSTORE: + /* Libunwind wants bspstore to be after the current register frame. + This is what ptrace() and gdb treats as the regular bsp value. */ + *val = read_register (IA64_BSP_REGNUM); + break; + + default: + /* For all other registers, just read the value directly. */ + *val = read_register (regnum); + break; + } + } + + if (gdbarch_debug >= 1) + fprintf_unfiltered (gdb_stdlog, + " access_rse_reg: from cache: %4s=0x%s\n", + (((unsigned) regnum <= IA64_NAT127_REGNUM) + ? ia64_register_names[regnum] : "r??"), + paddr_nz (*val)); + + return 0; +} + /* Libunwind callback accessor function for accessing memory. */ static int ia64_access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write, void *arg) { + if (addr - KERNEL_START < ktab_size) + { + unw_word_t *laddr = (unw_word_t*) ((char *) ktab + + (addr - KERNEL_START)); + + if (write) + *laddr = *val; + else + *val = *laddr; + return 0; + } + /* XXX do we need to normalize byte-order here? */ if (write) return target_write_memory (addr, (char *) val, sizeof (unw_word_t)); @@ -2296,8 +2457,14 @@ static int getunwind_table (void *buf, size_t len) { LONGEST x; - x = target_read_partial (¤t_target, TARGET_OBJECT_UNWIND_TABLE, NULL, - buf, 0, len); + + /* FIXME: This is a temporary solution to backtracing syscalls in corefiles. + To do this properly, the AUXV section should be used. This + fix will work as long as the kernel used to generate the corefile + is equivalent to the kernel used to debug the corefile. */ + x = ia64_linux_xfer_unwind_table (¤t_target, + TARGET_OBJECT_UNWIND_TABLE, NULL, + buf, NULL, 0, len); return (int)x; } @@ -2306,27 +2473,20 @@ getunwind_table (void *buf, size_t len) static int get_kernel_table (unw_word_t ip, unw_dyn_info_t *di) { - size_t size; - struct ia64_table_entry - { - uint64_t start_offset; - uint64_t end_offset; - uint64_t info_offset; - }; - static struct ia64_table_entry *ktab = NULL, *etab; + static struct ia64_table_entry *etab; - if (!ktab) + if (!ktab) { + size_t size; size = getunwind_table (NULL, 0); if ((int)size < 0) - return -UNW_ENOINFO; - ktab = xmalloc (size); - getunwind_table (ktab, size); - - /* Determine length of kernel's unwind table and relocate - it's entries. */ + return -UNW_ENOINFO; + ktab_size = size; + ktab = xmalloc (ktab_size); + getunwind_table (ktab, ktab_size); + for (etab = ktab; etab->start_offset; ++etab) - etab->info_offset += (uint64_t) ktab; + etab->info_offset += KERNEL_START; } if (ip < ktab[0].start_offset || ip >= etab[-1].end_offset) @@ -2389,16 +2549,35 @@ ia64_find_unwind_table (struct objfile *objfile, unw_word_t ip, } } - if (!p_text || !p_unwind - /* Verify that the segment that contains the IP also contains - the static unwind table. If not, we are dealing with - runtime-generated code, for which we have no info here. */ - || (p_unwind->p_vaddr - p_text->p_vaddr) >= p_text->p_memsz) + if (!p_text || !p_unwind) return -UNW_ENOINFO; + /* Verify that the segment that contains the IP also contains + the static unwind table. If not, we may be in the Linux kernel's + DSO gate page in which case the unwind table is another segment. + Otherwise, we are dealing with runtime-generated code, for which we + have no info here. */ segbase = p_text->p_vaddr + load_base; - dip->start_ip = segbase; + if ((p_unwind->p_vaddr - p_text->p_vaddr) >= p_text->p_memsz) + { + int ok = 0; + for (i = 0; i < ehdr->e_phnum; ++i) + { + if (phdr[i].p_type == PT_LOAD + && (p_unwind->p_vaddr - phdr[i].p_vaddr) < phdr[i].p_memsz) + { + ok = 1; + /* Get the segbase from the section containing the + libunwind table. */ + segbase = phdr[i].p_vaddr + load_base; + } + } + if (!ok) + return -UNW_ENOINFO; + } + + dip->start_ip = p_text->p_vaddr + load_base; dip->end_ip = dip->start_ip + p_text->p_memsz; dip->gp = ia64_find_global_pointer (ip); dip->format = UNW_INFO_FORMAT_REMOTE_TABLE; @@ -2528,14 +2707,33 @@ ia64_libunwind_frame_this_id (struct frame_info *next_frame, void **this_cache, char buf[8]; CORE_ADDR bsp; struct frame_id id; + CORE_ADDR prev_ip, addr; + int realnum, optimized; + enum lval_type lval; + libunwind_frame_this_id (next_frame, this_cache, &id); + if (frame_id_eq (id, null_frame_id)) + { + (*this_id) = null_frame_id; + return; + } - /* We must add the bsp as the special address for frame comparison purposes. */ + /* We must add the bsp as the special address for frame comparison + purposes. */ frame_unwind_register (next_frame, IA64_BSP_REGNUM, buf); bsp = extract_unsigned_integer (buf, 8); - (*this_id) = frame_id_build_special (id.stack_addr, id.code_addr, bsp); + /* If the previous frame pc value is 0, then we are at the end of the stack + and don't want to unwind past this frame. We return a null frame_id to + indicate this. */ + libunwind_frame_prev_register (next_frame, this_cache, IA64_IP_REGNUM, + &optimized, &lval, &addr, &realnum, &prev_ip); + + if (prev_ip != 0) + (*this_id) = frame_id_build_special (id.stack_addr, id.code_addr, bsp); + else + (*this_id) = null_frame_id; if (gdbarch_debug >= 1) fprintf_unfiltered (gdb_stdlog, @@ -2625,8 +2823,13 @@ ia64_libunwind_frame_prev_register (struct frame_info *next_frame, if (gdbarch_debug >= 1) fprintf_unfiltered (gdb_stdlog, "libunwind prev register <%s> is 0x%s\n", - (((unsigned) regnum <= IA64_NAT127_REGNUM) - ? ia64_register_names[regnum] : "r??"), + (regnum < IA64_GR32_REGNUM + || (regnum > IA64_GR127_REGNUM + && regnum < LAST_PSEUDO_REGNUM)) + ? ia64_register_names[regnum] + : (regnum < LAST_PSEUDO_REGNUM + ? ia64_register_names[regnum-IA64_GR32_REGNUM+V32_REGNUM] + : "OUT_OF_RANGE"), paddr_nz (extract_unsigned_integer (valuep, 8))); } @@ -2646,6 +2849,86 @@ ia64_libunwind_frame_sniffer (struct frame_info *next_frame) return NULL; } +static void +ia64_libunwind_sigtramp_frame_this_id (struct frame_info *next_frame, void **this_cache, + struct frame_id *this_id) +{ + char buf[8]; + CORE_ADDR bsp; + struct frame_id id; + CORE_ADDR prev_ip; + + libunwind_frame_this_id (next_frame, this_cache, &id); + if (frame_id_eq (id, null_frame_id)) + { + (*this_id) = null_frame_id; + return; + } + + /* We must add the bsp as the special address for frame comparison + purposes. */ + frame_unwind_register (next_frame, IA64_BSP_REGNUM, buf); + bsp = extract_unsigned_integer (buf, 8); + + /* For a sigtramp frame, we don't make the check for previous ip being 0. */ + (*this_id) = frame_id_build_special (id.stack_addr, id.code_addr, bsp); + + if (gdbarch_debug >= 1) + fprintf_unfiltered (gdb_stdlog, + "libunwind sigtramp frame id: code 0x%s, stack 0x%s, special 0x%s, next_frame %p\n", + paddr_nz (id.code_addr), paddr_nz (id.stack_addr), + paddr_nz (bsp), next_frame); +} + +static void +ia64_libunwind_sigtramp_frame_prev_register (struct frame_info *next_frame, + void **this_cache, + int regnum, int *optimizedp, + enum lval_type *lvalp, CORE_ADDR *addrp, + int *realnump, void *valuep) + +{ + CORE_ADDR prev_ip, addr; + int realnum, optimized; + enum lval_type lval; + + + /* If the previous frame pc value is 0, then we want to use the SIGCONTEXT + method of getting previous registers. */ + libunwind_frame_prev_register (next_frame, this_cache, IA64_IP_REGNUM, + &optimized, &lval, &addr, &realnum, &prev_ip); + + if (prev_ip == 0) + { + void *tmp_cache = NULL; + ia64_sigtramp_frame_prev_register (next_frame, &tmp_cache, regnum, optimizedp, lvalp, + addrp, realnump, valuep); + } + else + ia64_libunwind_frame_prev_register (next_frame, this_cache, regnum, optimizedp, lvalp, + addrp, realnump, valuep); +} + +static const struct frame_unwind ia64_libunwind_sigtramp_frame_unwind = +{ + SIGTRAMP_FRAME, + ia64_libunwind_sigtramp_frame_this_id, + ia64_libunwind_sigtramp_frame_prev_register +}; + +static const struct frame_unwind * +ia64_libunwind_sigtramp_frame_sniffer (struct frame_info *next_frame) +{ + if (libunwind_is_initialized ()) + { + if (libunwind_sigtramp_frame_sniffer (next_frame)) + return &ia64_libunwind_sigtramp_frame_unwind; + return NULL; + } + else + return ia64_sigtramp_frame_sniffer (next_frame); +} + /* Set of libunwind callback acccessor functions. */ static unw_accessors_t ia64_unw_accessors = { @@ -2659,6 +2942,22 @@ static unw_accessors_t ia64_unw_accessors = /* get_proc_name */ }; +/* Set of special libunwind callback acccessor functions specific for accessing + the rse registers. At the top of the stack, we want libunwind to figure out + how to read r32 - r127. Though usually they are found sequentially in memory + starting from $bof, this is not always true. */ +static unw_accessors_t ia64_unw_rse_accessors = +{ + ia64_find_proc_info_x, + ia64_put_unwind_info, + ia64_get_dyn_info_list, + ia64_access_mem, + ia64_access_rse_reg, + ia64_access_fpreg, + /* resume */ + /* get_proc_name */ +}; + /* Set of ia64 gdb libunwind-frame callbacks and data for generic libunwind-frame code to use. */ static struct libunwind_descr ia64_libunwind_descr = { @@ -2666,6 +2965,7 @@ static struct libunwind_descr ia64_libunwind_descr = ia64_uw2gdb_regnum, ia64_is_fpreg, &ia64_unw_accessors, + &ia64_unw_rse_accessors, }; #endif /* HAVE_LIBUNWIND_IA64_H */ @@ -3307,10 +3607,12 @@ ia64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) set_gdbarch_unwind_dummy_id (gdbarch, ia64_unwind_dummy_id); set_gdbarch_unwind_pc (gdbarch, ia64_unwind_pc); - frame_unwind_append_sniffer (gdbarch, ia64_sigtramp_frame_sniffer); #ifdef HAVE_LIBUNWIND_IA64_H + frame_unwind_append_sniffer (gdbarch, ia64_libunwind_sigtramp_frame_sniffer); frame_unwind_append_sniffer (gdbarch, ia64_libunwind_frame_sniffer); libunwind_frame_set_descr (gdbarch, &ia64_libunwind_descr); +#else + frame_unwind_append_sniffer (gdbarch, ia64_sigtramp_frame_sniffer); #endif frame_unwind_append_sniffer (gdbarch, ia64_frame_sniffer); frame_base_set_default (gdbarch, &ia64_frame_base); diff --git a/gdb/libunwind-frame.c b/gdb/libunwind-frame.c index 4e5d6b5bc93..9f5a31e4fc0 100644 --- a/gdb/libunwind-frame.c +++ b/gdb/libunwind-frame.c @@ -49,6 +49,7 @@ static struct gdbarch_data *libunwind_descr_handle; static int (*unw_get_reg_p) (unw_cursor_t *, unw_regnum_t, unw_word_t *); static int (*unw_get_fpreg_p) (unw_cursor_t *, unw_regnum_t, unw_fpreg_t *); static int (*unw_get_saveloc_p) (unw_cursor_t *, unw_regnum_t, unw_save_loc_t *); +static int (*unw_is_signal_frame_p) (unw_cursor_t *); static int (*unw_step_p) (unw_cursor_t *); static int (*unw_init_remote_p) (unw_cursor_t *, unw_addr_space_t, void *); static unw_addr_space_t (*unw_create_addr_space_p) (unw_accessors_t *, int); @@ -78,6 +79,7 @@ struct libunwind_frame_cache static char *get_reg_name = STRINGIFY(UNW_OBJ(get_reg)); static char *get_fpreg_name = STRINGIFY(UNW_OBJ(get_fpreg)); static char *get_saveloc_name = STRINGIFY(UNW_OBJ(get_save_loc)); +static char *is_signal_frame_name = STRINGIFY(UNW_OBJ(is_signal_frame)); static char *step_name = STRINGIFY(UNW_OBJ(step)); static char *init_remote_name = STRINGIFY(UNW_OBJ(init_remote)); static char *create_addr_space_name = STRINGIFY(UNW_OBJ(create_addr_space)); @@ -119,6 +121,7 @@ libunwind_frame_set_descr (struct gdbarch *gdbarch, struct libunwind_descr *desc arch_descr->uw2gdb = descr->uw2gdb; arch_descr->is_fpreg = descr->is_fpreg; arch_descr->accessors = descr->accessors; + arch_descr->special_accessors = descr->special_accessors; } static struct libunwind_frame_cache * @@ -139,6 +142,10 @@ libunwind_frame_cache (struct frame_info *next_frame, void **this_cache) cache = FRAME_OBSTACK_ZALLOC (struct libunwind_frame_cache); cache->func_addr = frame_func_unwind (next_frame); + if (cache->func_addr == 0 + && frame_relative_level (next_frame) > 0 + && get_frame_type (next_frame) != SIGTRAMP_FRAME) + return NULL; /* Get a libunwind cursor to the previous frame. We do this by initializing a cursor. Libunwind treats a new cursor as the top of stack and will get @@ -156,7 +163,8 @@ libunwind_frame_cache (struct frame_info *next_frame, void **this_cache) : __LITTLE_ENDIAN); unw_init_remote_p (&cache->cursor, as, next_frame); - unw_step_p (&cache->cursor); + if (unw_step_p (&cache->cursor) < 0) + return NULL; /* To get base address, get sp from previous frame. */ uw_sp_regnum = descr->gdb2uw (SP_REGNUM); @@ -208,8 +216,14 @@ libunwind_frame_sniffer (struct frame_info *next_frame) ret = unw_init_remote_p (&cursor, as, next_frame); - if (ret >= 0) - ret = unw_step_p (&cursor); + if (ret < 0) + return NULL; + + + /* Check to see if we have libunwind info by checking if we are in a + signal frame. If it doesn't return an error, we have libunwind info + and can use libunwind. */ + ret = unw_is_signal_frame_p (&cursor); if (ret < 0) return NULL; @@ -224,7 +238,10 @@ libunwind_frame_this_id (struct frame_info *next_frame, void **this_cache, struct libunwind_frame_cache *cache = libunwind_frame_cache (next_frame, this_cache); - (*this_id) = frame_id_build (cache->base, cache->func_addr); + if (cache != NULL) + (*this_id) = frame_id_build (cache->base, cache->func_addr); + else + (*this_id) = null_frame_id; } void @@ -245,6 +262,9 @@ libunwind_frame_prev_register (struct frame_info *next_frame, void **this_cache, unw_regnum_t uw_regnum; struct libunwind_descr *descr; + if (cache == NULL) + return; + /* Convert from gdb register number to libunwind register number. */ descr = libunwind_descr (get_frame_arch (next_frame)); uw_regnum = descr->gdb2uw (regnum); @@ -312,6 +332,8 @@ libunwind_frame_base_address (struct frame_info *next_frame, void **this_cache) struct libunwind_frame_cache *cache = libunwind_frame_cache (next_frame, this_cache); + if (cache == NULL) + return (CORE_ADDR)NULL; return cache->base; } @@ -325,6 +347,95 @@ libunwind_search_unwind_table (void *as, long ip, void *di, di, pi, need_unwind_info, args); } +/* Verify if we are in a sigtramp frame and we can use libunwind to unwind. */ +const struct frame_unwind * +libunwind_sigtramp_frame_sniffer (struct frame_info *next_frame) +{ + unw_cursor_t cursor; + unw_accessors_t *acc; + unw_addr_space_t as; + struct libunwind_descr *descr; + int i, ret; + + /* To test for libunwind unwind support, initialize a cursor to the + current frame and try to back up. We use this same method when + setting up the frame cache (see libunwind_frame_cache()). If + libunwind returns success for this operation, it means that it + has found sufficient libunwind unwinding information to do + so. */ + + descr = libunwind_descr (get_frame_arch (next_frame)); + acc = descr->accessors; + as = unw_create_addr_space_p (acc, + TARGET_BYTE_ORDER == BFD_ENDIAN_BIG + ? __BIG_ENDIAN + : __LITTLE_ENDIAN); + + ret = unw_init_remote_p (&cursor, as, next_frame); + + if (ret < 0) + return NULL; + + /* Check to see if we are in a signal frame. */ + ret = unw_is_signal_frame_p (&cursor); + if (ret > 0) + return &libunwind_frame_unwind; + + return NULL; +} + +/* The following routine is for accessing special registers of the top frame. + A special set of accessors must be given that work without frame info. + This is used by ia64 to access the rse registers r32-r127. While they + are usually located at BOF, this is not always true and only the libunwind + info can decipher where they actually are. */ +int +libunwind_get_reg_special (struct gdbarch *gdbarch, int regnum, void *buf) +{ + unw_cursor_t cursor; + unw_accessors_t *acc; + unw_addr_space_t as; + struct libunwind_descr *descr; + int ret; + unw_regnum_t uw_regnum; + unw_word_t intval; + unw_fpreg_t fpval; + void *ptr; + + + descr = libunwind_descr (gdbarch); + acc = descr->special_accessors; + as = unw_create_addr_space_p (acc, + TARGET_BYTE_ORDER == BFD_ENDIAN_BIG + ? __BIG_ENDIAN + : __LITTLE_ENDIAN); + + ret = unw_init_remote_p (&cursor, as, NULL); + if (ret < 0) + return -1; + + uw_regnum = descr->gdb2uw (regnum); + + if (descr->is_fpreg (uw_regnum)) + { + ret = unw_get_fpreg_p (&cursor, uw_regnum, &fpval); + ptr = &fpval; + } + else + { + ret = unw_get_reg_p (&cursor, uw_regnum, &intval); + ptr = &intval; + } + + if (ret < 0) + return -1; + + if (buf) + memcpy (buf, ptr, register_size (current_gdbarch, regnum)); + + return 0; +} + static int libunwind_load (void) { @@ -348,6 +459,10 @@ libunwind_load (void) if (unw_get_saveloc_p == NULL) return 0; + unw_is_signal_frame_p = dlsym (handle, is_signal_frame_name); + if (unw_is_signal_frame_p == NULL) + return 0; + unw_step_p = dlsym (handle, step_name); if (unw_step_p == NULL) return 0; diff --git a/gdb/libunwind-frame.h b/gdb/libunwind-frame.h index e47a792cc98..5a90235f6d7 100644 --- a/gdb/libunwind-frame.h +++ b/gdb/libunwind-frame.h @@ -25,6 +25,7 @@ struct frame_info; struct frame_id; +struct gdbarch; #ifndef LIBUNWIND_FRAME_H #define LIBUNWIND_FRAME_H 1 @@ -37,9 +38,11 @@ struct libunwind_descr int (*uw2gdb) (int); int (*is_fpreg) (int); void *accessors; + void *special_accessors; }; const struct frame_unwind *libunwind_frame_sniffer (struct frame_info *next_frame); +const struct frame_unwind *libunwind_sigtramp_frame_sniffer (struct frame_info *next_frame); void libunwind_frame_set_descr (struct gdbarch *arch, struct libunwind_descr *descr); @@ -59,6 +62,8 @@ int libunwind_search_unwind_table (void *as, long ip, void *di, unw_word_t libunwind_find_dyn_list (unw_addr_space_t, unw_dyn_info_t *, void *); +int libunwind_get_reg_special (struct gdbarch *gdbarch, int regnum, void *buf); + #endif /* libunwind-frame.h */ #endif /* HAVE_LIBUNWIND_H */ -- 2.30.2