* amd64-linux-nat.c (compat_int_t, compat_uptr_t, compat_time_t)
authorPedro Alves <palves@redhat.com>
Fri, 6 Feb 2009 23:06:58 +0000 (23:06 +0000)
committerPedro Alves <palves@redhat.com>
Fri, 6 Feb 2009 23:06:58 +0000 (23:06 +0000)
(compat_timer_t, compat_clock_t, struct compat_timeval)
(compat_sigval_t, compat_siginfo_t): New types.
(cpt_si_pid, cpt_si_uid, cpt_si_timerid, cpt_si_overrun)
(cpt_si_status, cpt_si_utime, cpt_si_stime, cpt_si_ptr)
(cpt_si_addr, cpt_si_band, cpt_si_fd): New defines.
(compat_siginfo_from_siginfo, siginfo_from_compat_siginfo)
(amd64_linux_siginfo_fixup): New.
* linux-nat.c (linux_nat_siginfo_fixup): New.
(siginfo_fixup): New.
(linux_xfer_siginfo): Use siginfo_fixup to convert between the
siginfo layout expected by ptrace and the siginfo layout of the
inferior.
(linux_nat_set_siginfo_fixup): New.
* linux-nat.h (linux_nat_set_siginfo_fixup): Declare.

gdb/ChangeLog
gdb/amd64-linux-nat.c
gdb/linux-nat.c
gdb/linux-nat.h

index 56496fd49d6718708e088d927cd1fec71a215f1f..8b4bcc00ddba6761650ea2774c4d3fcef58be268 100644 (file)
@@ -1,3 +1,21 @@
+2009-02-06  Pedro Alves  <pedro@codesourcery.com>
+
+       * amd64-linux-nat.c (compat_int_t, compat_uptr_t, compat_time_t)
+       (compat_timer_t, compat_clock_t, struct compat_timeval)
+       (compat_sigval_t, compat_siginfo_t): New types.
+       (cpt_si_pid, cpt_si_uid, cpt_si_timerid, cpt_si_overrun)
+       (cpt_si_status, cpt_si_utime, cpt_si_stime, cpt_si_ptr)
+       (cpt_si_addr, cpt_si_band, cpt_si_fd): New defines.
+       (compat_siginfo_from_siginfo, siginfo_from_compat_siginfo)
+       (amd64_linux_siginfo_fixup): New.
+       * linux-nat.c (linux_nat_siginfo_fixup): New.
+       (siginfo_fixup): New.
+       (linux_xfer_siginfo): Use siginfo_fixup to convert between the
+       siginfo layout expected by ptrace and the siginfo layout of the
+       inferior.
+       (linux_nat_set_siginfo_fixup): New.
+       * linux-nat.h (linux_nat_set_siginfo_fixup): Declare.
+
 2009-02-06  Pedro Alves  <pedro@codesourcery.com>
 
        * target.h (enum target_object): Add new TARGET_OBJECT_SIGNAL_INFO.
index e4ae88db096bafd347ab5dde459c4d8c712bbd90..30f313795ff4a9b88d7b0b1afaf5d993642a1a4f 100644 (file)
@@ -400,6 +400,239 @@ amd64_linux_child_post_startup_inferior (ptid_t ptid)
 }
 \f
 
