[ia64-hpux] unwinding bsp value from system call

This fixes unwinding from a thread that is stopped inside a system call.
This can be seen when switching to a thread that is stopped doing a
pthread_cond_wait, for instance...

The comments inside the code should explain what is happening in our
case (the HP-UX exception in the case of system calls): Under certain
circumstances (program stopped inside syscall), the offset to apply to
the current BSP in order to compute the previous BSP is not the usual
CFM & 0x7f.

We parts in this patch:

  1. Figuring out that we are stopped inside a syscal: This requires
     a TT_LWP_RUREGS ttrace call, which is not directly possible from
     ia64-tdep.c.  So use defined a new TARGET_OBJECT_HPUX_UREGS object
     to request it from the -nat side.

  2. Add a gdbarch_tdep method that allows us to change the default
     behavior on ia64-hpux, permitting us to have a different "size of
     register frame" in that one particular case.

gdb/ChangeLog:

        * target.h (enum target_object): Add TARGET_OBJECT_HPUX_UREGS.
        * ia64-tdep.h (struct frame_info): forward declaration.
        (struct gdbarch_tdep): Add field size_of_register_frame.
        * ia64-tdep.c (ia64_access_reg): Use tdep->size_of_register_frame
        to determine the size of the register frame.
        (ia64_size_of_register_frame): New function.
        (ia64_gdbarch_init): Set tdep->size_of_register_frame.
        * ia64-hpux-tdep.c: Include "target.h" and "frame.h".
        (IA64_HPUX_UREG_REASON): New macro.
        (ia64_hpux_stopped_in_syscall, ia64_hpux_size_of_register_frame):
        New functions.
        (ia64_hpux_init_abi): Set tdep->size_of_register_frame.
        * ia64-hpux-nat.c (ia64_hpux_xfer_uregs): New function.
        (ia64_hpux_xfer_partial): Add handling of TARGET_OBJECT_HPUX_UREGS
        objects.
This commit is contained in:
Joel Brobecker 2011-01-13 16:24:27 +00:00
parent 92c9a4639b
commit 77ca787b12
6 changed files with 122 additions and 2 deletions

View file

@ -1,3 +1,21 @@
2011-01-13 Joel Brobecker <brobecker@adacore.com>
* target.h (enum target_object): Add TARGET_OBJECT_HPUX_UREGS.
* ia64-tdep.h (struct frame_info): forward declaration.
(struct gdbarch_tdep): Add field size_of_register_frame.
* ia64-tdep.c (ia64_access_reg): Use tdep->size_of_register_frame
to determine the size of the register frame.
(ia64_size_of_register_frame): New function.
(ia64_gdbarch_init): Set tdep->size_of_register_frame.
* ia64-hpux-tdep.c: Include "target.h" and "frame.h".
(IA64_HPUX_UREG_REASON): New macro.
(ia64_hpux_stopped_in_syscall, ia64_hpux_size_of_register_frame):
New functions.
(ia64_hpux_init_abi): Set tdep->size_of_register_frame.
* ia64-hpux-nat.c (ia64_hpux_xfer_uregs): New function.
(ia64_hpux_xfer_partial): Add handling of TARGET_OBJECT_HPUX_UREGS
objects.
2011-01-13 Joel Brobecker <brobecker@adacore.com>
Add support for ia64-hpux.

View file

