This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
[rfc 7/9]#2 Associate siginfo_t with any signal
- From: Jan Kratochvil <jan dot kratochvil at redhat dot com>
- To: gdb-patches at sourceware dot org
- Date: Mon, 30 Aug 2010 09:13:58 +0200
- Subject: [rfc 7/9]#2 Associate siginfo_t with any signal
series #2 of: http://sourceware.org/ml/gdb-patches/2010-07/msg00409.html
Hi,
this patch brings the primary fix of this patchset. siginfo_t gets associated
with every signal (both target_signal_t and int host_signal).
It is RFC only as I expect there some changes of code for gdb/remote.c .
Also some code may need to be moved to gdb/common/ to be reused from
gdb/gdbserver/ (but which I have not planned to implement so far).
Thanks,
Jan
include/gdb/
2010-08-30 Jan Kratochvil <jan.kratochvil@redhat.com>
* signals.h (target_signal_t): Remove the comment about limitations.
New fields siginfo_gdbarch and siginfo.
(TARGET_SIGNAL_STRUCT): New define.
(TARGET_SIGNAL_INITIALIZER, target_signal_from_number): Adjust it.
gdb/
2010-08-30 Jan Kratochvil <jan.kratochvil@redhat.com>
* configure.ac (HAVE_SIGINFO_T, gdb_cv_sizeof_siginfo_t)
(MAX_SIGINFO_SIZE, HAVE_RT_TGSIGQUEUEINFO_SYSCALL): New checks.
* configure.tgt (gdb_target_siginfo_size, gdb_all_max_siginfo_size):
New constants.
(arm*-*-linux*, i[34567]86-*-linux*, x86_64-*-linux*): Set
gdb_target_siginfo_size.
($gdb_target_siginfo_size -gt $gdb_all_max_siginfo_size): New test.
* config.in: Regenerated.
* configure: Regenerated.
* amd64-linux-nat.c (amd64_linux_siginfo_fixup): New parameter
gdbarch. Follow it. New variable arch. New gdb_asserts on known
gdbarch.
* i386-linux-nat.c: Include inf-ptrace.h.
(PTRACE_SETSIGINFO)
(i386_linux_resume) <target_signal_siginfo_p (&signal)>: New.
* inf-ptrace.c (PTRACE_GETSIGINFO, PTRACE_SETSIGINFO): New.
(inf_ptrace_resume) <target_signal_siginfo_p (&signal)>
(inf_ptrace_wait) <TARGET_WAITKIND_USES_SIG (ourstatus->kind)>: New.
(inf_ptrace_siginfo_fixup): Conditionalize it by HAVE_SIGINFO_T. New
`gdbarch *' parameter.
(inf_ptrace_set_siginfo_fixup): Conditionalize it by HAVE_SIGINFO_T.
New `gdbarch *' parameter of the siginfo_fixup parameter.
(siginfo_fixup): Conditionalize it by HAVE_SIGINFO_T. Extend the
comment. New MAX_SIGINFO_SIZE gdb_assert. New gdbarch parameter,
pass it along.
* inf-ptrace.h (siginfo_fixup, inf_ptrace_set_siginfo_fixup):
Conditionalize the declarations by HAVE_SIGINFO_T.
* linux-nat.c: Include unistd.h and sys/syscall.h also for
HAVE_RT_TGSIGQUEUEINFO_SYSCALL.
(struct simple_pid_list) <siginfo>: New field.
(add_to_pid_list) <siginfop>: New parameter. Store it.
(pull_pid_from_list) <siginfop>: New parameter. Retrieve it.
(linux_record_stopped_pid) <siginfop: New parameter. Pass it.
(my_waitpid) <siginfop>: New parameter. Fill it in. Extend the
function comment.
(linux_test_for_tracefork): Pass NULL to my_waitpid.
(linux_child_follow_fork): Clear lp->siginfo.
(linux_nat_post_attach_wait) <siginfop>: New parameter. Pass it.
(lin_lwp_attach_lwp) <siginfo>: New variable. Use it, store it to
lp->siginfo.
(linux_nat_attach) <siginfo>: New variable. use it, store it to
lp->siginfo.
(get_pending_status): New comment about SIGNO.siginfo.
(resume_callback) <lp->siginfo>: Replace memset by just si_signo = 0.
(linux_nat_resume): Set saved_signo siginfo from lp->siginfo. Clear
lp->siginfo when clearing lp->status. Replace memset by si_signo = 0.
(kill_lwp) <debug_linux_nat>: New message.
(kill_lwp_siginfo): New function.
(linux_handle_extended_wait): New parameter siginfop. New variable
siginfo, initialize it. Pass siginfo around. Clear siginfo when
clearing status. Set signo.siginfo when setting signo.
(wait_lwp): New parameter siginfop. New variable siginfo_local. Pass
siginfop around. Clear siginfop on thread_dead. Clear siginfop on
setting status.
(save_siginfo): Remove the function.
(set_ignore_sigint): Clear lp->siginfo when clearing lp->status.
(stop_wait_callback): New variable siginfo. Use it to set
lp->siginfo. Call kill_lwp_siginfo instead of kill_lwp. Set
lp->siginfo when setting lp->status.
(cancel_breakpoints_callback): Clear lp->siginfo when clearing
lp->status.
(select_event_lwp): New parameter siginfop. Store it into
(*orig_lp)->siginfo and *siginfop.
(linux_nat_filter_event): New parameter siginfop. New variable
siginfo. Pass it along. Clear siginfo when setting status. Replace
called save_siginfo by using siginfo. Store siginfo to lp->siginfo.
(linux_nat_wait_1): Remove unused status initialization. New variable
siginfo. Initialize it. Set siginfo when setting status. Set
lp->siginfo when setting lp->status. Call kill_lwp_siginfo instead of
kill_lwp. Pass siginfo along. Store lp->siginfo into lp->waitstatus,
signo and ourstatus.
(resume_stopped_resumed_lwps): Replace memset by si_signo = 0.
(kill_wait_callback): Pass NULL to siginfop of my_waitpid.
(linux_xfer_siginfo): Use MAX_SIGINFO_SIZE size for inf_siginfo.
Create gdbarch for siginfo_fixup.
(linux_nat_stop_lwp): Set also lwp->siginfo when setting lwp->status.
(linux_nat_get_siginfo): Extend the function comment.
* linux-nat.h (struct lwp_info) <siginfo>: Move it to status.
Describe it more.
(linux_nat_get_siginfo): Extend the function comment.
* rs6000-nat.c (PTRACE_GETSIGINFO): New define.
(rs6000_wait): Set also ourstatus->value.sig.
* spu-linux-nat.c (PTRACE_GETSIGINFO): New define.
(spu_child_wait): New variable siginfo. Set also
ourstatus->value.sig.
* target.c (target_signal_siginfo_p, target_signal_siginfo_len)
(target_signal_siginfo_get, target_signal_siginfo_set): New functions.
* target.h (TARGET_WAITKIND_USES_SIG): New define.
(target_signal_siginfo_p, target_signal_siginfo_len)
(target_signal_siginfo_get, target_signal_siginfo_set): New
declarations.
gdb/testsuite/
2010-08-30 Jan Kratochvil <jan.kratochvil@redhat.com>
* gdb.threads/siginfo-threads.exp: New file.
* gdb.threads/siginfo-threads.c: New file.
--- a/gdb/amd64-linux-nat.c
+++ b/gdb/amd64-linux-nat.c
@@ -685,12 +685,21 @@ siginfo_from_compat_siginfo (siginfo_t *to, compat_siginfo_t *from)
INF. */
static int
-amd64_linux_siginfo_fixup (struct siginfo *native, gdb_byte *inf, int direction)
+amd64_linux_siginfo_fixup (struct siginfo *native, gdb_byte *inf,
+ int direction, struct gdbarch *gdbarch)
{
- /* Is the inferior 32-bit? If so, then do fixup the siginfo
- object. */
- if (gdbarch_addr_bit (get_frame_arch (get_current_frame ())) == 32)
- {
+ const bfd_arch_info_type *arch = gdbarch_bfd_arch_info (gdbarch);
+
+ gdb_assert (gdbarch_osabi (gdbarch) == GDB_OSABI_LINUX);
+ gdb_assert (arch->arch == bfd_arch_i386);
+
+ switch (arch->mach)
+ {
+ /* Is the inferior 32-bit? If so, then do fixup the siginfo
+ object. */
+ case bfd_mach_i386_i386:
+ case bfd_mach_i386_i386_intel_syntax:
+ gdb_assert (gdbarch_addr_bit (gdbarch) == 32);
gdb_assert (sizeof (struct siginfo) == sizeof (compat_siginfo_t));
if (direction == 0)
@@ -699,9 +708,16 @@ amd64_linux_siginfo_fixup (struct siginfo *native, gdb_byte *inf, int direction)
siginfo_from_compat_siginfo (native, (struct compat_siginfo *) inf);
return 1;
- }
- else
- return 0;
+
+ case bfd_mach_x86_64:
+ case bfd_mach_x86_64_intel_syntax:
+ gdb_assert (gdbarch_addr_bit (gdbarch) == 64);
+
+ return 0;
+
+ default:
+ gdb_assert_not_reached ("Unsupported architecture");
+ }
}
/* Get Linux/x86 target description from running target.
--- a/gdb/config.in
+++ b/gdb/config.in
@@ -464,6 +464,9 @@
/* Define to 1 if you have the `resize_term' function. */
#undef HAVE_RESIZE_TERM
+/* Define if you support the rt_tgsigqueueinfo syscall. */
+#undef HAVE_RT_TGSIGQUEUEINFO_SYSCALL
+
/* Define to 1 if you have the `sbrk' function. */
#undef HAVE_SBRK
@@ -488,6 +491,9 @@
/* Define to 1 if you have the `sigaction' function. */
#undef HAVE_SIGACTION
+/* Define to 1 if the system has the type `siginfo_t'. */
+#undef HAVE_SIGINFO_T
+
/* Define to 1 if you have the <signal.h> header file. */
#undef HAVE_SIGNAL_H
@@ -718,6 +724,9 @@
/* Define to a substitute value for mmap()'s MAP_ANONYMOUS flag. */
#undef MAP_ANONYMOUS
+/* Maximum size of siginfo_t across any supported target. */
+#undef MAX_SIGINFO_SIZE
+
/* Define if you want to use new multi-fd /proc interface (replaces
HAVE_MULTIPLE_PROC_FDS as well as other macros). */
#undef NEW_PROC_API
--- a/gdb/configure
+++ b/gdb/configure
@@ -2339,116 +2339,116 @@ rm -f conftest.val
} # ac_fn_c_compute_int
-# ac_fn_c_check_member LINENO AGGR MEMBER VAR INCLUDES
-# ----------------------------------------------------
-# Tries to find if the field MEMBER exists in type AGGR, after including
-# INCLUDES, setting cache variable VAR accordingly.
-ac_fn_c_check_member ()
+# ac_fn_c_check_type LINENO TYPE VAR INCLUDES
+# -------------------------------------------
+# Tests whether TYPE exists after having included INCLUDES, setting cache
+# variable VAR accordingly.
+ac_fn_c_check_type ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5
-$as_echo_n "checking for $2.$3... " >&6; }
-if { as_var=$4; eval "test \"\${$as_var+set}\" = set"; }; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then :
$as_echo_n "(cached) " >&6
else
+ eval "$3=no"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
-$5
+$4
int
main ()
{
-static $2 ac_aggr;
-if (ac_aggr.$3)
-return 0;
+if (sizeof ($2))
+ return 0;
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
- eval "$4=yes"
-else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
-$5
+$4
int
main ()
{
-static $2 ac_aggr;
-if (sizeof ac_aggr.$3)
-return 0;
+if (sizeof (($2)))
+ return 0;
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
- eval "$4=yes"
+
else
- eval "$4=no"
+ eval "$3=yes"
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
-eval ac_res=\$$4
+eval ac_res=\$$3
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
-} # ac_fn_c_check_member
+} # ac_fn_c_check_type
-# ac_fn_c_check_type LINENO TYPE VAR INCLUDES
-# -------------------------------------------
-# Tests whether TYPE exists after having included INCLUDES, setting cache
-# variable VAR accordingly.
-ac_fn_c_check_type ()
+# ac_fn_c_check_member LINENO AGGR MEMBER VAR INCLUDES
+# ----------------------------------------------------
+# Tries to find if the field MEMBER exists in type AGGR, after including
+# INCLUDES, setting cache variable VAR accordingly.
+ac_fn_c_check_member ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
-$as_echo_n "checking for $2... " >&6; }
-if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5
+$as_echo_n "checking for $2.$3... " >&6; }
+if { as_var=$4; eval "test \"\${$as_var+set}\" = set"; }; then :
$as_echo_n "(cached) " >&6
else
- eval "$3=no"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
-$4
+$5
int
main ()
{
-if (sizeof ($2))
- return 0;
+static $2 ac_aggr;
+if (ac_aggr.$3)
+return 0;
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
+ eval "$4=yes"
+else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
-$4
+$5
int
main ()
{
-if (sizeof (($2)))
- return 0;
+static $2 ac_aggr;
+if (sizeof ac_aggr.$3)
+return 0;
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
-
+ eval "$4=yes"
else
- eval "$3=yes"
+ eval "$4=no"
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
-eval ac_res=\$$3
+eval ac_res=\$$4
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
-} # ac_fn_c_check_type
+} # ac_fn_c_check_member
cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
@@ -7919,10 +7919,44 @@ fi
. $srcdir/configure.host
+if test "${gdb_native}" = yes; then
+ ac_fn_c_check_type "$LINENO" "siginfo_t" "ac_cv_type_siginfo_t" "#include <signal.h>
+"
+if test "x$ac_cv_type_siginfo_t" = x""yes; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_SIGINFO_T 1
+_ACEOF
+
+
+fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sizeof (siginfo_t)" >&5
+$as_echo_n "checking for sizeof (siginfo_t)... " >&6; }
+ if test "${gdb_cv_sizeof_siginfo_t+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ if ac_fn_c_compute_int "$LINENO" "sizeof (siginfo_t)" "gdb_cv_sizeof_siginfo_t" "#include <signal.h>"; then :
+
+else
+ gdb_cv_sizeof_siginfo_t=0
+fi
+
+fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $gdb_cv_sizeof_siginfo_t" >&5
+$as_echo "$gdb_cv_sizeof_siginfo_t" >&6; }
+fi
+
# Accumulate some settings from configure.tgt over all enabled targets
TARGET_OBS=
all_targets=
+if test "${gdb_native}" = yes; then
+ MAX_SIGINFO_SIZE=$gdb_cv_sizeof_siginfo_t
+else
+ MAX_SIGINFO_SIZE=0
+fi
for targ_alias in `echo $target_alias $enable_targets | sed 's/,/ /g'`
do
@@ -7953,6 +7987,10 @@ fi
esac
done
+ if test $MAX_SIGINFO_SIZE -lt $gdb_target_siginfo_size; then
+ MAX_SIGINFO_SIZE=$gdb_target_siginfo_size
+ fi
+
# Check whether this target needs 64-bit CORE_ADDR
if test x${want64} = xfalse; then
. ${srcdir}/../bfd/config.bfd
@@ -8025,6 +8063,17 @@ gdb_osabi=
build_gdbserver=
targ=$target; . ${srcdir}/configure.tgt
+if test x${all_targets} = xtrue; then
+ if test $MAX_SIGINFO_SIZE -lt $gdb_all_max_siginfo_size; then
+ MAX_SIGINFO_SIZE=$gdb_all_max_siginfo_size
+ fi
+fi
+
+cat >>confdefs.h <<_ACEOF
+#define MAX_SIGINFO_SIZE $MAX_SIGINFO_SIZE
+_ACEOF
+
+
# Fetch the default architecture and default target vector from BFD.
targ=$target; . $srcdir/../bfd/config.bfd
@@ -14605,6 +14654,41 @@ $as_echo "#define HAVE_TKILL_SYSCALL 1" >>confdefs.h
fi
+if test "x$ac_cv_header_sys_syscall_h" = "xyes"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether <sys/syscall.h> has __NR_rt_tgsigqueueinfo" >&5
+$as_echo_n "checking whether <sys/syscall.h> has __NR_rt_tgsigqueueinfo... " >&6; }
+if test "${gdb_cv_sys_syscall_h_has_rt_tgsigqueueinfo+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/syscall.h>
+int
+main ()
+{
+int i = __NR_rt_tgsigqueueinfo;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ gdb_cv_sys_syscall_h_has_rt_tgsigqueueinfo=yes
+else
+ gdb_cv_sys_syscall_h_has_rt_tgsigqueueinfo=no
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gdb_cv_sys_syscall_h_has_rt_tgsigqueueinfo" >&5
+$as_echo "$gdb_cv_sys_syscall_h_has_rt_tgsigqueueinfo" >&6; }
+fi
+if test "x$gdb_cv_sys_syscall_h_has_rt_tgsigqueueinfo" = "xyes" && test "x$ac_cv_func_syscall" = "xyes"; then
+
+$as_echo "#define HAVE_RT_TGSIGQUEUEINFO_SYSCALL 1" >>confdefs.h
+
+fi
+
ac_fn_c_check_decl "$LINENO" "ADDR_NO_RANDOMIZE" "ac_cv_have_decl_ADDR_NO_RANDOMIZE" "#include <sys/personality.h>
"
if test "x$ac_cv_have_decl_ADDR_NO_RANDOMIZE" = x""yes; then :
--- a/gdb/configure.ac
+++ b/gdb/configure.ac
@@ -141,10 +141,24 @@ fi
. $srcdir/configure.host
+if test "${gdb_native}" = yes; then
+ AC_CHECK_TYPES([siginfo_t],,, [[#include <signal.h>]])
+ AC_MSG_CHECKING([for sizeof (siginfo_t)])
+ AC_CACHE_VAL(gdb_cv_sizeof_siginfo_t,
+ [AC_COMPUTE_INT([gdb_cv_sizeof_siginfo_t], [sizeof (siginfo_t)],
+ [[#include <signal.h>]], [gdb_cv_sizeof_siginfo_t=0])])
+ AC_MSG_RESULT($gdb_cv_sizeof_siginfo_t)
+fi
+
# Accumulate some settings from configure.tgt over all enabled targets
TARGET_OBS=
all_targets=
+if test "${gdb_native}" = yes; then
+ MAX_SIGINFO_SIZE=$gdb_cv_sizeof_siginfo_t
+else
+ MAX_SIGINFO_SIZE=0
+fi
for targ_alias in `echo $target_alias $enable_targets | sed 's/,/ /g'`
do
@@ -174,6 +188,10 @@ do
esac
done
+ if test $MAX_SIGINFO_SIZE -lt $gdb_target_siginfo_size; then
+ MAX_SIGINFO_SIZE=$gdb_target_siginfo_size
+ fi
+
# Check whether this target needs 64-bit CORE_ADDR
if test x${want64} = xfalse; then
. ${srcdir}/../bfd/config.bfd
@@ -213,6 +231,14 @@ gdb_osabi=
build_gdbserver=
targ=$target; . ${srcdir}/configure.tgt
+if test x${all_targets} = xtrue; then
+ if test $MAX_SIGINFO_SIZE -lt $gdb_all_max_siginfo_size; then
+ MAX_SIGINFO_SIZE=$gdb_all_max_siginfo_size
+ fi
+fi
+AC_DEFINE_UNQUOTED(MAX_SIGINFO_SIZE, $MAX_SIGINFO_SIZE,
+ [Maximum size of siginfo_t across any supported target.])
+
# Fetch the default architecture and default target vector from BFD.
targ=$target; . $srcdir/../bfd/config.bfd
@@ -1642,6 +1668,23 @@ if test "x$gdb_cv_sys_syscall_h_has_tkill" = "xyes" && test "x$ac_cv_func_syscal
AC_DEFINE(HAVE_TKILL_SYSCALL, 1, [Define if you support the tkill syscall.])
fi
+dnl See if we have a sys/syscall header file that has __NR_rt_tgsigqueueinfo.
+if test "x$ac_cv_header_sys_syscall_h" = "xyes"; then
+ AC_CACHE_CHECK([whether <sys/syscall.h> has __NR_rt_tgsigqueueinfo],
+ gdb_cv_sys_syscall_h_has_rt_tgsigqueueinfo,
+ AC_TRY_COMPILE(
+ [#include <sys/syscall.h>],
+ [int i = __NR_rt_tgsigqueueinfo;],
+ gdb_cv_sys_syscall_h_has_rt_tgsigqueueinfo=yes,
+ gdb_cv_sys_syscall_h_has_rt_tgsigqueueinfo=no
+ )
+ )
+fi
+dnl See if we can issue rt_tgsigqueueinfo syscall.
+if test "x$gdb_cv_sys_syscall_h_has_rt_tgsigqueueinfo" = "xyes" && test "x$ac_cv_func_syscall" = "xyes"; then
+ AC_DEFINE(HAVE_RT_TGSIGQUEUEINFO_SYSCALL, 1, [Define if you support the rt_tgsigqueueinfo syscall.])
+fi
+
dnl Check if we can disable the virtual address space randomization.
dnl The functionality of setarch -R.
AC_CHECK_DECLS([ADDR_NO_RANDOMIZE],,, [#include <sys/personality.h>])
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -30,6 +30,10 @@ esac
# map target info into gdb names.
+gdb_target_siginfo_size=0
+# Maximum size of $gdb_target_siginfo_size set by any of the targets.
+gdb_all_max_siginfo_size=128
+
case "${targ}" in
alpha*-*-osf*)
@@ -79,6 +83,7 @@ arm*-*-linux*)
# Target: ARM based machine running GNU/Linux
gdb_target_obs="arm-tdep.o arm-linux-tdep.o glibc-tdep.o \
solib.o solib-svr4.o symfile-mem.o corelow.o linux-tdep.o"
+ gdb_target_siginfo_size=128
build_gdbserver=yes
;;
arm*-*-netbsd* | arm*-*-knetbsd*-gnu)
@@ -206,9 +211,11 @@ i[34567]86-*-linux*)
gdb_target_obs="i386-tdep.o i386-linux-tdep.o glibc-tdep.o i387-tdep.o \
solib.o solib-svr4.o symfile-mem.o corelow.o \
linux-tdep.o linux-record.o"
+ gdb_target_siginfo_size=128
if test "x$enable_64_bit_bfd" = "xyes"; then
# Target: GNU/Linux x86-64
gdb_target_obs="amd64-tdep.o amd64-linux-tdep.o ${gdb_target_obs}"
+ # gdb_target_siginfo_size has the same size.
fi
build_gdbserver=yes
;;
@@ -579,6 +586,7 @@ x86_64-*-linux*)
i387-tdep.o i386-linux-tdep.o glibc-tdep.o \
solib.o solib-svr4.o corelow.o symfile-mem.o linux-tdep.o \
linux-record.o"
+ gdb_target_siginfo_size=128
build_gdbserver=yes
;;
x86_64-*-freebsd* | x86_64-*-kfreebsd*-gnu)
@@ -619,6 +627,11 @@ xtensa*)
esac
+if [ $gdb_target_siginfo_size -gt $gdb_all_max_siginfo_size ]; then
+ echo "*** Configuration $targ exceeds gdb_all_max_siginfo_size." >&2
+ exit 1
+fi
+
# map target onto default OS ABI
case "${targ}" in
--- a/gdb/i386-linux-nat.c
+++ b/gdb/i386-linux-nat.c
@@ -26,6 +26,7 @@
#include "regset.h"
#include "target.h"
#include "linux-nat.h"
+#include "inf-ptrace.h"
#include "gdb_assert.h"
#include "gdb_string.h"
@@ -83,6 +84,10 @@
#define PTRACE_SETREGSET 0x4205
#endif
+#ifndef PTRACE_SETSIGINFO
+# define PTRACE_SETSIGINFO 0x4203
+#endif
+
/* Does the current host support PTRACE_GETREGSET? */
static int have_ptrace_getregset = -1;
@@ -891,6 +896,22 @@ i386_linux_resume (struct target_ops *ops,
}
}
+ if (target_signal_siginfo_p (&signal))
+ {
+ struct gdbarch *gdbarch;
+ siginfo_t host_siginfo;
+ /* INF_SIGINFO should be `const *'. */
+ gdb_byte *inf_siginfo;
+
+ gdbarch = target_thread_architecture (ptid);
+ inf_siginfo = target_signal_siginfo_get (&signal, gdbarch);
+ siginfo_fixup (&host_siginfo, inf_siginfo, 1, gdbarch);
+
+ if (ptrace (PTRACE_SETSIGINFO, pid, (PTRACE_TYPE_ARG3) 0, &host_siginfo)
+ != 0)
+ perror_with_name (("ptrace (PTRACE_SETSIGINFO)"));
+ }
+
if (ptrace (request, pid, 0, target_signal_to_host (signal)) == -1)
perror_with_name (("ptrace"));
}
--- a/gdb/inf-ptrace.c
+++ b/gdb/inf-ptrace.c
@@ -39,6 +39,11 @@
+#ifndef PTRACE_GETSIGINFO
+# define PTRACE_GETSIGINFO 0x4202
+# define PTRACE_SETSIGINFO 0x4203
+#endif
+
#ifdef PT_GET_PROCESS_STATE
static int
@@ -375,6 +380,24 @@ inf_ptrace_resume (struct target_ops *ops,
request = PT_STEP;
}
+#ifdef HAVE_SIGINFO_T
+ if (target_signal_siginfo_p (&signal))
+ {
+ struct gdbarch *gdbarch;
+ siginfo_t host_siginfo;
+ /* INF_SIGINFO should be `const *'. */
+ gdb_byte *inf_siginfo;
+
+ gdbarch = target_thread_architecture (ptid);
+ inf_siginfo = target_signal_siginfo_get (&signal, gdbarch);
+ siginfo_fixup (&host_siginfo, inf_siginfo, 1, gdbarch);
+
+ if (ptrace (PTRACE_SETSIGINFO, pid, (PTRACE_TYPE_ARG3) 0, &host_siginfo)
+ != 0)
+ perror_with_name (("ptrace (PTRACE_SETSIGINFO)"));
+ }
+#endif /* HAVE_SIGINFO_T */
+
/* An address of (PTRACE_TYPE_ARG3)1 tells ptrace to continue from
where it was. If GDB wanted it to start some other way, we have
already written a new program counter value to the child. */
@@ -465,6 +488,28 @@ inf_ptrace_wait (struct target_ops *ops,
#endif
store_waitstatus (ourstatus, status);
+
+#ifdef HAVE_SIGINFO_T
+ if (TARGET_WAITKIND_USES_SIG (ourstatus->kind))
+ {
+ struct gdbarch *pid_gdbarch;
+ siginfo_t siginfo;
+
+ /* We could also use waitid. */
+
+ pid_gdbarch = target_thread_architecture (pid_to_ptid (pid));
+ if (ptrace (PTRACE_GETSIGINFO, pid, (PTRACE_TYPE_ARG3) 0, &siginfo)
+ == 0)
+ {
+ gdb_byte inf_siginfo[MAX_SIGINFO_SIZE];
+
+ siginfo_fixup (&siginfo, inf_siginfo, 0, pid_gdbarch);
+ target_signal_siginfo_set (&ourstatus->value.sig, pid_gdbarch,
+ inf_siginfo);
+ }
+ }
+#endif /* HAVE_SIGINFO_T */
+
return pid_to_ptid (pid);
}
@@ -797,10 +842,13 @@ inf_ptrace_trad_target (CORE_ADDR (*register_u_offset)
return t;
}
+#ifdef HAVE_SIGINFO_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 (*inf_ptrace_siginfo_fixup) (struct siginfo *, gdb_byte *, int);
+static int (*inf_ptrace_siginfo_fixup) (struct siginfo *, gdb_byte *, int,
+ struct gdbarch *);
/* Register a method that converts a siginfo object between the layout
that ptrace returns, and the layout in the architecture of the
@@ -808,22 +856,29 @@ static int (*inf_ptrace_siginfo_fixup) (struct siginfo *, gdb_byte *, int);
void
inf_ptrace_set_siginfo_fixup (struct target_ops *t,
int (*siginfo_fixup) (struct siginfo *,
- gdb_byte *, int))
+ gdb_byte *, int,
+ struct gdbarch *))
{
/* Save the pointer. */
inf_ptrace_siginfo_fixup = siginfo_fixup;
}
/* Convert a native/host siginfo object, into/from the siginfo in the
- layout of the inferiors' architecture. */
+ layout of the inferiors' architecture.
+ DIRECTION 1 transfers SIGINFO <- INF_SIGINFO.
+ DIRECTION 0 transfers SIGINFO -> INF_SIGINFO.
+ INF_SIGINFO must have size of at least MAX_SIGINFO_SIZE. */
void
-siginfo_fixup (struct siginfo *siginfo, gdb_byte *inf_siginfo, int direction)
+siginfo_fixup (struct siginfo *siginfo, gdb_byte *inf_siginfo, int direction,
+ struct gdbarch *gdbarch)
{
int done = 0;
+ gdb_assert (MAX_SIGINFO_SIZE >= sizeof (struct siginfo));
+
if (inf_ptrace_siginfo_fixup != NULL)
- done = inf_ptrace_siginfo_fixup (siginfo, inf_siginfo, direction);
+ done = inf_ptrace_siginfo_fixup (siginfo, inf_siginfo, direction, gdbarch);
/* If there was no callback, or the callback didn't do anything,
then just do a straight memcpy. */
@@ -835,3 +890,5 @@ siginfo_fixup (struct siginfo *siginfo, gdb_byte *inf_siginfo, int direction)
memcpy (inf_siginfo, siginfo, sizeof (struct siginfo));
}
}
+
+#endif /* HAVE_SIGINFO_T */
--- a/gdb/inf-ptrace.h
+++ b/gdb/inf-ptrace.h
@@ -34,14 +34,19 @@ extern struct target_ops *
inf_ptrace_trad_target (CORE_ADDR (*register_u_offset)
(struct gdbarch *, int, int));
+#ifdef HAVE_SIGINFO_T
+
struct siginfo;
extern void siginfo_fixup (struct siginfo *siginfo, gdb_byte *inf_siginfo,
- int direction);
+ int direction, struct gdbarch *gdbarch);
extern
void inf_ptrace_set_siginfo_fixup (struct target_ops *t,
int (*siginfo_fixup) (struct siginfo *,
gdb_byte *,
- int));
+ int,
+ struct gdbarch *));
+
+#endif /* HAVE_SIGINFO_T */
#endif
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -24,7 +24,7 @@
#include "gdb_string.h"
#include "gdb_wait.h"
#include "gdb_assert.h"
-#ifdef HAVE_TKILL_SYSCALL
+#if defined HAVE_TKILL_SYSCALL || defined HAVE_RT_TGSIGQUEUEINFO_SYSCALL
#include <unistd.h>
#include <sys/syscall.h>
#endif
@@ -267,6 +267,7 @@ struct simple_pid_list
{
int pid;
int status;
+ siginfo_t siginfo;
struct simple_pid_list *next;
};
struct simple_pid_list *stopped_pids;
@@ -358,18 +359,21 @@ static struct lwp_info *find_lwp_pid (ptid_t ptid);
/* Trivial list manipulation functions to keep track of a list of
new stopped processes. */
static void
-add_to_pid_list (struct simple_pid_list **listp, int pid, int status)
+add_to_pid_list (struct simple_pid_list **listp, int pid, int status,
+ const siginfo_t *siginfop)
{
struct simple_pid_list *new_pid = xmalloc (sizeof (struct simple_pid_list));
new_pid->pid = pid;
new_pid->status = status;
+ new_pid->siginfo = *siginfop;
new_pid->next = *listp;
*listp = new_pid;
}
static int
-pull_pid_from_list (struct simple_pid_list **listp, int pid, int *statusp)
+pull_pid_from_list (struct simple_pid_list **listp, int pid, int *statusp,
+ siginfo_t *siginfop)
{
struct simple_pid_list **p;
@@ -379,6 +383,8 @@ pull_pid_from_list (struct simple_pid_list **listp, int pid, int *statusp)
struct simple_pid_list *next = (*p)->next;
*statusp = (*p)->status;
+ if (siginfop)
+ *siginfop = (*p)->siginfo;
xfree (*p);
*p = next;
return 1;
@@ -387,9 +393,9 @@ pull_pid_from_list (struct simple_pid_list **listp, int pid, int *statusp)
}
static void
-linux_record_stopped_pid (int pid, int status)
+linux_record_stopped_pid (int pid, int status, const siginfo_t *siginfop)
{
- add_to_pid_list (&stopped_pids, pid, status);
+ add_to_pid_list (&stopped_pids, pid, status, siginfop);
}
@@ -404,10 +410,13 @@ linux_tracefork_child (void)
_exit (0);
}
-/* Wrapper function for waitpid which handles EINTR. */
+/* Wrapper function for waitpid which handles EINTR. If SIGINFOP is not NULL
+ it may be filled-in with extended information about the returned signal if
+ it is available. It will be cleared otherwise. If SIGINFOP is not NULL
+ then STATUSP must be also not NULL. */
static int
-my_waitpid (int pid, int *statusp, int flags)
+my_waitpid (int pid, int *statusp, struct siginfo *siginfop, int flags)
{
int ret;
@@ -417,6 +426,18 @@ my_waitpid (int pid, int *statusp, int flags)
}
while (ret == -1 && errno == EINTR);
+ if (siginfop != NULL)
+ {
+ gdb_assert (statusp != NULL);
+
+ /* We could also use waitid. */
+
+ if (! (ret > 0 && (WIFSIGNALED (*statusp) || WIFSTOPPED (*statusp)))
+ || (ptrace (PTRACE_GETSIGINFO, ret, (PTRACE_TYPE_ARG3) 0, siginfop)
+ == -1))
+ siginfop->si_signo = 0;
+ }
+
return ret;
}
@@ -460,7 +481,7 @@ linux_test_for_tracefork (int original_pid)
if (child_pid == 0)
linux_tracefork_child ();
- ret = my_waitpid (child_pid, &status, 0);
+ ret = my_waitpid (child_pid, &status, NULL, 0);
if (ret == -1)
perror_with_name (("waitpid"));
else if (ret != child_pid)
@@ -479,7 +500,7 @@ linux_test_for_tracefork (int original_pid)
return;
}
- ret = my_waitpid (child_pid, &status, 0);
+ ret = my_waitpid (child_pid, &status, NULL, 0);
if (ret != child_pid)
warning (_("linux_test_for_tracefork: failed to wait for killed child"));
else if (!WIFSIGNALED (status))
@@ -499,7 +520,7 @@ linux_test_for_tracefork (int original_pid)
if (ret != 0)
warning (_("linux_test_for_tracefork: failed to resume child"));
- ret = my_waitpid (child_pid, &status, 0);
+ ret = my_waitpid (child_pid, &status, NULL, 0);
if (ret == child_pid && WIFSTOPPED (status)
&& status >> 16 == PTRACE_EVENT_FORK)
@@ -511,11 +532,11 @@ linux_test_for_tracefork (int original_pid)
int second_status;
linux_supports_tracefork_flag = 1;
- my_waitpid (second_pid, &second_status, 0);
+ my_waitpid (second_pid, &second_status, NULL, 0);
ret = ptrace (PTRACE_KILL, second_pid, 0, 0);
if (ret != 0)
warning (_("linux_test_for_tracefork: failed to kill second child"));
- my_waitpid (second_pid, &status, 0);
+ my_waitpid (second_pid, &status, NULL, 0);
}
}
else
@@ -525,7 +546,7 @@ linux_test_for_tracefork (int original_pid)
ret = ptrace (PTRACE_KILL, child_pid, 0, 0);
if (ret != 0)
warning (_("linux_test_for_tracefork: failed to kill child"));
- my_waitpid (child_pid, &status, 0);
+ my_waitpid (child_pid, &status, NULL, 0);
restore_child_signals_mask (&prev_mask);
}
@@ -845,6 +866,7 @@ holding the child stopped. Try \"set detach-on-fork\" or \
will notice a pending event, and bypasses actually
resuming the inferior. */
lp->status = 0;
+ lp->siginfo.si_signo = 0;
lp->waitstatus.kind = TARGET_WAITKIND_VFORK_DONE;
lp->stopped = 0;
lp->resumed = 1;
@@ -1344,7 +1366,7 @@ pid_is_stopped (pid_t pid)
static int
linux_nat_post_attach_wait (ptid_t ptid, int first, int *cloned,
- int *signalled)
+ int *signalled, struct siginfo *siginfop)
{
pid_t new_pid, pid = GET_LWP (ptid);
int status;
@@ -1378,14 +1400,14 @@ linux_nat_post_attach_wait (ptid_t ptid, int first, int *cloned,
/* Make sure the initial process is stopped. The user-level threads
layer might want to poke around in the inferior, and that won't
work if things haven't stabilized yet. */
- new_pid = my_waitpid (pid, &status, 0);
+ new_pid = my_waitpid (pid, &status, siginfop, 0);
if (new_pid == -1 && errno == ECHILD)
{
if (first)
warning (_("%s is a cloned process"), target_pid_to_str (ptid));
/* Try again with __WCLONE to check cloned processes. */
- new_pid = my_waitpid (pid, &status, __WCLONE);
+ new_pid = my_waitpid (pid, &status, siginfop, __WCLONE);
*cloned = 1;
}
@@ -1420,6 +1442,7 @@ lin_lwp_attach_lwp (ptid_t ptid)
{
struct lwp_info *lp;
sigset_t prev_mask;
+ siginfo_t siginfo;
gdb_assert (is_lwp (ptid));
@@ -1455,7 +1478,8 @@ lin_lwp_attach_lwp (ptid_t ptid)
"LLAL: PTRACE_ATTACH %s, 0, 0 (OK)\n",
target_pid_to_str (ptid));
- status = linux_nat_post_attach_wait (ptid, 0, &cloned, &signalled);
+ status = linux_nat_post_attach_wait (ptid, 0, &cloned, &signalled,
+ &siginfo);
if (!WIFSTOPPED (status))
return -1;
@@ -1467,6 +1491,7 @@ lin_lwp_attach_lwp (ptid_t ptid)
{
lp->resumed = 1;
lp->status = status;
+ lp->siginfo = siginfo;
}
target_post_attach (GET_LWP (lp->ptid));
@@ -1544,6 +1569,7 @@ linux_nat_attach (struct target_ops *ops, char *args, int from_tty)
{
struct lwp_info *lp;
int status;
+ siginfo_t siginfo;
ptid_t ptid;
linux_ops->to_attach (ops, args, from_tty);
@@ -1557,7 +1583,7 @@ linux_nat_attach (struct target_ops *ops, char *args, int from_tty)
lp = add_lwp (ptid);
status = linux_nat_post_attach_wait (lp->ptid, 1, &lp->cloned,
- &lp->signalled);
+ &lp->signalled, &siginfo);
if (!WIFSTOPPED (status))
{
if (WIFEXITED (status))
@@ -1601,6 +1627,7 @@ linux_nat_attach (struct target_ops *ops, char *args, int from_tty)
(long) GET_PID (lp->ptid), status_to_str (status));
lp->status = status;
+ lp->siginfo = siginfo;
if (target_can_async_p ())
target_async (inferior_event_handler, 0);
@@ -1636,7 +1663,10 @@ get_pending_status (struct lwp_info *lp, int *status)
if (lp->waitstatus.kind != TARGET_WAITKIND_IGNORE)
signo = TARGET_SIGNAL_0; /* a pending ptrace event, not a real signal. */
else if (lp->status)
- signo = target_signal_from_host (WSTOPSIG (lp->status));
+ {
+ signo = target_signal_from_host (WSTOPSIG (lp->status));
+ /* SIGNO.siginfo is not used below. */
+ }
else if (non_stop && !is_executing (lp->ptid))
{
struct thread_info *tp = find_thread_ptid (lp->ptid);
@@ -1827,7 +1857,7 @@ resume_callback (struct lwp_info *lp, void *data)
target_pid_to_str (lp->ptid));
lp->stopped = 0;
lp->step = 0;
- memset (&lp->siginfo, 0, sizeof (lp->siginfo));
+ lp->siginfo.si_signo = 0;
lp->stopped_by_watchpoint = 0;
}
else if (lp->stopped && debug_linux_nat)
@@ -1906,6 +1936,15 @@ linux_nat_resume (struct target_ops *ops,
inf = find_inferior_pid (ptid_get_pid (lp->ptid));
gdb_assert (inf);
saved_signo = target_signal_from_host (WSTOPSIG (lp->status));
+ if (lp->siginfo.si_signo != 0)
+ {
+ struct gdbarch *gdbarch;
+ gdb_byte inf_siginfo[MAX_SIGINFO_SIZE];
+
+ gdbarch = target_thread_architecture (lp->ptid);
+ siginfo_fixup (&lp->siginfo, inf_siginfo, 0, gdbarch);
+ target_signal_siginfo_set (&saved_signo, gdbarch, inf_siginfo);
+ }
/* Defer to common code if we're gaining control of the
inferior. */
@@ -1924,6 +1963,7 @@ linux_nat_resume (struct target_ops *ops,
gdb_assert (TARGET_SIGNAL_EQ (signo, TARGET_SIGNAL_0));
signo = saved_signo;
lp->status = 0;
+ lp->siginfo.si_signo = 0;
}
}
@@ -1959,7 +1999,7 @@ linux_nat_resume (struct target_ops *ops,
ptid = pid_to_ptid (GET_LWP (lp->ptid));
linux_ops->to_resume (linux_ops, ptid, step, signo);
- memset (&lp->siginfo, 0, sizeof (lp->siginfo));
+ lp->siginfo.si_signo = 0;
lp->stopped_by_watchpoint = 0;
if (debug_linux_nat)
@@ -1995,6 +2035,10 @@ kill_lwp (int lwpid, int signo)
if (errno != ENOSYS)
return ret;
tkill_failed = 1;
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "KL: syscall tkill is not supported, "
+ "giving up\n");
}
}
#endif
@@ -2002,6 +2046,57 @@ kill_lwp (int lwpid, int signo)
return kill (lwpid, signo);
}
+/* Send an extended signal information to an LWP. */
+
+static void
+kill_lwp_siginfo (struct lwp_info *lp, int signo, const siginfo_t *siginfop)
+{
+#ifdef HAVE_RT_TGSIGQUEUEINFO_SYSCALL
+ {
+ static int rt_tgsigqueueinfo_failed;
+
+ if (! rt_tgsigqueueinfo_failed)
+ {
+ int ret;
+
+ errno = 0;
+ ret = syscall (__NR_rt_tgsigqueueinfo, GET_PID (lp->ptid),
+ GET_LWP (lp->ptid), signo, siginfop);
+ if (ret == 0)
+ return;
+ if (errno == ENOSYS)
+ {
+ rt_tgsigqueueinfo_failed = 1;
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "KLS: syscall rt_tgsigqueueinfo is not "
+ "supported, giving up\n");
+ }
+ else
+ {
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "KLS: rt_tgsigqueueinfo failed: %s\n",
+ safe_strerror (errno));
+ }
+
+ /* We may also fail with EPERM when tkill still can be used. It will
+ drop some associated siginfo_t information, though. As this
+ siginfo_t information is commonly ignored by the inferior so we
+ should not stop. */
+ }
+ }
+#else /* ! HAVE_RT_TGSIGQUEUEINFO_SYSCALL */
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "KLS: No rt_tgsigqueueinfo support compiled in\n");
+#endif /* ! HAVE_RT_TGSIGQUEUEINFO_SYSCALL */
+
+ /* SIGINFOP has to be dropped - give a warning? */
+
+ kill_lwp (GET_LWP (lp->ptid), signo);
+}
+
/* Handle a GNU/Linux syscall trap wait response. If we see a syscall
event, check if the core is interested in it: if not, ignore the
event, and keep waiting; otherwise, we need to toggle the LWP's
@@ -2137,11 +2232,17 @@ linux_handle_syscall_trap (struct lwp_info *lp, int stopping)
static int
linux_handle_extended_wait (struct lwp_info *lp, int status,
- int stopping)
+ const siginfo_t *siginfop, int stopping)
{
int pid = GET_LWP (lp->ptid);
struct target_waitstatus *ourstatus = &lp->waitstatus;
int event = status >> 16;
+ siginfo_t siginfo;
+
+ if (siginfop == NULL)
+ siginfo.si_signo = 0;
+ else
+ siginfo = *siginfop;
if (event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_VFORK
|| event == PTRACE_EVENT_CLONE)
@@ -2152,11 +2253,11 @@ linux_handle_extended_wait (struct lwp_info *lp, int status,
ptrace (PTRACE_GETEVENTMSG, pid, 0, &new_pid);
/* If we haven't already seen the new PID stop, wait for it now. */
- if (! pull_pid_from_list (&stopped_pids, new_pid, &status))
+ if (! pull_pid_from_list (&stopped_pids, new_pid, &status, &siginfo))
{
/* The new child has a pending SIGSTOP. We can't affect it until it
hits the SIGSTOP, but we're already attached. */
- ret = my_waitpid (new_pid, &status,
+ ret = my_waitpid (new_pid, &status, &siginfo,
(event == PTRACE_EVENT_CLONE) ? __WCLONE : 0);
if (ret == -1)
perror_with_name (_("waiting for new child"));
@@ -2225,7 +2326,10 @@ linux_handle_extended_wait (struct lwp_info *lp, int status,
new_lp->signalled = 1;
}
else
- status = 0;
+ {
+ status = 0;
+ siginfo.si_signo = 0;
+ }
if (non_stop)
{
@@ -2264,9 +2368,22 @@ linux_handle_extended_wait (struct lwp_info *lp, int status,
new_lp->stopped = 0;
new_lp->resumed = 1;
- signo = (status
- ? target_signal_from_host (WSTOPSIG (status))
- : TARGET_SIGNAL_0);
+ if (status == 0)
+ signo = TARGET_SIGNAL_0;
+ else
+ {
+ signo = target_signal_from_host (WSTOPSIG (status));
+ if (siginfo.si_signo != 0)
+ {
+ struct gdbarch *gdbarch;
+ gdb_byte inf_siginfo[MAX_SIGINFO_SIZE];
+
+ gdbarch = target_thread_architecture (new_lp->ptid);
+ siginfo_fixup (&siginfo, inf_siginfo, 0, gdbarch);
+ target_signal_siginfo_set (&signo, gdbarch,
+ inf_siginfo);
+ }
+ }
linux_ops->to_resume (linux_ops, pid_to_ptid (new_pid),
0, signo);
@@ -2342,22 +2459,28 @@ LHEW: Got PTRACE_EVENT_VFORK_DONE from LWP %ld: resuming\n",
}
/* Wait for LP to stop. Returns the wait status, or 0 if the LWP has
- exited. */
+ exited. If SIGINFOP is not NULL it may be filled-in with extended
+ information about the returned signal if it is available. It will be
+ cleared otherwise. */
static int
-wait_lwp (struct lwp_info *lp)
+wait_lwp (struct lwp_info *lp, struct siginfo *siginfop)
{
pid_t pid;
int status;
int thread_dead = 0;
+ siginfo_t siginfo_local;
gdb_assert (!lp->stopped);
gdb_assert (lp->status == 0);
+
+ if (siginfop == NULL)
+ siginfop = &siginfo_local;
- pid = my_waitpid (GET_LWP (lp->ptid), &status, 0);
+ pid = my_waitpid (GET_LWP (lp->ptid), &status, siginfop, 0);
if (pid == -1 && errno == ECHILD)
{
- pid = my_waitpid (GET_LWP (lp->ptid), &status, __WCLONE);
+ pid = my_waitpid (GET_LWP (lp->ptid), &status, siginfop, __WCLONE);
if (pid == -1 && errno == ECHILD)
{
/* The thread has previously exited. We need to delete it
@@ -2397,6 +2520,7 @@ wait_lwp (struct lwp_info *lp)
if (thread_dead)
{
exit_lwp (lp);
+ siginfop->si_signo = 0;
return 0;
}
@@ -2410,8 +2534,9 @@ wait_lwp (struct lwp_info *lp)
on handling the event like a regular SIGTRAP from here
on. */
status = W_STOPCODE (SIGTRAP);
+ siginfop->si_signo = 0;
if (linux_handle_syscall_trap (lp, 1))
- return wait_lwp (lp);
+ return wait_lwp (lp, siginfop);
}
/* Handle GNU/Linux's extended waitstatus for trace events. */
@@ -2421,29 +2546,13 @@ wait_lwp (struct lwp_info *lp)
fprintf_unfiltered (gdb_stdlog,
"WL: Handling extended status 0x%06x\n",
status);
- if (linux_handle_extended_wait (lp, status, 1))
- return wait_lwp (lp);
+ if (linux_handle_extended_wait (lp, status, siginfop, 1))
+ return wait_lwp (lp, siginfop);
}
return status;
}
-/* Save the most recent siginfo for LP. This is currently only called
- for SIGTRAP; some ports use the si_addr field for
- target_stopped_data_address. In the future, it may also be used to
- restore the siginfo of requeued signals. */
-
-static void
-save_siginfo (struct lwp_info *lp)
-{
- errno = 0;
- ptrace (PTRACE_GETSIGINFO, GET_LWP (lp->ptid),
- (PTRACE_TYPE_ARG3) 0, &lp->siginfo);
-
- if (errno != 0)
- memset (&lp->siginfo, 0, sizeof (lp->siginfo));
-}
-
/* Send a SIGSTOP to LP. */
static int
@@ -2501,7 +2610,10 @@ set_ignore_sigint (struct lwp_info *lp, void *data)
flag to consume the next one. */
if (lp->stopped && lp->status != 0 && WIFSTOPPED (lp->status)
&& WSTOPSIG (lp->status) == SIGINT)
- lp->status = 0;
+ {
+ lp->status = 0;
+ lp->siginfo.si_signo = 0;
+ }
else
lp->ignore_sigint = 1;
@@ -2650,8 +2762,9 @@ stop_wait_callback (struct lwp_info *lp, void *data)
if (!lp->stopped)
{
int status;
+ siginfo_t siginfo;
- status = wait_lwp (lp);
+ status = wait_lwp (lp, &siginfo);
if (status == 0)
return 0;
@@ -2690,9 +2803,7 @@ stop_wait_callback (struct lwp_info *lp, void *data)
user will delete or disable the breakpoint, but the
thread will have already tripped on it. */
- /* Save the trap's siginfo in case we need it later. */
- save_siginfo (lp);
-
+ lp->siginfo = siginfo;
save_sigtrap (lp);
/* Now resume this LWP and get the SIGSTOP event. */
@@ -2723,11 +2834,12 @@ stop_wait_callback (struct lwp_info *lp, void *data)
"SWC: kill %s, %s\n",
target_pid_to_str (lp->ptid),
status_to_str ((int) status));
- kill_lwp (GET_LWP (lp->ptid), WSTOPSIG (lp->status));
+ kill_lwp_siginfo (lp, WSTOPSIG (lp->status), &lp->siginfo);
}
/* Save the sigtrap event. */
lp->status = status;
+ lp->siginfo = siginfo;
return 0;
}
else
@@ -2767,10 +2879,13 @@ stop_wait_callback (struct lwp_info *lp, void *data)
target_pid_to_str (lp->ptid),
status_to_str ((int) status));
}
- kill_lwp (GET_LWP (lp->ptid), WSTOPSIG (status));
+ kill_lwp_siginfo (lp, WSTOPSIG (status), &siginfo);
}
else
- lp->status = status;
+ {
+ lp->status = status;
+ lp->siginfo = siginfo;
+ }
return 0;
}
}
@@ -2919,8 +3034,11 @@ cancel_breakpoints_callback (struct lwp_info *lp, void *data)
if (linux_nat_lp_status_is_event (lp)
&& cancel_breakpoint (lp))
- /* Throw away the SIGTRAP. */
- lp->status = 0;
+ {
+ /* Throw away the SIGTRAP. */
+ lp->status = 0;
+ lp->siginfo.si_signo = 0;
+ }
return 0;
}
@@ -2928,7 +3046,8 @@ cancel_breakpoints_callback (struct lwp_info *lp, void *data)
/* Select one LWP out of those that have events pending. */
static void
-select_event_lwp (ptid_t filter, struct lwp_info **orig_lp, int *status)
+select_event_lwp (ptid_t filter, struct lwp_info **orig_lp, int *status,
+ siginfo_t *siginfop)
{
int num_events = 0;
int random_selector;
@@ -2936,6 +3055,7 @@ select_event_lwp (ptid_t filter, struct lwp_info **orig_lp, int *status)
/* Record the wait status for the original LWP. */
(*orig_lp)->status = *status;
+ (*orig_lp)->siginfo = *siginfop;
/* Give preference to any LWP that is being single-stepped. */
event_lp = iterate_over_lwps (filter,
@@ -2974,10 +3094,12 @@ select_event_lwp (ptid_t filter, struct lwp_info **orig_lp, int *status)
/* Switch the event LWP. */
*orig_lp = event_lp;
*status = event_lp->status;
+ *siginfop = event_lp->siginfo;
}
/* Flush the wait status for the event LWP. */
(*orig_lp)->status = 0;
+ (*orig_lp)->siginfo.si_signo = 0;
}
/* Return non-zero if LP has been resumed. */
@@ -3013,9 +3135,16 @@ stop_and_resume_callback (struct lwp_info *lp, void *data)
/* Check if we should go on and pass this event to common code.
Return the affected lwp if we are, or NULL otherwise. */
static struct lwp_info *
-linux_nat_filter_event (int lwpid, int status, int options)
+linux_nat_filter_event (int lwpid, int status, const siginfo_t *siginfop,
+ int options)
{
struct lwp_info *lp;
+ siginfo_t siginfo;
+
+ if (siginfop)
+ siginfo = *siginfop;
+ else
+ siginfo.si_signo = 0;
lp = find_lwp_pid (pid_to_ptid (lwpid));
@@ -3029,7 +3158,7 @@ linux_nat_filter_event (int lwpid, int status, int options)
from waitpid before or after the event is. */
if (WIFSTOPPED (status) && !lp)
{
- linux_record_stopped_pid (lwpid, status);
+ linux_record_stopped_pid (lwpid, status, &siginfo);
return NULL;
}
@@ -3075,6 +3204,7 @@ linux_nat_filter_event (int lwpid, int status, int options)
on handling the event like a regular SIGTRAP from here
on. */
status = W_STOPCODE (SIGTRAP);
+ siginfo.si_signo = 0;
if (linux_handle_syscall_trap (lp, 0))
return NULL;
}
@@ -3086,15 +3216,13 @@ linux_nat_filter_event (int lwpid, int status, int options)
fprintf_unfiltered (gdb_stdlog,
"LLW: Handling extended status 0x%06x\n",
status);
- if (linux_handle_extended_wait (lp, status, 0))
+ if (linux_handle_extended_wait (lp, status, &siginfo, 0))
return NULL;
}
if (linux_nat_status_is_event (status))
{
- /* Save the trap's siginfo in case we need it later. */
- save_siginfo (lp);
-
+ lp->siginfo = siginfo;
save_sigtrap (lp);
}
@@ -3220,6 +3348,7 @@ linux_nat_filter_event (int lwpid, int status, int options)
/* An interesting event. */
gdb_assert (lp);
lp->status = status;
+ lp->siginfo = siginfo;
return lp;
}
@@ -3231,7 +3360,8 @@ linux_nat_wait_1 (struct target_ops *ops,
static sigset_t prev_mask;
struct lwp_info *lp = NULL;
int options = 0;
- int status = 0;
+ int status;
+ siginfo_t siginfo;
pid_t pid;
if (debug_linux_nat_async)
@@ -3268,6 +3398,7 @@ linux_nat_wait_1 (struct target_ops *ops,
retry:
lp = NULL;
status = 0;
+ siginfo.si_signo = 0;
/* Make sure that of those LWPs we want to get an event from, there
is at least one LWP that has been resumed. If there's none, just
@@ -3360,7 +3491,9 @@ retry:
/* Catch the pending SIGSTOP. */
status = lp->status;
+ siginfo = lp->siginfo;
lp->status = 0;
+ lp->siginfo.si_signo = 0;
stop_wait_callback (lp, NULL);
@@ -3374,10 +3507,11 @@ retry:
"LLW: kill %s, %s\n",
target_pid_to_str (lp->ptid),
status_to_str (lp->status));
- kill_lwp (GET_LWP (lp->ptid), WSTOPSIG (lp->status));
+ kill_lwp_siginfo (lp, WSTOPSIG (lp->status), &lp->siginfo);
}
lp->status = status;
+ lp->siginfo = siginfo;
}
if (!target_can_async_p ())
@@ -3394,7 +3528,7 @@ retry:
{
pid_t lwpid;
- lwpid = my_waitpid (pid, &status, options);
+ lwpid = my_waitpid (pid, &status, &siginfo, options);
if (lwpid > 0)
{
@@ -3407,7 +3541,7 @@ retry:
(long) lwpid, status_to_str (status));
}
- lp = linux_nat_filter_event (lwpid, status, options);
+ lp = linux_nat_filter_event (lwpid, status, &siginfo, options);
/* STATUS is now no longer valid, use LP->STATUS instead. */
status = 0;
@@ -3441,6 +3575,7 @@ retry:
{
/* Throw away the SIGTRAP. */
lp->status = 0;
+ lp->siginfo.si_signo = 0;
if (debug_linux_nat)
fprintf (stderr,
@@ -3482,6 +3617,18 @@ retry:
/* Store the pending event in the waitstatus as
well, because W_EXITCODE(0,0) == 0. */
store_waitstatus (&lp->waitstatus, lp->status);
+ if (lp->siginfo.si_signo != 0
+ && TARGET_WAITKIND_USES_SIG (lp->waitstatus.kind))
+ {
+ struct gdbarch *gdbarch;
+ gdb_byte inf_siginfo[MAX_SIGINFO_SIZE];
+
+ gdbarch = target_thread_architecture (lp->ptid);
+
+ siginfo_fixup (&lp->siginfo, inf_siginfo, 0, gdbarch);
+ target_signal_siginfo_set (&lp->waitstatus.value.sig,
+ gdbarch, inf_siginfo);
+ }
}
/* Keep looking. */
@@ -3549,7 +3696,9 @@ retry:
gdb_assert (lp);
status = lp->status;
+ siginfo = lp->siginfo;
lp->status = 0;
+ lp->siginfo.si_signo = 0;
/* Don't report signals that GDB isn't interested in, such as
signals that are neither printed nor stopped upon. Stopping all
@@ -3560,9 +3709,20 @@ retry:
if (WIFSTOPPED (status))
{
- target_signal_t signo = target_signal_from_host (WSTOPSIG (status));
+ target_signal_t signo;
struct inferior *inf;
+ signo = target_signal_from_host (WSTOPSIG (status));
+ if (siginfo.si_signo != 0)
+ {
+ struct gdbarch *gdbarch;
+ gdb_byte inf_siginfo[MAX_SIGINFO_SIZE];
+
+ gdbarch = target_thread_architecture (lp->ptid);
+ siginfo_fixup (&siginfo, inf_siginfo, 0, gdbarch);
+ target_signal_siginfo_set (&signo, gdbarch, inf_siginfo);
+ }
+
inf = find_inferior_pid (ptid_get_pid (lp->ptid));
gdb_assert (inf);
@@ -3638,7 +3798,7 @@ retry:
to all LWPs that have had events helps prevent
starvation. */
if (pid == -1)
- select_event_lwp (ptid, &lp, &status);
+ select_event_lwp (ptid, &lp, &status, &siginfo);
/* Now that we've selected our final event LWP, cancel any
breakpoints in other LWPs that have hit a GDB breakpoint.
@@ -3667,7 +3827,19 @@ retry:
lp->waitstatus.kind = TARGET_WAITKIND_IGNORE;
}
else
- store_waitstatus (ourstatus, status);
+ {
+ store_waitstatus (ourstatus, status);
+ if (siginfo.si_signo != 0 && TARGET_WAITKIND_USES_SIG (ourstatus->kind))
+ {
+ struct gdbarch *gdbarch;
+ gdb_byte inf_siginfo[MAX_SIGINFO_SIZE];
+
+ gdbarch = target_thread_architecture (lp->ptid);
+ siginfo_fixup (&siginfo, inf_siginfo, 0, gdbarch);
+ target_signal_siginfo_set (&ourstatus->value.sig, gdbarch,
+ inf_siginfo);
+ }
+ }
if (debug_linux_nat_async)
fprintf_unfiltered (gdb_stdlog, "LLW: exit\n");
@@ -3717,7 +3889,7 @@ resume_stopped_resumed_lwps (struct lwp_info *lp, void *data)
linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)),
lp->step, TARGET_SIGNAL_0);
lp->stopped = 0;
- memset (&lp->siginfo, 0, sizeof (lp->siginfo));
+ lp->siginfo.si_signo = 0;
lp->stopped_by_watchpoint = 0;
}
@@ -3795,7 +3967,7 @@ kill_wait_callback (struct lwp_info *lp, void *data)
{
do
{
- pid = my_waitpid (GET_LWP (lp->ptid), NULL, __WCLONE);
+ pid = my_waitpid (GET_LWP (lp->ptid), NULL, NULL, __WCLONE);
if (pid != (pid_t) -1)
{
if (debug_linux_nat)
@@ -3817,7 +3989,7 @@ kill_wait_callback (struct lwp_info *lp, void *data)
do
{
- pid = my_waitpid (GET_LWP (lp->ptid), NULL, 0);
+ pid = my_waitpid (GET_LWP (lp->ptid), NULL, NULL, 0);
if (pid != (pid_t) -1)
{
if (debug_linux_nat)
@@ -3899,7 +4071,7 @@ linux_xfer_siginfo (struct target_ops *ops, enum target_object object,
{
int pid;
struct siginfo siginfo;
- gdb_byte inf_siginfo[sizeof (struct siginfo)];
+ gdb_byte inf_siginfo[MAX_SIGINFO_SIZE];
gdb_assert (object == TARGET_OBJECT_SIGNAL_INFO);
gdb_assert (readbuf || writebuf);
@@ -3922,7 +4094,8 @@ linux_xfer_siginfo (struct target_ops *ops, enum target_object object,
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);
+ siginfo_fixup (&siginfo, inf_siginfo, 0,
+ get_frame_arch (get_current_frame ()));
if (offset + len > sizeof (siginfo))
len = sizeof (siginfo) - offset;
@@ -3934,7 +4107,8 @@ linux_xfer_siginfo (struct target_ops *ops, enum target_object object,
memcpy (inf_siginfo + offset, writebuf, len);
/* Convert back to ptrace layout before flushing it out. */
- siginfo_fixup (&siginfo, inf_siginfo, 1);
+ siginfo_fixup (&siginfo, inf_siginfo, 1,
+ get_frame_arch (get_current_frame ()));
errno = 0;
ptrace (PTRACE_SETSIGINFO, pid, (PTRACE_TYPE_ARG3) 0, &siginfo);
@@ -5450,7 +5624,10 @@ linux_nat_stop_lwp (struct lwp_info *lwp, void *data)
event-loop will end up calling target_wait which will collect
these. */
if (lwp->status == 0)
- lwp->status = W_STOPCODE (0);
+ {
+ lwp->status = W_STOPCODE (0);
+ lwp->siginfo.si_signo = 0;
+ }
async_file_mark ();
}
else
@@ -5666,8 +5843,13 @@ linux_nat_set_new_thread (struct target_ops *t, void (*new_thread) (ptid_t))
linux_nat_new_thread = new_thread;
}
+/* Return the saved siginfo associated with PTID. Returned data are in the
+ host native format (which may be different from the target thread format
+ during biarch operation).
+
+ This siginfo is for the currently detected stop, before it gets presented
+ to the user. Signal gets moved into thread_info->stop_signal later. */
-/* Return the saved siginfo associated with PTID. */
struct siginfo *
linux_nat_get_siginfo (ptid_t ptid)
{
--- a/gdb/linux-nat.h
+++ b/gdb/linux-nat.h
@@ -52,16 +52,16 @@ struct lwp_info
didn't try to let the LWP run. */
int resumed;
- /* If non-zero, a pending wait status. */
+ /* If non-zero, a pending wait status. SIGINFO contains extended
+ information for WIFSTOPPED and WIFSIGNALLED cases of STATUS. SIGINFO
+ data have the host native format (which may be different from the target
+ thread format during biarch operation). */
int status;
+ struct siginfo siginfo;
/* Non-zero if we were stepping this LWP. */
int step;
- /* Non-zero si_signo if this LWP stopped with a trap. si_addr may
- be the address of a hardware watchpoint. */
- struct siginfo siginfo;
-
/* STOPPED_BY_WATCHPOINT is non-zero if this LWP stopped with a data
watchpoint trap. */
int stopped_by_watchpoint;
@@ -159,7 +159,12 @@ void linux_nat_set_new_thread (struct target_ops *, void (*) (ptid_t));
to another. */
void linux_nat_switch_fork (ptid_t new_ptid);
-/* Return the saved siginfo associated with PTID. */
+/* Return the saved siginfo associated with PTID. Returned data are in the
+ host native format (which may be different from the target thread format
+ during biarch operation).
+
+ This siginfo is for the currently detected stop, before it gets presented
+ to the user. Signal gets moved into thread_info->stop_signal later. */
struct siginfo *linux_nat_get_siginfo (ptid_t ptid);
/* Compute and return the processor core of a given thread. */
--- a/gdb/rs6000-nat.c
+++ b/gdb/rs6000-nat.c
@@ -118,6 +118,10 @@ typedef union {
#define LDI_FD(ldi, arch64) LDI_FIELD(ldi, arch64, fd)
#define LDI_FILENAME(ldi, arch64) LDI_FIELD(ldi, arch64, filename)
+#ifndef PTRACE_GETSIGINFO
+# define PTRACE_GETSIGINFO 0x4202
+#endif
+
extern struct vmap *map_vmap (bfd * bf, bfd * arch);
static void vmap_exec (void);
@@ -567,7 +571,25 @@ rs6000_wait (struct target_ops *ops,
ourstatus->kind = TARGET_WAITKIND_SPURIOUS;
/* A normal waitstatus. Let the usual macros deal with it. */
else
- store_waitstatus (ourstatus, status);
+ {
+ siginfo_t siginfo;
+
+ store_waitstatus (ourstatus, status);
+
+ /* We could also use waitid. */
+ if (TARGET_WAITKIND_USES_SIG (ourstatus->kind)
+ && (ptrace (PTRACE_GETSIGINFO, pid, (PTRACE_TYPE_ARG3) 0, &siginfo)
+ == 0))
+ {
+ struct gdbarch *gdbarch;
+ gdb_byte inf_siginfo[MAX_SIGINFO_SIZE];
+
+ gdbarch = target_thread_architecture (pid_to_ptid (pid));
+ siginfo_fixup (&siginfo, inf_siginfo, 0, gdbarch);
+ target_signal_siginfo_set (&ourstatus->value.sig, gdbarch,
+ inf_siginfo);
+ }
+ }
return pid_to_ptid (pid);
}
--- a/gdb/spu-linux-nat.c
+++ b/gdb/spu-linux-nat.c
@@ -40,6 +40,9 @@
#define INSTR_SC 0x44000002
#define NR_spu_run 0x0116
+#ifndef PTRACE_GETSIGINFO
+# define PTRACE_GETSIGINFO 0x4202
+#endif
/* Fetch PPU register REGNO. */
static ULONGEST
@@ -429,6 +432,7 @@ spu_child_wait (struct target_ops *ops,
int save_errno;
int status;
pid_t pid;
+ siginfo_t siginfo;
do
{
@@ -466,6 +470,19 @@ spu_child_wait (struct target_ops *ops,
}
store_waitstatus (ourstatus, status);
+
+ /* We could also use waitid. */
+ if (TARGET_WAITKIND_USES_SIG (ourstatus->kind)
+ && ptrace (PTRACE_GETSIGINFO, ret, (PTRACE_TYPE_ARG3) 0, &siginfo) == 0)
+ {
+ struct gdbarch *gdbarch;
+ gdb_byte inf_siginfo[MAX_SIGINFO_SIZE];
+
+ gdbarch = target_thread_architecture (pid_to_ptid (pid));
+ siginfo_fixup (&siginfo, inf_siginfo, 0, gdbarch);
+ target_signal_siginfo_set (&ourstatus->value.sig, gdbarch, inf_siginfo);
+ }
+
return pid_to_ptid (pid);
}
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -2984,7 +2984,96 @@ store_waitstatus (struct target_waitstatus *ourstatus, int hoststatus)
ourstatus->value.sig = target_signal_from_host (WSTOPSIG (hoststatus));
}
}
-
+
+int
+target_signal_siginfo_p (const target_signal_t *sigp)
+{
+#if MAX_SIGINFO_SIZE == 0
+ return 0;
+#else /* MAX_SIGINFO_SIZE > 0 */
+ return sigp->siginfo_gdbarch != NULL;
+#endif /* MAX_SIGINFO_SIZE > 0 */
+}
+
+size_t
+target_signal_siginfo_len (struct gdbarch *siginfo_gdbarch)
+{
+#if MAX_SIGINFO_SIZE == 0
+ gdb_assert_not_reached ("no siginfo target is configured");
+#else /* MAX_SIGINFO_SIZE > 0 */
+
+ gdb_assert (siginfo_gdbarch != NULL);
+
+ if (gdbarch_get_siginfo_type_p (siginfo_gdbarch))
+ {
+ struct type *type;
+ size_t len;
+
+ type = gdbarch_get_siginfo_type (siginfo_gdbarch);
+ len = TYPE_LENGTH (type);
+
+ return len;
+ }
+
+#ifdef HAVE_SIGINFO_T
+
+ /* This case is used only for native targets with their tdep missing
+ gdbarch_get_siginfo_type. */
+ return sizeof (siginfo_t);
+
+#else /* ! HAVE_SIGINFO_T */
+
+ /* remote.c will need to provide the length as received from the remote even
+ if the tdep is missing gdbarch_get_siginfo_type. */
+ internal_error (__FILE__, __LINE__,
+ _("arch %s is missing gdbarch_get_siginfo_type"),
+ gdbarch_bfd_arch_info (siginfo_gdbarch)->printable_name);
+
+#endif /* ! HAVE_SIGINFO_T */
+
+#endif /* MAX_SIGINFO_SIZE > 0 */
+}
+
+void *
+target_signal_siginfo_get (target_signal_t *sigp,
+ struct gdbarch *siginfo_gdbarch)
+{
+#if MAX_SIGINFO_SIZE == 0
+ gdb_assert_not_reached ("no siginfo target is configured");
+#else /* MAX_SIGINFO_SIZE > 0 */
+ gdb_assert (sigp->siginfo_gdbarch != NULL);
+ gdb_assert (siginfo_gdbarch != NULL);
+ gdb_assert (siginfo_gdbarch == sigp->siginfo_gdbarch);
+
+ return sigp->siginfo;
+#endif /* MAX_SIGINFO_SIZE > 0 */
+}
+
+void
+target_signal_siginfo_set (target_signal_t *sig,
+ struct gdbarch *siginfo_gdbarch,
+ const void *siginfo)
+{
+#if MAX_SIGINFO_SIZE > 0
+ size_t len;
+
+ gdb_assert (siginfo != NULL);
+ gdb_assert (sig->siginfo_gdbarch == NULL);
+
+ len = target_signal_siginfo_len (siginfo_gdbarch);
+
+ if (MAX_SIGINFO_SIZE < len)
+ internal_error (__FILE__, __LINE__,
+ _("MAX_SIGINFO_SIZE (%lu) is smaller than "
+ "siginfo_t size (%lu) of arch %s"),
+ (unsigned long) MAX_SIGINFO_SIZE, (unsigned long) len,
+ gdbarch_bfd_arch_info (siginfo_gdbarch)->printable_name);
+
+ sig->siginfo_gdbarch = siginfo_gdbarch;
+ memcpy (sig->siginfo, siginfo, len);
+#endif /* MAX_SIGINFO_SIZE > 0 */
+}
+
/* Convert a normal process ID to a string. Returns the string in a
static buffer. */
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -170,6 +170,10 @@ struct target_waitstatus
value;
};
+/* Is target_waitstatus.value.sig used for this target_waitstatus.KIND? */
+#define TARGET_WAITKIND_USES_SIG(kind) ((kind) == TARGET_WAITKIND_SIGNALLED \
+ || (kind) == TARGET_WAITKIND_STOPPED)
+
/* Options that can be passed to target_wait. */
/* Return immediately if there's no event already queued. If this
@@ -1588,6 +1592,14 @@ extern int remote_timeout;
/* This is for native targets which use a unix/POSIX-style waitstatus. */
extern void store_waitstatus (struct target_waitstatus *, int);
+extern int target_signal_siginfo_p (const target_signal_t *sigp);
+extern size_t target_signal_siginfo_len (struct gdbarch *siginfo_gdbarch);
+extern void *target_signal_siginfo_get (target_signal_t *sigp,
+ struct gdbarch *siginfo_gdbarch);
+extern void target_signal_siginfo_set (target_signal_t *sigp,
+ struct gdbarch *siginfo_gdbarch,
+ const void *siginfo);
+
/* These are in common/signals.c, but they're only used by gdb. */
extern target_signal_t default_target_signal_from_host (struct gdbarch *,
int);
--- /dev/null
+++ b/gdb/testsuite/gdb.threads/siginfo-threads.c
@@ -0,0 +1,447 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2010 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#define _GNU_SOURCE
+#include <pthread.h>
+#include <stdio.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <unistd.h>
+#include <asm/unistd.h>
+
+#define gettid() syscall (__NR_gettid)
+#define tgkill(tgid, tid, sig) syscall (__NR_tgkill, tgid, tid, sig)
+
+/* Terminate always in the main task, it can lock up with SIGSTOPped GDB
+ otherwise. */
+#define TIMEOUT (gettid () == getpid() ? 10 : 15)
+
+static pid_t thread1_tid;
+static pthread_cond_t thread1_tid_cond = PTHREAD_COND_INITIALIZER;
+static pthread_mutex_t thread1_tid_mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
+static int thread1_sigusr1_hit;
+static int thread1_sigusr2_hit;
+
+static pid_t thread2_tid;
+static pthread_cond_t thread2_tid_cond = PTHREAD_COND_INITIALIZER;
+static pthread_mutex_t thread2_tid_mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
+static int thread2_sigusr1_hit;
+static int thread2_sigusr2_hit;
+
+static pthread_mutex_t terminate_mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
+
+/* Do not use alarm as it would create a ptrace event which would hang up us if
+ we are being traced by GDB which we stopped ourselves. */
+
+static void timed_mutex_lock (pthread_mutex_t *mutex)
+{
+ int i;
+ struct timespec start, now;
+
+ i = clock_gettime (CLOCK_MONOTONIC, &start);
+ assert (i == 0);
+
+ do
+ {
+ i = pthread_mutex_trylock (mutex);
+ if (i == 0)
+ return;
+ assert (i == EBUSY);
+
+ i = clock_gettime (CLOCK_MONOTONIC, &now);
+ assert (i == 0);
+ assert (now.tv_sec >= start.tv_sec);
+ }
+ while (now.tv_sec - start.tv_sec < TIMEOUT);
+
+ fprintf (stderr, "Timed out waiting for internal lock!\n");
+ exit (EXIT_FAILURE);
+}
+
+static void
+handler (int signo, siginfo_t *siginfo, void *exception)
+{
+ int *varp;
+
+ assert (siginfo->si_signo == signo);
+ assert (siginfo->si_code == SI_TKILL);
+ assert (siginfo->si_pid == getpid ());
+
+ if (gettid () == thread1_tid)
+ {
+ if (signo == SIGUSR1)
+ varp = &thread1_sigusr1_hit;
+ else if (signo == SIGUSR2)
+ varp = &thread1_sigusr2_hit;
+ else
+ assert (0);
+ }
+ else if (gettid () == thread2_tid)
+ {
+ if (signo == SIGUSR1)
+ varp = &thread2_sigusr1_hit;
+ else if (signo == SIGUSR2)
+ varp = &thread2_sigusr2_hit;
+ else
+ assert (0);
+ }
+ else
+ assert (0);
+
+ if (*varp)
+ {
+ fprintf (stderr, "Signal %d for TID %lu has been already hit!\n", signo,
+ (unsigned long) gettid ());
+ exit (EXIT_FAILURE);
+ }
+ *varp = 1;
+}
+
+static void *
+thread1_func (void *unused)
+{
+ int i;
+
+ timed_mutex_lock (&thread1_tid_mutex);
+
+ /* THREAD1_TID_MUTEX must be already locked to avoid race. */
+ thread1_tid = gettid ();
+
+ i = pthread_cond_signal (&thread1_tid_cond);
+ assert (i == 0);
+ i = pthread_mutex_unlock (&thread1_tid_mutex);
+ assert (i == 0);
+
+ /* Be sure the "t (tracing stop)" test can proceed for both threads. */
+ timed_mutex_lock (&terminate_mutex);
+ i = pthread_mutex_unlock (&terminate_mutex);
+ assert (i == 0);
+
+ if (! thread1_sigusr1_hit)
+ {
+ fprintf (stderr, "Thread 1 signal SIGUSR1 not hit!\n");
+ exit (EXIT_FAILURE);
+ }
+ if (! thread1_sigusr2_hit)
+ {
+ fprintf (stderr, "Thread 1 signal SIGUSR2 not hit!\n");
+ exit (EXIT_FAILURE);
+ }
+
+ return NULL;
+}
+
+static void *
+thread2_func (void *unused)
+{
+ int i;
+
+ timed_mutex_lock (&thread2_tid_mutex);
+
+ /* THREAD2_TID_MUTEX must be already locked to avoid race. */
+ thread2_tid = gettid ();
+
+ i = pthread_cond_signal (&thread2_tid_cond);
+ assert (i == 0);
+ i = pthread_mutex_unlock (&thread2_tid_mutex);
+ assert (i == 0);
+
+ /* Be sure the "t (tracing stop)" test can proceed for both threads. */
+ timed_mutex_lock (&terminate_mutex);
+ i = pthread_mutex_unlock (&terminate_mutex);
+ assert (i == 0);
+
+ if (! thread2_sigusr1_hit)
+ {
+ fprintf (stderr, "Thread 2 signal SIGUSR1 not hit!\n");
+ exit (EXIT_FAILURE);
+ }
+ if (! thread2_sigusr2_hit)
+ {
+ fprintf (stderr, "Thread 2 signal SIGUSR2 not hit!\n");
+ exit (EXIT_FAILURE);
+ }
+
+ return NULL;
+}
+
+static const char *
+proc_string (const char *filename, const char *line)
+{
+ FILE *f;
+ static char buf[LINE_MAX];
+ size_t line_len = strlen (line);
+
+ f = fopen (filename, "r");
+ if (f == NULL)
+ {
+ fprintf (stderr, "fopen (\"%s\") for \"%s\": %s\n", filename, line,
+ strerror (errno));
+ exit (EXIT_FAILURE);
+ }
+ while (errno = 0, fgets (buf, sizeof (buf), f))
+ {
+ char *s;
+
+ s = strchr (buf, '\n');
+ assert (s != NULL);
+ *s = 0;
+
+ if (strncmp (buf, line, line_len) != 0)
+ continue;
+
+ if (fclose (f))
+ {
+ fprintf (stderr, "fclose (\"%s\") for \"%s\": %s\n", filename, line,
+ strerror (errno));
+ exit (EXIT_FAILURE);
+ }
+
+ return &buf[line_len];
+ }
+ if (errno != 0)
+ {
+ fprintf (stderr, "fgets (\"%s\": %s\n", filename, strerror (errno));
+ exit (EXIT_FAILURE);
+ }
+ fprintf (stderr, "\"%s\": No line \"%s\" found.\n", filename, line);
+ exit (EXIT_FAILURE);
+}
+
+static unsigned long
+proc_ulong (const char *filename, const char *line)
+{
+ const char *s = proc_string (filename, line);
+ long retval;
+ char *end;
+
+ errno = 0;
+ retval = strtol (s, &end, 10);
+ if (retval < 0 || retval >= LONG_MAX || (end && *end))
+ {
+ fprintf (stderr, "\"%s\":\"%s\": %ld, %s\n", filename, line, retval,
+ strerror (errno));
+ exit (EXIT_FAILURE);
+ }
+ return retval;
+}
+
+static void
+state_wait (pid_t process, const char *wanted)
+{
+ char *filename;
+ int i;
+ struct timespec start, now;
+ const char *state;
+
+ i = asprintf (&filename, "/proc/%lu/status", (unsigned long) process);
+ assert (i > 0);
+
+ i = clock_gettime (CLOCK_MONOTONIC, &start);
+ assert (i == 0);
+
+ do
+ {
+ state = proc_string (filename, "State:\t");
+
+ /* torvalds/linux-2.6.git 464763cf1c6df632dccc8f2f4c7e50163154a2c0
+ has changed "T (tracing stop)" to "t (tracing stop)". Make the GDB
+ testcase backward compatible with older Linux kernels. */
+ if (strcmp (state, "T (tracing stop)") == 0)
+ state = "t (tracing stop)";
+
+ if (strcmp (state, wanted) == 0)
+ {
+ free (filename);
+ return;
+ }
+
+ if (sched_yield ())
+ {
+ perror ("sched_yield()");
+ exit (EXIT_FAILURE);
+ }
+
+ i = clock_gettime (CLOCK_MONOTONIC, &now);
+ assert (i == 0);
+ assert (now.tv_sec >= start.tv_sec);
+ }
+ while (now.tv_sec - start.tv_sec < TIMEOUT);
+
+ fprintf (stderr, "Timed out waiting for PID %lu \"%s\" (now it is \"%s\")!\n",
+ (unsigned long) process, wanted, state);
+ exit (EXIT_FAILURE);
+}
+
+static volatile pid_t tracer = 0;
+static pthread_t thread1, thread2;
+
+static void
+cleanup (void)
+{
+ printf ("Resuming GDB PID %lu.\n", (unsigned long) tracer);
+
+ if (tracer)
+ {
+ int i;
+ int tracer_save = tracer;
+
+ tracer = 0;
+
+ i = kill (tracer_save, SIGCONT);
+ assert (i == 0);
+ }
+}
+
+int
+main (int argc, char **argv)
+{
+ int i;
+ int standalone = 0;
+ struct sigaction act;
+
+ if (argc == 2 && strcmp (argv[1], "-s") == 0)
+ standalone = 1;
+ else
+ assert (argc == 1);
+
+ setbuf (stdout, NULL);
+
+ timed_mutex_lock (&thread1_tid_mutex);
+ timed_mutex_lock (&thread2_tid_mutex);
+
+ timed_mutex_lock (&terminate_mutex);
+
+ errno = 0;
+ memset (&act, 0, sizeof (act));
+ act.sa_sigaction = handler;
+ act.sa_flags = SA_RESTART | SA_SIGINFO;
+ i = sigemptyset (&act.sa_mask);
+ assert_perror (errno);
+ assert (i == 0);
+ i = sigaction (SIGUSR1, &act, NULL);
+ assert_perror (errno);
+ assert (i == 0);
+ i = sigaction (SIGUSR2, &act, NULL);
+ assert_perror (errno);
+ assert (i == 0);
+
+ i = pthread_create (&thread1, NULL, thread1_func, NULL);
+ assert (i == 0);
+
+ i = pthread_create (&thread2, NULL, thread2_func, NULL);
+ assert (i == 0);
+
+ if (!standalone)
+ {
+ tracer = proc_ulong ("/proc/self/status", "TracerPid:\t");
+ if (tracer == 0)
+ {
+ fprintf (stderr, "The testcase must be run by GDB!\n");
+ exit (EXIT_FAILURE);
+ }
+ if (tracer != getppid ())
+ {
+ fprintf (stderr, "The testcase parent must be our GDB tracer!\n");
+ exit (EXIT_FAILURE);
+ }
+ }
+
+ /* SIGCONT our debugger in the case of our crash as we would deadlock
+ otherwise. */
+
+ atexit (cleanup);
+
+ printf ("Stopping GDB PID %lu.\n", (unsigned long) tracer);
+
+ if (tracer)
+ {
+ i = kill (tracer, SIGSTOP);
+ assert (i == 0);
+ state_wait (tracer, "T (stopped)");
+ }
+
+ /* Threads are now waiting at timed_mutex_lock (thread1_tid_mutex) and so
+ they could not trigger the signals before GDB gets unstopped later.
+ Threads get resumed at pthread_cond_wait below. Use `while' loops for
+ protection against spurious pthread_cond_wait wakeups. */
+
+ printf ("Waiting till the threads initialize their TIDs.\n");
+
+ while (thread1_tid == 0)
+ {
+ i = pthread_cond_wait (&thread1_tid_cond, &thread1_tid_mutex);
+ assert (i == 0);
+ }
+
+ while (thread2_tid == 0)
+ {
+ i = pthread_cond_wait (&thread2_tid_cond, &thread2_tid_mutex);
+ assert (i == 0);
+ }
+
+ printf ("Thread 1 TID = %lu, thread 2 TID = %lu, PID = %lu.\n",
+ (unsigned long) thread1_tid, (unsigned long) thread2_tid,
+ (unsigned long) getpid ());
+
+ errno = 0;
+ i = tgkill (getpid (), thread1_tid, SIGUSR1);
+ assert_perror (errno);
+ assert (i == 0);
+ i = tgkill (getpid (), thread1_tid, SIGUSR2);
+ assert_perror (errno);
+ assert (i == 0);
+ i = tgkill (getpid (), thread2_tid, SIGUSR1);
+ assert_perror (errno);
+ assert (i == 0);
+ i = tgkill (getpid (), thread2_tid, SIGUSR2);
+ assert_perror (errno);
+ assert (i == 0);
+
+ printf ("Waiting till the threads get trapped by the signals.\n");
+
+ if (tracer)
+ {
+ /* s390x-unknown-linux-gnu will fail with "R (running)". */
+
+ state_wait (thread1_tid, "t (tracing stop)");
+
+ state_wait (thread2_tid, "t (tracing stop)");
+ }
+
+ cleanup ();
+
+ printf ("Joining the threads.\n");
+
+ i = pthread_mutex_unlock (&terminate_mutex);
+ assert (i == 0);
+
+ i = pthread_join (thread1, NULL);
+ assert (i == 0);
+
+ i = pthread_join (thread2, NULL);
+ assert (i == 0);
+
+ printf ("Exiting.\n"); /* break-at-exit */
+
+ return EXIT_SUCCESS;
+}
--- /dev/null
+++ b/gdb/testsuite/gdb.threads/siginfo-threads.exp
@@ -0,0 +1,49 @@
+# Copyright 2010 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+set testfile "siginfo-threads"
+set srcfile ${testfile}.c
+set binfile ${objdir}/${subdir}/${testfile}
+if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" ${binfile} executable [list debug additional_flags=-lrt]] != "" } {
+ return -1
+}
+
+clean_restart $testfile
+
+if ![runto_main] {
+ return -1
+}
+
+# `nostop noprint pass' could in some cases report false PASS due to the
+# (preempt 'handle') code path.
+
+gdb_test "handle SIGUSR1 nostop print pass" "Signal\[ \t\]+Stop\[ \t\]+Print\[ \t\]+Pass to program\[ \t\]+Description\r\nSIGUSR1\[ \t\]+No\[ \t\]+Yes\[ \t\]+Yes\[ \t\]+User defined signal 1"
+gdb_test "handle SIGUSR2 nostop print pass" "Signal\[ \t\]+Stop\[ \t\]+Print\[ \t\]+Pass to program\[ \t\]+Description\r\nSIGUSR2\[ \t\]+No\[ \t\]+Yes\[ \t\]+Yes\[ \t\]+User defined signal 2"
+
+gdb_breakpoint [gdb_get_line_number "break-at-exit"]
+
+# Catch any KLS: .*rt_tgsigqueueinfo messages.
+# Ignore possible: Undefined set debug command: "lin-lwp 1" ...
+gdb_test "set debug lin-lwp 1" ".*"
+
+set test "continue to break-at-exit"
+gdb_test_multiple "continue" $test {
+ -re "KLS: \[^\r\n\]*rt_tgsigqueueinfo" {
+ unsupported "Missing rt_tgsigqueueinfo kernel support"
+ }
+ -re "Breakpoint .*break-at-exit.*\r\n$gdb_prompt $" {
+ pass $test
+ }
+}
--- a/include/gdb/signals.h
+++ b/include/gdb/signals.h
@@ -39,14 +39,7 @@
(1) This set of signals represents a widely-accepted attempt to
represent events of this sort in a portable fashion, (2) we want a
signal to make it from wait to child_wait to the user intact, (3) many
- remote protocols use a similar encoding. However, it is
- recognized that this set of signals has limitations (such as not
- distinguishing between various kinds of SIGSEGV, or not
- distinguishing hitting a breakpoint from finishing a single step).
- So in the future we may get around this either by adding additional
- signals for breakpoint, single-step, etc., or by adding signal
- codes; the latter seems more in the spirit of what BSD, System V,
- etc. are doing to address these issues. */
+ remote protocols use a similar encoding. */
/* For an explanation of what each signal means, see
target_signal_to_string. */
@@ -66,11 +59,35 @@ enum target_signal_number
typedef struct
{
enum target_signal_number number;
+
+#if MAX_SIGINFO_SIZE > 0
+
+ /* Target architecture of the current frame of the associated thread. */
+ struct gdbarch *siginfo_gdbarch;
+
+ /* Valid iff SIGINFO_GDBARCH is not NULL. Content has the target
+ siginfo_t format, even despite the native PTRACE_GETSIGINFO format may
+ be different during biarch operation. */
+ unsigned char siginfo[MAX_SIGINFO_SIZE];
+
+#endif /* MAX_SIGINFO_SIZE > 0 */
}
target_signal_t;
-#define TARGET_SIGNAL_INITIALIZER(target_signal) \
- { TARGET_SIGNAL_NUMBER_CONST (target_signal) }
+#if MAX_SIGINFO_SIZE > 0
+
+# define TARGET_SIGNAL_STRUCT(target_signal_number) \
+ { (target_signal_number), NULL }
+
+#else /* MAX_SIGINFO_SIZE == 0 */
+
+# define TARGET_SIGNAL_STRUCT(target_signal_number) \
+ { (target_signal_number) }
+
+#endif /* MAX_SIGINFO_SIZE == 0 */
+
+# define TARGET_SIGNAL_INITIALIZER(target_signal) \
+ TARGET_SIGNAL_STRUCT (TARGET_SIGNAL_NUMBER_CONST (target_signal))
#define SET(symbol, constant, name, string) ANY (symbol, name, string)
#define ANY(symbol, name, string) \
@@ -85,7 +102,7 @@ target_signal_t;
static inline target_signal_t
target_signal_from_number (enum target_signal_number number)
{
- target_signal_t retval = { number } ;
+ target_signal_t retval = TARGET_SIGNAL_STRUCT (number);
return retval;
}