This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH 7/8] 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 ttrace call, which is not directly possible from ia64-tdep.c.
     So we get the info using a target-read of a TARGET_OBJECT_OSDATA
     object (with the annex set to "ia64.hpux.in_syscall").

  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:

        * 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".
        (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_in_syscall): New function.
        (ia64_hpux_xfer_os_data): New function.
        (ia64_hpux_xfer_partial): Add handling of TARGET_OBJECT_OSDATA objects.

---
 gdb/ia64-hpux-nat.c  |   56 ++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/ia64-hpux-tdep.c |   47 +++++++++++++++++++++++++++++++++++++++++
 gdb/ia64-tdep.c      |   14 ++++++++++-
 gdb/ia64-tdep.h      |    9 ++++++++
 4 files changed, 124 insertions(+), 2 deletions(-)

diff --git a/gdb/ia64-hpux-nat.c b/gdb/ia64-hpux-nat.c
index 53941a7..dfa6b7f 100644
--- a/gdb/ia64-hpux-nat.c
+++ b/gdb/ia64-hpux-nat.c
@@ -567,6 +567,60 @@ ia64_hpux_xfer_memory (struct target_ops *ops, const char *annex,
   return len;
 }
 
+/* Handle the transfer of the "ia64.hpux.in_syscall" TARGET_OBJECT_OSDATA
+   object.  */
+
+static LONGEST
+ia64_hpux_xfer_in_syscall (struct target_ops *ops, gdb_byte *readbuf,
+			   LONGEST len)
+{
+  struct gdbarch *gdbarch = target_gdbarch;
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  gdb_byte buf[8];
+  int status;
+
+  gdb_assert (len > 0); /* We need 1 byte to write the return value.  */
+
+  status = ia64_hpux_read_register_from_save_state_t (__reason, buf, 8);
+  if (status < 0)
+    {
+      /* This should never happen, but if it does, just warn the user
+	 and return -1 (we don't know).  The caller should be able to
+	 handle this condition.  */
+      warning (_("failed to read reason pseudo-register"));
+      return -1;
+    }
+
+  readbuf[0] = (extract_unsigned_integer (buf, 8, byte_order) == 0);
+  return 1;
+}
+
+/* Handle the transfer of TARGET_OBJECT_OSDATA objects on ia64-hpux.
+
+   The type of data being requested depends on the value of ANNEX:
+
+     * "ia64.hpux.in_syscall":
+       Store a 1-byte integer in READBUF.  The value of that integer
+       is nonzero iff the inferior is stopped inside a system call.
+       OFFSET is ignored.
+
+   Any other request is deferred to super_xfer_partial.
+   ANNEX is assumed to never be NULL.  */
+
+static LONGEST
+ia64_hpux_xfer_os_data (struct target_ops *ops, const char *annex,
+			gdb_byte *readbuf, ULONGEST offset, LONGEST len)
+{
+  gdb_assert (annex != NULL);
+
+  if (strcmp (annex, "ia64.hpux.in_syscall") == 0)
+    return ia64_hpux_xfer_in_syscall (ops, readbuf, len);
+  else
+    /* Unknown OSDATA request, delegate to super_xfer_partial.  */
+    return super_xfer_partial (ops, TARGET_OBJECT_OSDATA, annex, readbuf,
+                               NULL, offset, len);
+}
+
 /* The "to_xfer_partial" target_ops routine for ia64-hpux.  */
 
 static LONGEST
@@ -578,6 +632,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 (readbuf && object == TARGET_OBJECT_OSDATA && annex)
+    val = ia64_hpux_xfer_os_data (ops, annex, readbuf, 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..5dd0a4e 100644
--- a/gdb/ia64-hpux-tdep.c
+++ b/gdb/ia64-hpux-tdep.c
@@ -23,6 +23,8 @@
 #include "osabi.h"
 #include "gdbtypes.h"
 #include "solib.h"
+#include "target.h"
+#include "frame.h"
 
 /* Return nonzero if the value of the register identified by REGNUM
    can be modified.  */
@@ -74,6 +76,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[1];
+  int len;
+
+  len = target_read (ops, TARGET_OBJECT_OSDATA, "ia64.hpux.in_syscall",
+		     buf, 0, 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 == 1);
+
+  return extract_unsigned_integer (buf, len, byte_order);
+}
+
+/* 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 +126,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;
 };
-- 
1.7.1


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]