From: John Baldwin Date: Wed, 28 Jun 2017 16:53:06 +0000 (-0700) Subject: Fetch signal information for native FreeBSD processes. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=929edea98d27cf9d72305c1584ee77627da7fa96;p=binutils-gdb.git 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). --- diff --git a/gdb/ChangeLog b/gdb/ChangeLog index d5afeade3a3..1809ebaf774 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,11 @@ +2017-07-07 John Baldwin + + * 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 * fbsd-tdep.c (fbsd_gdbarch_data_handle, struct fbsd_gdbarch_data) diff --git a/gdb/fbsd-nat.c b/gdb/fbsd-nat.c index ef5ad1ec92b..85f56059bdf 100644 --- a/gdb/fbsd-nat.c +++ b/gdb/fbsd-nat.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #ifdef HAVE_KINFO_GETVMMAP #include @@ -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 + . 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);