From 5b009018d2759269b0fd192077a391c795ebfa97 Mon Sep 17 00:00:00 2001 From: Pedro Alves Date: Fri, 6 Feb 2009 23:06:58 +0000 Subject: [PATCH] * 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/ChangeLog | 18 ++++ gdb/amd64-linux-nat.c | 234 ++++++++++++++++++++++++++++++++++++++++++ gdb/linux-nat.c | 59 ++++++++++- gdb/linux-nat.h | 8 ++ 4 files changed, 317 insertions(+), 2 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 56496fd49d6..8b4bcc00ddb 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,21 @@ +2009-02-06 Pedro Alves + + * 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 * target.h (enum target_object): Add new TARGET_OBJECT_SIGNAL_INFO. diff --git a/gdb/amd64-linux-nat.c b/gdb/amd64-linux-nat.c index e4ae88db096..30f313795ff 100644 --- a/gdb/amd64-linux-nat.c +++ b/gdb/amd64-linux-nat.c @@ -400,6 +400,239 @@ amd64_linux_child_post_startup_inferior (ptid_t ptid) } +/* 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); } diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c index 0b098a07194..a54ebb4564e 100644 --- a/gdb/linux-nat.c +++ b/gdb/linux-nat.c @@ -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) diff --git a/gdb/linux-nat.h b/gdb/linux-nat.h index 0985e8409f5..fec51393bd7 100644 --- a/gdb/linux-nat.h +++ b/gdb/linux-nat.h @@ -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); -- 2.30.2