From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 13672 invoked by alias); 28 Dec 2010 04:44:16 -0000 Received: (qmail 13493 invoked by uid 22791); 28 Dec 2010 04:44:14 -0000 X-SWARE-Spam-Status: No, hits=-2.0 required=5.0 tests=AWL,BAYES_00,TW_XZ,T_RP_MATCHES_RCVD X-Spam-Check-By: sourceware.org Received: from rock.gnat.com (HELO rock.gnat.com) (205.232.38.15) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Tue, 28 Dec 2010 04:44:05 +0000 Received: from localhost (localhost.localdomain [127.0.0.1]) by filtered-rock.gnat.com (Postfix) with ESMTP id 06D012BABC4; Mon, 27 Dec 2010 23:44:04 -0500 (EST) Received: from rock.gnat.com ([127.0.0.1]) by localhost (rock.gnat.com [127.0.0.1]) (amavisd-new, port 10024) with LMTP id fHP40g+iShxX; Mon, 27 Dec 2010 23:44:03 -0500 (EST) Received: from joel.gnat.com (localhost.localdomain [127.0.0.1]) by rock.gnat.com (Postfix) with ESMTP id 0936A2BAB2A; Mon, 27 Dec 2010 23:44:03 -0500 (EST) Received: by joel.gnat.com (Postfix, from userid 1000) id 758D11457BB; Tue, 28 Dec 2010 05:43:54 +0100 (CET) From: Joel Brobecker To: gdb-patches@sourceware.org Cc: Joel Brobecker Subject: [PATCH 7/8] ia64-hpux: unwinding bsp value from system call Date: Tue, 28 Dec 2010 04:54:00 -0000 Message-Id: <1293511386-7384-8-git-send-email-brobecker@adacore.com> In-Reply-To: <1293511386-7384-1-git-send-email-brobecker@adacore.com> References: <1293511386-7384-1-git-send-email-brobecker@adacore.com> Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2010-12/txt/msg00486.txt.bz2 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