+/* When GDB is built as a 64-bit application on linux, the
+   PTRACE_GETSIGINFO data is always presented in 64-bit layout.  Since
+   debugging a 32-bit inferior with a 64-bit GDB should look the same
+   as debugging it with a 32-bit GDB, we do the 32-bit <-> 64-bit
+   conversion in-place ourselves.  */
+
+/* These types below (compat_*) define a siginfo type that is layout
+   compatible with the siginfo type exported by the 32-bit userspace
+   support.  */
+
+typedef int compat_int_t;
+typedef unsigned int compat_uptr_t;
+
+typedef int compat_time_t;
+typedef int compat_timer_t;
+typedef int compat_clock_t;
+
+struct compat_timeval
+{
+  compat_time_t tv_sec;
+  int tv_usec;
+};
+
+typedef union compat_sigval
+{
+  compat_int_t sival_int;
+  compat_uptr_t sival_ptr;
+} compat_sigval_t;
+
+typedef struct compat_siginfo
+{
+  int si_signo;
+  int si_errno;
+  int si_code;
+
+  union
+  {
+    int _pad[((128 / sizeof (int)) - 3)];
+
+    /* kill() */
+    struct
+    {
+      unsigned int _pid;
+      unsigned int _uid;
+    } _kill;
+
+    /* POSIX.1b timers */
+    struct
+    {
+      compat_timer_t _tid;
+      int _overrun;
+      compat_sigval_t _sigval;
+    } _timer;
+
+    /* POSIX.1b signals */
+    struct
+    {
+      unsigned int _pid;
+      unsigned int _uid;
+      compat_sigval_t _sigval;
+    } _rt;
+
+    /* SIGCHLD */
+    struct
+    {
+      unsigned int _pid;
+      unsigned int _uid;
+      int _status;
+      compat_clock_t _utime;
+      compat_clock_t _stime;
+    } _sigchld;
+
+    /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
+    struct
+    {
+      unsigned int _addr;
+    } _sigfault;
+
+    /* SIGPOLL */
+    struct
+    {
+      int _band;
+      int _fd;
+    } _sigpoll;
+  } _sifields;
+} compat_siginfo_t;
+
+#define cpt_si_pid _sifields._kill._pid
+#define cpt_si_uid _sifields._kill._uid
+#define cpt_si_timerid _sifields._timer._tid
+#define cpt_si_overrun _sifields._timer._overrun
+#define cpt_si_status _sifields._sigchld._status
+#define cpt_si_utime _sifields._sigchld._utime
+#define cpt_si_stime _sifields._sigchld._stime
+#define cpt_si_ptr _sifields._rt._sigval.sival_ptr
+#define cpt_si_addr _sifields._sigfault._addr
+#define cpt_si_band _sifields._sigpoll._band
+#define cpt_si_fd _sifields._sigpoll._fd
+
+static void
+compat_siginfo_from_siginfo (compat_siginfo_t *to, siginfo_t *from)
+{
+  memset (to, 0, sizeof (*to));
+
+  to->si_signo = from->si_signo;
+  to->si_errno = from->si_errno;
+  to->si_code = from->si_code;
+
+  if (to->si_code < 0)
+    {
+      to->cpt_si_ptr = (intptr_t) from->si_ptr;
+    }
+  else if (to->si_code == SI_USER)
+    {
+      to->cpt_si_pid = from->si_pid;
+      to->cpt_si_uid = from->si_uid;
+    }
+  else if (to->si_code == SI_TIMER)
+    {
+      to->cpt_si_timerid = from->si_timerid;
+      to->cpt_si_overrun = from->si_overrun;
+      to->cpt_si_ptr = (intptr_t) from->si_ptr;
+    }
+  else
+    {
+      switch (to->si_signo)
+       {
+       case SIGCHLD:
+         to->cpt_si_pid = from->si_pid;
+         to->cpt_si_uid = from->si_uid;
+         to->cpt_si_status = from->si_status;
+         to->cpt_si_utime = from->si_utime;
+         to->cpt_si_stime = from->si_stime;
+         break;
+       case SIGILL:
+       case SIGFPE:
+       case SIGSEGV:
+       case SIGBUS:
+         to->cpt_si_addr = (intptr_t) from->si_addr;
+         break;
+       case SIGPOLL:
+         to->cpt_si_band = from->si_band;
+         to->cpt_si_fd = from->si_fd;
+         break;
+       default:
+         to->cpt_si_pid = from->si_pid;
+         to->cpt_si_uid = from->si_uid;
+         to->cpt_si_ptr = (intptr_t) from->si_ptr;
+         break;
+       }
+    }
+}
+
+static void
+siginfo_from_compat_siginfo (siginfo_t *to, compat_siginfo_t *from)
+{
+  memset (to, 0, sizeof (*to));
+
+  to->si_signo = from->si_signo;
+  to->si_errno = from->si_errno;
+  to->si_code = from->si_code;
+
+  if (to->si_code < 0)
+    {
+      to->si_ptr = (void *) (intptr_t) from->cpt_si_ptr;
+    }
+  else if (to->si_code == SI_USER)
+    {
+      to->si_pid = from->cpt_si_pid;
+      to->si_uid = from->cpt_si_uid;
+    }
+  else if (to->si_code == SI_TIMER)
+    {
+      to->si_timerid = from->cpt_si_timerid;
+      to->si_overrun = from->cpt_si_overrun;
+      to->si_ptr = (void *) (intptr_t) from->cpt_si_ptr;
+    }
+  else
+    {
+      switch (to->si_signo)
+       {
+       case SIGCHLD:
+         to->si_pid = from->cpt_si_pid;
+         to->si_uid = from->cpt_si_uid;
+         to->si_status = from->cpt_si_status;
+         to->si_utime = from->cpt_si_utime;
+         to->si_stime = from->cpt_si_stime;
+         break;
+       case SIGILL:
+       case SIGFPE:
+       case SIGSEGV:
+       case SIGBUS:
+         to->si_addr = (void *) (intptr_t) from->cpt_si_addr;
+         break;
+       case SIGPOLL:
+         to->si_band = from->cpt_si_band;
+         to->si_fd = from->cpt_si_fd;
+         break;
+       default:
+         to->si_pid = from->cpt_si_pid;
+         to->si_uid = from->cpt_si_uid;
+         to->si_ptr = (void* ) (intptr_t) from->cpt_si_ptr;
+         break;
+       }
+    }
+}
+
+/* Convert a native/host siginfo object, into/from the siginfo in the
+   layout of the inferiors' architecture.  Returns true if any
+   conversion was done; false otherwise.  If DIRECTION is 1, then copy
+   from INF to NATIVE.  If DIRECTION is 0, copy from NATIVE to
+   INF.  */
+
+static int
+amd64_linux_siginfo_fixup (struct siginfo *native, gdb_byte *inf, int direction)
+{
+  /* Is the inferior 32-bit?  If so, then do fixup the siginfo
+     object.  */
+  if (gdbarch_addr_bit (get_frame_arch (get_current_frame ())) == 32)
+    {
+      gdb_assert (sizeof (struct siginfo) == sizeof (compat_siginfo_t));
+
+      if (direction == 0)
+       compat_siginfo_from_siginfo ((struct compat_siginfo *) inf, native);
+      else
+       siginfo_from_compat_siginfo (native, (struct compat_siginfo *) inf);
+
+      return 1;
+    }
+  else
+    return 0;
+}
+
 /* Provide a prototype to silence -Wmissing-prototypes.  */
 void _initialize_amd64_linux_nat (void);
 
