This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
[PATCH 7/8] ia64-hpux: unwinding bsp value from system call
- From: Joel Brobecker <brobecker at adacore dot com>
- To: gdb-patches at sourceware dot org
- Cc: Joel Brobecker <brobecker at adacore dot com>
- Date: Tue, 28 Dec 2010 08:43:05 +0400
- Subject: [PATCH 7/8] ia64-hpux: unwinding bsp value from system call
- References: <1293511386-7384-1-git-send-email-brobecker@adacore.com>
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 = ¤t_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