@ -568,6 +568,28 @@ ia64_hpux_xfer_memory (struct target_ops *ops, const char *annex,
return len;
}
/* Handle the transfer of TARGET_OBJECT_HPUX_UREGS objects on ia64-hpux.
ANNEX is currently ignored.
The current implementation does not support write transfers (because
we do not currently do not need these transfers), and will raise
a failed assertion if WRITEBUF is not NULL. */
static LONGEST
ia64_hpux_xfer_uregs (struct target_ops *ops, const char *annex,
gdb_byte *readbuf, const gdb_byte *writebuf,
ULONGEST offset, LONGEST len)
{
int status;
gdb_assert (writebuf == NULL);
status = ia64_hpux_read_register_from_save_state_t (offset, readbuf, len);
if (status < 0)
return -1;
return len;
}
/* The "to_xfer_partial" target_ops routine for ia64-hpux. */
static LONGEST
@ -579,6 +601,8 @@ ia64_hpux_xfer_partial (struct target_ops *ops, enum target_object object,
if (object == TARGET_OBJECT_MEMORY)
val = ia64_hpux_xfer_memory (ops, annex, readbuf, writebuf, offset, len);
else if (object == TARGET_OBJECT_HPUX_UREGS)
val = ia64_hpux_xfer_uregs (ops, annex, readbuf, writebuf, offset, len);
else
val = super_xfer_partial (ops, object, annex, readbuf, writebuf, offset,
len);

View file

@ -23,6 +23,17 @@
#include "osabi.h"
#include "gdbtypes.h"
#include "solib.h"
#include "target.h"
#include "frame.h"
/* The offset to be used in order to get the __reason pseudo-register
when using one of the *UREGS ttrace requests (see system header file
/usr/include/ia64/sys/uregs.h for more details).
The documentation for this pseudo-register says that a nonzero value
indicates that the thread stopped due to a fault, trap, or interrupt.
A null value indicates a stop inside a syscall. */
#define IA64_HPUX_UREG_REASON 0x00070000
/* Return nonzero if the value of the register identified by REGNUM
can be modified. */
@ -74,6 +85,47 @@ ia64_hpux_cannot_store_register (struct gdbarch *gdbarch, int regnum)
return 0;
}
/* Return nonzero if the inferior is stopped inside a system call. */
static int
ia64_hpux_stopped_in_syscall (struct gdbarch *gdbarch)
{
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
struct target_ops *ops = &current_target;
gdb_byte buf[8];
int len;
len = target_read (ops, TARGET_OBJECT_HPUX_UREGS, NULL,
buf, IA64_HPUX_UREG_REASON, sizeof (buf));
if (len == -1)
/* The target wasn't able to tell us. Assume we are not stopped
in a system call, which is the normal situation. */
return 0;
gdb_assert (len == 8);
return (extract_unsigned_integer (buf, len, byte_order) == 0);
}
/* The "size_of_register_frame" gdbarch_tdep routine for ia64-hpux. */
static int
ia64_hpux_size_of_register_frame (struct frame_info *this_frame,
ULONGEST cfm)
{
int sof;
if (frame_relative_level (this_frame) == 0
&& ia64_hpux_stopped_in_syscall (get_frame_arch (this_frame)))
/* If the inferior stopped in a system call, the base address
of the register frame is at BSP - SOL instead of BSP - SOF.
This is an HP-UX exception. */
sof = (cfm & 0x3f80) >> 7;
else
sof = (cfm & 0x7f);
return sof;
}
/* Should be set to non-NULL if the ia64-hpux solib module is linked in.
This may not be the case because the shared library support code can
only be compiled on ia64-hpux. */
@ -83,6 +135,10 @@ struct target_so_ops *ia64_hpux_so_ops = NULL;
static void
ia64_hpux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
tdep->size_of_register_frame = ia64_hpux_size_of_register_frame;
set_gdbarch_long_double_format (gdbarch, floatformats_ia64_quad);
set_gdbarch_cannot_store_register (gdbarch, ia64_hpux_cannot_store_register);

View file

@ -2452,7 +2452,7 @@ ia64_is_fpreg (int uw_regnum)
{
return unw_is_fpreg (uw_regnum);
}
/* Libunwind callback accessor function for general registers. */
static int
ia64_access_reg (unw_addr_space_t as, unw_regnum_t uw_regnum, unw_word_t *val,
@ -2490,7 +2490,7 @@ ia64_access_reg (unw_addr_space_t as, unw_regnum_t uw_regnum, unw_word_t *val,
bsp = extract_unsigned_integer (buf, 8, byte_order);
get_frame_register (this_frame, IA64_CFM_REGNUM, buf);
cfm = extract_unsigned_integer (buf, 8, byte_order);
sof = (cfm & 0x7f);
sof = gdbarch_tdep (gdbarch)->size_of_register_frame (this_frame, cfm);
*val = ia64_rse_skip_regs (bsp, -sof);
break;
@ -3848,6 +3848,14 @@ ia64_print_insn (bfd_vma memaddr, struct disassemble_info *info)
return print_insn_ia64 (memaddr, info);
}
/* The default "size_of_register_frame" gdbarch_tdep routine for ia64. */
static int
ia64_size_of_register_frame (struct frame_info *this_frame, ULONGEST cfm)
{
return (cfm & 0x7f);
}
static struct gdbarch *
ia64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
{
@ -3862,6 +3870,8 @@ ia64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
tdep = xzalloc (sizeof (struct gdbarch_tdep));
gdbarch = gdbarch_alloc (&info, tdep);
tdep->size_of_register_frame = ia64_size_of_register_frame;
/* According to the ia64 specs, instructions that store long double
floats in memory use a long-double format different than that
used in the floating registers. The memory format matches the

View file

@ -195,11 +195,20 @@
#define IA64_NAT32_REGNUM (IA64_NAT0_REGNUM + 32)
#define IA64_NAT127_REGNUM (IA64_NAT0_REGNUM + 127)
struct frame_info;
struct gdbarch_tdep
{
CORE_ADDR (*sigcontext_register_address) (struct gdbarch *, CORE_ADDR, int);
int (*pc_in_sigtramp) (CORE_ADDR);
/* Return the total size of THIS_FRAME's register frame.
CFM is THIS_FRAME's cfm register value.
Normally, the size of the register frame is always obtained by
extracting the lowest 7 bits ("cfm & 0x7f"). */
int (*size_of_register_frame) (struct frame_info *this_frame, ULONGEST cfm);
/* ISA-specific data types. */
struct type *ia64_ext_type;
};

View file

@ -266,6 +266,9 @@ enum target_object
TARGET_OBJECT_THREADS,
/* Collected static trace data. */
TARGET_OBJECT_STATIC_TRACE_DATA,
/* The HP-UX registers (those that can be obtained or modified by using
the TT_LWP_RUREGS/TT_LWP_WUREGS ttrace requests). */
TARGET_OBJECT_HPUX_UREGS,
/* Possible future objects: TARGET_OBJECT_FILE, ... */
};