@@ -434,4 +667,5 @@ _initialize_amd64_linux_nat (void)
   /* Register the target.  */
   linux_nat_add_target (t);
   linux_nat_set_new_thread (t, amd64_linux_new_thread);
+  linux_nat_set_siginfo_fixup (t, amd64_linux_siginfo_fixup);
 }
index 0b098a07194592255f0f667c46792668e53f3b6a..a54ebb4564e73dadb178dc993655d60eb9ab693c 100644 (file)
@@ -205,6 +205,13 @@ static struct target_ops linux_ops_saved;
 /* The method to call, if any, when a new thread is attached.  */
 static void (*linux_nat_new_thread) (ptid_t);
 
+/* The method to call, if any, when the siginfo object needs to be
+   converted between the layout returned by ptrace, and the layout in
+   the architecture of the inferior.  */
+static int (*linux_nat_siginfo_fixup) (struct siginfo *,
+                                      gdb_byte *,
+                                      int);
+
 /* The saved to_xfer_partial method, inherited from inf-ptrace.c.
    Called by our to_xfer_partial.  */
 static LONGEST (*super_xfer_partial) (struct target_ops *, 
@@ -3223,6 +3230,28 @@ linux_nat_mourn_inferior (struct target_ops *ops)
     linux_fork_mourn_inferior ();
 }
 
