diff mbox

[2/8] Fetch signal information for native FreeBSD processes.

Message ID 20170629233226.20155-3-jhb@FreeBSD.org
State New
Headers show

Commit Message

John Baldwin June 29, 2017, 11:32 p.m. UTC
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  |   8 +++
 gdb/fbsd-nat.c | 155 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 163 insertions(+)
diff mbox

Patch

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 43d422b4ab..1e624d3667 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,13 @@ 
 2017-06-28  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-06-28  John Baldwin  <jhb@FreeBSD.org>
+
 	* fbsd-tdep.c (fbsd_gdbarch_data_handle, struct fbsd_gdbarch_data)
 	(init_fbsd_gdbarch_data, get_fbsd_gdbarch_data)
 	(fbsd_get_siginfo_type): New.
diff --git a/gdb/fbsd-nat.c b/gdb/fbsd-nat.c
index ef5ad1ec92..3852b8e48d 100644
--- a/gdb/fbsd-nat.c
+++ b/gdb/fbsd-nat.c
@@ -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,128 @@  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 +351,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);