Re: [3/2] Inspect extra signal information, handle amd64 bi-arch gdb

Pedro Alves
2009-02-06T23:37:12Z

On Wednesday 04 February 2009 21:17:12, Daniel Jacobowitz wrote:
> Seems fine to me.

Thanks Daniel. Here's the version I just checked in.

--
Pedro Alves

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.

---
gdb/amd64-linux-nat.c | 234 ++++++++++++++++++++++++++++++++++++++++++++++++++
gdb/linux-nat.c | 59 ++++++++++++
gdb/linux-nat.h | 8 +
3 files changed, 299 insertions(+), 2 deletions(-)

Index: src/gdb/amd64-linux-nat.c
===================================================================
--- src.orig/gdb/amd64-linux-nat.c 2009-02-06 23:02:14.000000000 +0000
+++ src/gdb/amd64-linux-nat.c 2009-02-06 23:04:07.000000000 +0000
@@ -400,6 +400,239 @@ amd64_linux_child_post_startup_inferior
}
\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: src/gdb/linux-nat.c
===================================================================
--- src.orig/gdb/linux-nat.c 2009-02-06 23:02:15.000000000 +0000
+++ src/gdb/linux-nat.c 2009-02-06 23:04:07.000000000 +0000
@@ -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_
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 *o
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 *o
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_
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: src/gdb/linux-nat.h
===================================================================
--- src.orig/gdb/linux-nat.h 2009-02-06 23:02:15.000000000 +0000
+++ src/gdb/linux-nat.h 2009-02-06 23:04:07.000000000 +0000
@@ -125,6 +125,14 @@ void linux_nat_add_target (struct target
/* 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);