+/* Convert a native/host siginfo object, into/from the siginfo in the
+   layout of the inferiors' architecture.  */
+
+static void
+siginfo_fixup (struct siginfo *siginfo, gdb_byte *inf_siginfo, int direction)
+{
+  int done = 0;
+
+  if (linux_nat_siginfo_fixup != NULL)
+    done = linux_nat_siginfo_fixup (siginfo, inf_siginfo, direction);
+
+  /* If there was no callback, or the callback didn't do anything,
+     then just do a straight memcpy.  */
+  if (!done)
+    {
+      if (direction == 1)
+       memcpy (siginfo, inf_siginfo, sizeof (struct siginfo));
+      else
+       memcpy (inf_siginfo, siginfo, sizeof (struct siginfo));
+    }
+}
+
 static LONGEST
 linux_xfer_siginfo (struct target_ops *ops, enum target_object object,
                     const char *annex, gdb_byte *readbuf,
@@ -3232,6 +3261,7 @@ linux_xfer_siginfo (struct target_ops *ops, enum target_object object,
   LONGEST n;
   int pid;
   struct siginfo siginfo;
+  gdb_byte inf_siginfo[sizeof (struct siginfo)];
 
   gdb_assert (object == TARGET_OBJECT_SIGNAL_INFO);
   gdb_assert (readbuf || writebuf);
@@ -3248,14 +3278,26 @@ linux_xfer_siginfo (struct target_ops *ops, enum target_object object,
   if (errno != 0)
     return -1;
 
+  /* When GDB is built as a 64-bit application, ptrace writes into
+     SIGINFO an object with 64-bit layout.  Since debugging a 32-bit
+     inferior with a 64-bit GDB should look the same as debugging it
+     with a 32-bit GDB, we need to convert it.  GDB core always sees
+     the converted layout, so any read/write will have to be done
+     post-conversion.  */
+  siginfo_fixup (&siginfo, inf_siginfo, 0);
+
   if (offset + len > sizeof (siginfo))
     len = sizeof (siginfo) - offset;
 
   if (readbuf != NULL)
-    memcpy (readbuf, (char *)&siginfo + offset, len);
+    memcpy (readbuf, inf_siginfo + offset, len);
   else
     {
-      memcpy ((char *)&siginfo + offset, writebuf, len);
+      memcpy (inf_siginfo + offset, writebuf, len);
+
+      /* Convert back to ptrace layout before flushing it out.  */
+      siginfo_fixup (&siginfo, inf_siginfo, 1);
+
       errno = 0;
       ptrace (PTRACE_SETSIGINFO, pid, (PTRACE_TYPE_ARG3) 0, &siginfo);
       if (errno != 0)
@@ -4720,6 +4762,19 @@ linux_nat_set_new_thread (struct target_ops *t, void (*new_thread) (ptid_t))
   linux_nat_new_thread = new_thread;
 }
 
+/* Register a method that converts a siginfo object between the layout
+   that ptrace returns, and the layout in the architecture of the
+   inferior.  */
+void
+linux_nat_set_siginfo_fixup (struct target_ops *t,
+                            int (*siginfo_fixup) (struct siginfo *,
+                                                  gdb_byte *,
+                                                  int))
+{
+  /* Save the pointer.  */
+  linux_nat_siginfo_fixup = siginfo_fixup;
+}
+
 /* Return the saved siginfo associated with PTID.  */
 struct siginfo *
 linux_nat_get_siginfo (ptid_t ptid)
index 0985e8409f58edd31b10b6b6f64d3630f65a8668..fec51393bd7539268cd639f0f3ec0c3406140472 100644 (file)
@@ -125,6 +125,14 @@ void linux_nat_add_target (struct target_ops *);
 /* Register a method to call whenever a new thread is attached.  */
 void linux_nat_set_new_thread (struct target_ops *, void (*) (ptid_t));
 
+/* Register a method that converts a siginfo object between the layout
+   that ptrace returns, and the layout in the architecture of the
+   inferior.  */
+void linux_nat_set_siginfo_fixup (struct target_ops *,
+                                 int (*) (struct siginfo *,
+                                          gdb_byte *,
+                                          int));
+
 /* Update linux-nat internal state when changing from one fork
    to another.  */
 void linux_nat_switch_fork (ptid_t new_ptid);