Re: [PATCH 7/8] ia64-hpux: unwinding bsp value from system call

Joel Brobecker
2010-12-31T06:33:19Z

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

Following Pedro's suggestion, here is a new revised version of this
patch that defines a new TARGET_OBJECT_HPUX_UREGS target object to
get to the __reason pseudo-register...

Thanks again, Pedro.

--
Joel

[-- Attachment #2: uregs.diff --]
[-- Type: text/x-diff, Size: 9335 bytes --]

commit e246563d5cc60890c2ceba3d5b85d30e3f1ce5b2
Author: Joel Brobecker <brobecker@adacore.com>
Date: Tue Dec 21 07:04:13 2010 -0500

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

diff --git a/gdb/ia64-hpux-nat.c b/gdb/ia64-hpux-nat.c
index 53941a7..80f7d99 100644
--- a/gdb/ia64-hpux-nat.c
+++ b/gdb/ia64-hpux-nat.c
@@ -567,6 +567,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
@@ -578,6 +600,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 139ff83..a4d2fa7 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 = &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);

diff --git a/gdb/ia64-tdep.c b/gdb/ia64-tdep.c
index 1cd6a38..1cfffc7 100644
--- a/gdb/ia64-tdep.c
+++ b/gdb/ia64-tdep.c
@@ -2423,7 +2423,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,
@@ -2460,7 +2460,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;

@@ -3812,6 +3812,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)
{
@@ -3826,6 +3834,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 b7a8eaf..b88031e 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 7290d90..24221ce 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -265,6 +265,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, ... */
};