diff options
author | John Baldwin <jhb@FreeBSD.org> | 2017-06-28 09:53:06 -0700 |
---|---|---|
committer | John Baldwin <jhb@FreeBSD.org> | 2017-07-07 16:05:47 -0700 |
commit | 929edea98d27cf9d72305c1584ee77627da7fa96 (patch) | |
tree | 011d2a1dc33fc3cf74e3ce9df94e857c4fa60c31 | |
parent | 762c974a09746bda8a5d64ed3ee887adeae742b9 (diff) | |
download | gdb-929edea98d27cf9d72305c1584ee77627da7fa96.zip gdb-929edea98d27cf9d72305c1584ee77627da7fa96.tar.gz gdb-929edea98d27cf9d72305c1584ee77627da7fa96.tar.bz2 |
Fetch signal information for native FreeBSD processes.
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).
-rw-r--r-- | gdb/ChangeLog | 8 | ||||
-rw-r--r-- | gdb/fbsd-nat.c | 162 |
2 files changed, 170 insertions, 0 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index d5afead..1809eba 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,5 +1,13 @@ 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) (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 ef5ad1e..85f5605 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,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); |