[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:
parent
92c9a4639b
commit
77ca787b12
6 changed files with 122 additions and 2 deletions
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 = ¤t_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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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, ... */
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue