Fetch signal information for native FreeBSD processes.
authorJohn Baldwin <jhb@FreeBSD.org>
Wed, 28 Jun 2017 16:53:06 +0000 (09:53 -0700)
committerJohn Baldwin <jhb@FreeBSD.org>
Fri, 7 Jul 2017 23:05:47 +0000 (16:05 -0700)
Use the `pl_siginfo' field in the `struct ptrace_lwpinfo' object returned
by the PT_LWPINFO ptrace() request to supply the current contents of
$_siginfo for each thread.  Note that FreeBSD does not supply a way to
modify the signal information for a thread, so $_siginfo is read-only for
FreeBSD.

To handle 32-bit processes on a 64-bit host, define types for 32-bit
compatible siginfo_t and convert the 64-bit siginfo_t to the 32-bit
equivalent when supplying information for a 32-bit process.

gdb/ChangeLog:

* fbsd-nat.c [PT_LWPINFO && __LP64__] (union sigval32)
(struct siginfo32): New.
[PT_LWPINFO] (fbsd_siginfo_size, fbsd_convert_siginfo): New.
(fbsd_xfer_partial) [PT_LWPINFO]: Handle TARGET_OBJECT_SIGNAL_INFO
via ptrace(PT_LWPINFO).

gdb/ChangeLog
gdb/fbsd-nat.c

index d5afeade3a3fb237e3c56da18e1c83d706b3e912..1809ebaf7742546efe2d29663a1c8251462f5edb 100644 (file)
@@ -1,3 +1,11 @@
+2017-07-07  John Baldwin  <jhb@FreeBSD.org>
+
+       * fbsd-nat.c [PT_LWPINFO && __LP64__] (union sigval32)
+       (struct siginfo32): New.
+       [PT_LWPINFO] (fbsd_siginfo_size, fbsd_convert_siginfo): New.
+       (fbsd_xfer_partial) [PT_LWPINFO]: Handle TARGET_OBJECT_SIGNAL_INFO
+       via ptrace(PT_LWPINFO).
+
 2017-07-07  John Baldwin  <jhb@FreeBSD.org>
 
        * fbsd-tdep.c (fbsd_gdbarch_data_handle, struct fbsd_gdbarch_data)
index ef5ad1ec92bee9f3cf2ea9ac6443b4ca98146e69..85f56059bdf81b17ee46ad55143dfedf0f2fe9a0 100644 (file)
@@ -28,6 +28,7 @@
 #include <sys/types.h>
 #include <sys/procfs.h>
 #include <sys/ptrace.h>
+#include <sys/signal.h>
 #include <sys/sysctl.h>
 #ifdef HAVE_KINFO_GETVMMAP
 #include <sys/user.h>
@@ -216,6 +217,135 @@ static enum target_xfer_status (*super_xfer_partial) (struct target_ops *ops,
                                                      ULONGEST len,
                                                      ULONGEST *xfered_len);
 
+#ifdef PT_LWPINFO
+/* Return the size of siginfo for the current inferior.  */
+
+#ifdef __LP64__
+union sigval32 {
+  int sival_int;
+  uint32_t sival_ptr;
+};
+
+/* This structure matches the naming and layout of `siginfo_t' in
+   <sys/signal.h>.  In particular, the `si_foo' macros defined in that
+   header can be used with both types to copy fields in the `_reason'
+   union.  */
+
+struct siginfo32
+{
+  int si_signo;
+  int si_errno;
+  int si_code;
+  __pid_t si_pid;
+  __uid_t si_uid;
+  int si_status;
+  uint32_t si_addr;
+  union sigval32 si_value;
+  union
+  {
+    struct
+    {
+      int _trapno;
+    } _fault;
+    struct
+    {
+      int _timerid;
+      int _overrun;
+    } _timer;
+    struct
+    {
+      int _mqd;
+    } _mesgq;
+    struct
+    {
+      int32_t _band;
+    } _poll;
+    struct
+    {
+      int32_t __spare1__;
+      int __spare2__[7];
+    } __spare__;
+  } _reason;
+};
+#endif
+
+static size_t
+fbsd_siginfo_size ()
+{
+#ifdef __LP64__
+  struct gdbarch *gdbarch = get_frame_arch (get_current_frame ());
+
+  /* Is the inferior 32-bit?  If so, use the 32-bit siginfo size.  */
+  if (gdbarch_bfd_arch_info (gdbarch)->bits_per_word == 32)
+    return sizeof (struct siginfo32);
+#endif
+  return sizeof (siginfo_t);
+}
+
+/* Convert a native 64-bit siginfo object to a 32-bit object.  Note
+   that FreeBSD doesn't support writing to $_siginfo, so this only
+   needs to convert one way.  */
+
+static void
+fbsd_convert_siginfo (siginfo_t *si)
+{
+#ifdef __LP64__
+  struct gdbarch *gdbarch = get_frame_arch (get_current_frame ());
+
+  /* Is the inferior 32-bit?  If not, nothing to do.  */
+  if (gdbarch_bfd_arch_info (gdbarch)->bits_per_word != 32)
+    return;
+
+  struct siginfo32 si32;
+
+  si32.si_signo = si->si_signo;
+  si32.si_errno = si->si_errno;
+  si32.si_code = si->si_code;
+  si32.si_pid = si->si_pid;
+  si32.si_uid = si->si_uid;
+  si32.si_status = si->si_status;
+  si32.si_addr = (uintptr_t) si->si_addr;
+
+  /* If sival_ptr is being used instead of sival_int on a big-endian
+     platform, then sival_int will be zero since it holds the upper
+     32-bits of the pointer value.  */
+#if _BYTE_ORDER == _BIG_ENDIAN
+  if (si->si_value.sival_int == 0)
+    si32->si_value.sival_ptr = (uintptr_t) si->si_value.sival_ptr;
+  else
+    si32.si_value.sival_int = si->si_value.sival_int;
+#else
+  si32.si_value.sival_int = si->si_value.sival_int;
+#endif
+
+  /* Always copy the spare fields and then possibly overwrite them for
+     signal-specific or code-specific fields.  */
+  si32._reason.__spare__.__spare1__ = si->_reason.__spare__.__spare1__;
+  for (int i = 0; i < 7; i++)
+    si32._reason.__spare__.__spare2__[i] = si->_reason.__spare__.__spare2__[i];
+  switch (si->si_signo) {
+  case SIGILL:
+  case SIGFPE:
+  case SIGSEGV:
+  case SIGBUS:
+    si32.si_trapno = si->si_trapno;
+    break;
+  }
+  switch (si->si_code) {
+  case SI_TIMER:
+    si32.si_timerid = si->si_timerid;
+    si32.si_overrun = si->si_overrun;
+    break;
+  case SI_MESGQ:
+    si32.si_mqd = si->si_mqd;
+    break;
+  }
+
+  memcpy(si, &si32, sizeof (si32));
+#endif
+}
+#endif
+
 /* Implement the "to_xfer_partial target_ops" method.  */
 
 static enum target_xfer_status
@@ -228,6 +358,38 @@ fbsd_xfer_partial (struct target_ops *ops, enum target_object object,
 
   switch (object)
     {
+#ifdef PT_LWPINFO
+    case TARGET_OBJECT_SIGNAL_INFO:
+      {
+       struct ptrace_lwpinfo pl;
+       size_t siginfo_size;
+
+       /* FreeBSD doesn't support writing to $_siginfo.  */
+       if (writebuf != NULL)
+         return TARGET_XFER_E_IO;
+
+       if (inferior_ptid.lwp_p ())
+         pid = inferior_ptid.lwp ();
+
+       siginfo_size = fbsd_siginfo_size ();
+       if (offset > siginfo_size)
+         return TARGET_XFER_E_IO;
+
+       if (ptrace (PT_LWPINFO, pid, (PTRACE_TYPE_ARG3) &pl, sizeof (pl)) == -1)
+         return TARGET_XFER_E_IO;
+
+       if (!(pl.pl_flags & PL_FLAG_SI))
+         return TARGET_XFER_E_IO;
+
+       fbsd_convert_siginfo (&pl.pl_siginfo);
+       if (offset + len > siginfo_size)
+         len = siginfo_size - offset;
+
+       memcpy (readbuf, ((gdb_byte *) &pl.pl_siginfo) + offset, len);
+       *xfered_len = len;
+       return TARGET_XFER_OK;
+      }
+#endif
     case TARGET_OBJECT_AUXV:
       {
        struct cleanup *cleanup = make_cleanup (null_cleanup, NULL);