[COMMIT]: ia64 update

Jeff Johnston
2005-06-08T22:02:00Z

[-- Attachment #1: Type: text/plain, Size: 2455 bytes --]

A long-overdue update of the ia64 code has been made. It contains a number of
changes worked on by myself and David Mosberger that were only in Red Hat's ia64
gdb. The testsuite results in 23 less failures (127 down to 104). Most
importantly, backtracing via libunwind is much improved, including vsyscall
support, threaded backtraces, and stopping backtraces.

2005-06-08 Jeff Johnston <jjohnstn@redhat.com>
David Mosberger <davidm@hpl.hp.com>

* 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.

[-- Attachment #2: ia64-update.patch --]
[-- Type: text/plain, Size: 26075 bytes --]

Index: ia64-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/ia64-tdep.c,v
retrieving revision 1.132
diff -u -p -r1.132 ia64-tdep.c
--- ia64-tdep.c 19 May 2005 16:37:09 -0000 1.132
+++ ia64-tdep.c 8 Jun 2005 21:52:35 -0000
@@ -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 gdbarc
{
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)
- {
- 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);
+ /* 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)
+ {
+ /* 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 *n
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 (struc
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,
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 (&current_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 (&current_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 *
}
}

- 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 fra
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 (stru
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 fra
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_accessor
/* 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_libun
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 i
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);
Index: libunwind-frame.c
===================================================================
RCS file: /cvs/src/src/gdb/libunwind-frame.c,v
retrieving revision 1.6
diff -u -p -r1.6 libunwind-frame.c
--- libunwind-frame.c 11 Feb 2005 04:05:56 -0000 1.6
+++ libunwind-frame.c 8 Jun 2005 21:52:35 -0000
@@ -49,6 +49,7 @@ static struct gdbarch_data *libunwind_de
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 gdbarc
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
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
: __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_in

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_in
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 fr
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 fra
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,
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;
Index: libunwind-frame.h
===================================================================
RCS file: /cvs/src/src/gdb/libunwind-frame.h,v
retrieving revision 1.3
diff -u -p -r1.3 libunwind-frame.h
--- libunwind-frame.h 5 Jan 2004 19:53:07 -0000 1.3
+++ libunwind-frame.h 8 Jun 2005 21:52:35 -0000
@@ -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
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 */