From 77ca787b12516ebb1b0d23710021b26b9c81b018 Mon Sep 17 00:00:00 2001 From: Joel Brobecker Date: Thu, 13 Jan 2011 16:24:27 +0000 Subject: [PATCH] [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. --- gdb/ChangeLog | 18 ++++++++++++++ gdb/ia64-hpux-nat.c | 24 +++++++++++++++++++ gdb/ia64-hpux-tdep.c | 56 ++++++++++++++++++++++++++++++++++++++++++++ gdb/ia64-tdep.c | 14 +++++++++-- gdb/ia64-tdep.h | 9 +++++++ gdb/target.h | 3 +++ 6 files changed, 122 insertions(+), 2 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 814b0dc4426..54a8d05e8c0 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,21 @@ +2011-01-13 Joel Brobecker + + * 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 Add support for ia64-hpux. diff --git a/gdb/ia64-hpux-nat.c b/gdb/ia64-hpux-nat.c index ca1c278e3ac..fd58bbaf204 100644 --- a/gdb/ia64-hpux-nat.c +++ b/gdb/ia64-hpux-nat.c @@ -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); diff --git a/gdb/ia64-hpux-tdep.c b/gdb/ia64-hpux-tdep.c index 139ff836719..a4d2fa75234 100644 --- a/gdb/ia64-hpux-tdep.c +++ b/gdb/ia64-hpux-tdep.c @@ -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); diff --git a/gdb/ia64-tdep.c b/gdb/ia64-tdep.c index 05ca3a127ea..25298979249 100644 --- a/gdb/ia64-tdep.c +++ b/gdb/ia64-tdep.c @@ -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 diff --git a/gdb/ia64-tdep.h b/gdb/ia64-tdep.h index 7e53cb6f655..384cb14bd8a 100644 --- a/gdb/ia64-tdep.h +++ b/gdb/ia64-tdep.h @@ -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; }; diff --git a/gdb/target.h b/gdb/target.h index a1288d03860..4b2e6a871ef 100644 --- a/gdb/target.h +++ b/gdb/target.h @@ -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, ... */ }; -- 2.30.2