This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
Re: [PATCH] [RFC, docs RFA] gdbserver and gdb.threads/attach-into-signal.exp.
- From: Pedro Alves <palves at redhat dot com>
- To: Pedro Alves <palves at redhat dot com>
- Cc: gdb-patches at sourceware dot org
- Date: Tue, 06 Mar 2012 18:11:34 +0000
- Subject: Re: [PATCH] [RFC, docs RFA] gdbserver and gdb.threads/attach-into-signal.exp.
- References: <20120222171937.29946.56976.stgit@hit-nxdomain.opendns.com>
Hi!
Any further comments on this? Eli, this needs a documentation review.
<http://sourceware.org/ml/gdb-patches/2012-02/msg00481.html>
Thanks!
--
Pedro Alves
On 02/22/2012 05:19 PM, Pedro Alves wrote:
> This patch fixes gdbserver so that it passes attach-into-signal.exp.
>
> Currently, we get (with the extended-remote board):
>
> Running ../../../src/gdb/testsuite/gdb.threads/attach-into-signal.exp ...
> FAIL: gdb.threads/attach-into-signal.exp: nonthreaded: detach (the program is no longer running)
> FAIL: gdb.threads/attach-into-signal.exp: nonthreaded: detach (the program is no longer running)
> FAIL: gdb.threads/attach-into-signal.exp: nonthreaded: detach (the program is no longer running)
> FAIL: gdb.threads/attach-into-signal.exp: nonthreaded: detach (the program is no longer running)
> FAIL: gdb.threads/attach-into-signal.exp: nonthreaded: detach (the program is no longer running)
> FAIL: gdb.threads/attach-into-signal.exp: nonthreaded: detach (the program is no longer running)
> FAIL: gdb.threads/attach-into-signal.exp: nonthreaded: detach (the program is no longer running)
> (...)
>
> The test exercises attaching and findind the program stops with a
> signal not SIGSTOP, and then detaching, then attaching, etc, in a
> loop.
>
> In gdb.log, we see:
>
> (gdb) attach 28431
> Attaching to program: /home/pedro/gdb/mygit/build/gdb/testsuite/gdb.threads/attach-into-signal-nothr, process 28431
> Attached; pid = 28431
>
> Program received signal SIGALRM, Alarm clock.
> Reading symbols from /lib64/libm.so.6...(no debugging symbols found)...done.
> Loaded symbols for /lib64/libm.so.6
> Reading symbols from /lib64/libc.so.6...(no debugging symbols found)...done.
> Loaded symbols for /lib64/libc.so.6
> Reading symbols from /lib64/ld-linux-x86-64.so.2...(no debugging symbols found)...done.
> Loaded symbols for /lib64/ld-linux-x86-64.so.2
> 0x000000339e636285 in raise () from /lib64/libc.so.6
> (gdb) PASS: gdb.threads/attach-into-signal.exp: nonthreaded: attach (pass 1), pending signal catch
> attach (pass 1), pending signal catch succeeded on the attempt # 2 of 100
> detach
> Detaching from program: /home/pedro/gdb/mygit/build/gdb/testsuite/gdb.threads/attach-into-signal-nothr, process 28431
> Detaching from process 28431
> (gdb) attach 28431
> Attaching to program: /home/pedro/gdb/mygit/build/gdb/testsuite/gdb.threads/attach-into-signal-nothr, process 28431
> Attached; pid = 28431
>
> Child terminated with signal = 0x6 (SIGABRT)
> warning: Selected architecture i386:x86-64 is not compatible with reported target architecture i386
> warning: Architecture rejected target-supplied description
>
> Program terminated with signal SIGABRT, Aborted.
>
> The test code reads:
>
> raise (SIGALRM);
>
> /* We must not get past this point, either in a free standing or debugged
> state. */
>
> abort ();
>
> The issue is that when GDBserver detaches the inferior, it forgets the
> SIGALRM signal it had found when it attached, so the inferior doesn't
> get the signal (it is suppressed) and reaches the abort. The fix is
> to make sure we forward the pending signals to the inferior on detach,
> like gdb/linux-nat.c does. gdb/linux-nat.c does not pass down to the
> inferior signals that are in the "handle SIG nopass" state, however.
> GDBserver has no idea currently about the pass/nopass state of
> signals, so in order for GDBserver to do the same GDB, we need to make
> GDB tell GDBserver about the pass/nopass state of all signals. This
> is recorded in GDB in the infrun.c:signal_program array. The patch
> adds a new RSP packet QProgramSignals (similar to QPassSignals, though
> with different semantics) to do exactly that.
>
> Comments on the approach? I first thought of augmenting the detach
> packet, but I quickly gave up, due to pending signals and
> multi-threading (any, or multiple threads can have pending signals
> that need to be delivered). I went back and forth between caring
> about "handle SIG nopass" at all. It is always possible a signal gets
> queued right after we check for "handle SIG nopass", so we could just
> punt it, but I think this handles 99.99% of the cases and its what a
> users expect in the common case (especially for the case of the signal
> that _has_ been reported to GDB, if one gets program stopped by
> SIGFOO, and SIGFOO is nopass, I think it's reasonable to expect that
> detach suppresses it).
>
> Tested on x86_64 Fedora 16, extended-remote gdbserver. No
> regressions. The test gdb.threads/attach-into-signal.exp test passes
> cleanly with the patch applied.
>
> 2012-02-22 Pedro Alves <palves@redhat.com>
>
> gdb/doc/
> * gdb.texinfo (): Document new QProgramSignals packet.
> * gdb.texinfo (Remote configuration): Mention
> "program-signals-packet".
> (General Query Packets): Document QProgramSignals.
>
> gdb/gdbserver/
> * linux-low.c (get_pending_signal): New.
> (linux_detach_one_lwp): Get rid of a pending SIGSTOP with SIGCONT.
> Pass on pending signals to PTRACE_DETACH. Check the result of the
> ptrace call.
> * server.c (program_signals, program_signals_p): New.
> (handle_general_set): Handle QProgramSignals.
> * server.h (program_signals, program_signals_p): Declare.
>
> gdb/
> * inferior.h (update_signals_program_target): Declare.
> * infrun.c: (update_signals_program_target): New.
> (handle_command): Update the target of the new program signals
> array changes.
> * remote.c (PACKET_QProgramSignals): New enum.
> (last_program_signals_packet): New global.
> (remote_program_signals): New.
> (remote_start_remote): Update the target with the program signals
> list.
> (remote_protocol_features): Add entry for QPassSignals.
> (remote_open_1): Free anc clear last_program_signals_packet.
> (init_remote_ops): Install remote_program_signals.
> * target.c (update_current_target): Adjust.
> (target_program_signals): New.
> * target.h (struct target_ops) <to_program_signals>: New field.
> (target_program_signals): Declare.
> ---
> gdb/doc/gdb.texinfo | 46 ++++++++++++++++++++
> gdb/gdbserver/linux-low.c | 105 ++++++++++++++++++++++++++++++++++++++++++---
> gdb/gdbserver/server.c | 33 ++++++++++++++
> gdb/gdbserver/server.h | 2 +
> gdb/inferior.h | 2 +
> gdb/infrun.c | 10 ++++
> gdb/remote.c | 75 ++++++++++++++++++++++++++++++++
> gdb/target.c | 31 +++++++++++++
> gdb/target.h | 20 +++++++++
> 9 files changed, 315 insertions(+), 9 deletions(-)
>
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index 73779a7..8223931 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -17453,6 +17453,10 @@ are:
> @tab @code{QPassSignals}
> @tab @code{handle @var{signal}}
>
> +@item @code{program-signals}
> +@tab @code{QProgramSignals}
> +@tab @code{handle @var{signal}}
> +
> @item @code{hostio-close-packet}
> @tab @code{vFile:close}
> @tab @code{remote get}, @code{remote put}
> @@ -34817,6 +34821,48 @@ command (@pxref{Remote Configuration, set remote pass-signals}).
> This packet is not probed by default; the remote stub must request it,
> by supplying an appropriate @samp{qSupported} response (@pxref{qSupported}).
>
> +@item QProgramSignals: @var{signal} @r{[};@var{signal}@r{]}@dots{}
> +@cindex signals the inferior may see, remote request
> +@cindex @samp{QProgramSignals} packet
> +@anchor{QProgramSignals}
> +Each listed @var{signal} may be delivered to the inferior process.
> +Others should be silently discarded.
> +
> +In some cases, the remote stub may need to decide whether to deliver a
> +signal to the program or not without @value{GDBN} involvement. One
> +example of that is while detaching --- the program's threads may have
> +stopped for signals that haven't yet had a chance of being reported to
> +@value{GDBN}, and so the remote stub can use the signal list specified
> +by this packet to know whether to deliver or ignore those pending
> +signals.
> +
> +This does not influence whether to deliver a signal as requested by a
> +resumption packet (@pxref{vCont packet}).
> +
> +Signals are numbered identically to continue packets and stop replies
> +(@pxref{Stop Reply Packets}). Each @var{signal} list item should be
> +strictly greater than the previous item. Multiple
> +@samp{QProgramSignals} packets do not combine; any earlier
> +@samp{QProgramSignals} list is completely replaced by the new list.
> +
> +Reply:
> +@table @samp
> +@item OK
> +The request succeeded.
> +
> +@item E @var{nn}
> +An error occurred. @var{nn} are hex digits.
> +
> +@item
> +An empty reply indicates that @samp{QProgramSignals} is not supported
> +by the stub.
> +@end table
> +
> +Use of this packet is controlled by the @code{set remote program-signals}
> +command (@pxref{Remote Configuration, set remote program-signals}).
> +This packet is not probed by default; the remote stub must request it,
> +by supplying an appropriate @samp{qSupported} response (@pxref{qSupported}).
> +
> @item qRcmd,@var{command}
> @cindex execute remote command, remote request
> @cindex @samp{qRcmd} packet
> diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
> index ab34d84..5353cff 100644
> --- a/gdb/gdbserver/linux-low.c
> +++ b/gdb/gdbserver/linux-low.c
> @@ -928,36 +928,125 @@ linux_kill (int pid)
> return 0;
> }
>
> +/* Get pending signal of LP, for detaching purposes. */
> +
> +static int
> +get_pending_signal (struct thread_info *thread)
> +{
> + enum target_signal signo = TARGET_SIGNAL_0;
> + int status;
> + struct lwp_info *lp = get_thread_lwp (thread);
> +
> + if (lp->status_pending_p)
> + status = lp->status_pending;
> + else
> + {
> + /* If the thread had been suspended by gdbserver, and it stopped
> + cleanly, then it'll have stopped with SIGSTOP. But we don't
> + want to deliver that SIGSTOP. */
> + if (thread->last_status.kind != TARGET_WAITKIND_STOPPED
> + || thread->last_status.value.sig == TARGET_SIGNAL_0)
> + return 0;
> +
> + /* Otherwise, we may need to deliver the signal we
> + intercepted. */
> + status = lp->last_status;
> + }
> +
> + if (!WIFSTOPPED (status))
> + {
> + if (debug_threads)
> + fprintf (stderr,
> + "GPS: lwp %s hasn't stopped: no pending signal\n",
> + target_pid_to_str (ptid_of (lp)));
> + return 0;
> + }
> +
> + /* Extended wait statuses aren't real SIGTRAPs. */
> + if (WSTOPSIG (status) == SIGTRAP && status >> 16 != 0)
> + {
> + if (debug_threads)
> + fprintf (stderr,
> + "GPS: lwp %s had stopped with extended "
> + "status: no pending signal\n",
> + target_pid_to_str (ptid_of (lp)));
> + return 0;
> + }
> +
> + signo = target_signal_from_host (WSTOPSIG (status));
> +
> + if (program_signals_p && !program_signals[signo])
> + {
> + if (debug_threads)
> + fprintf (stderr,
> + "GPS: lwp %s had signal %s, but it is in nopass state\n",
> + target_pid_to_str (ptid_of (lp)),
> + target_signal_to_string (signo));
> + return 0;
> + }
> + else if (!program_signals_p
> + /* If we have no way to know which signals GDB does not
> + want to have passed to the program, assume
> + SIGTRAP/SIGINT, which is GDB's default. */
> + && (signo == TARGET_SIGNAL_TRAP || signo == TARGET_SIGNAL_INT))
> + {
> + if (debug_threads)
> + fprintf (stderr,
> + "GPS: lwp %s had signal %s, "
> + "but we don't know if we should pass it. Default to not.\n",
> + target_pid_to_str (ptid_of (lp)),
> + target_signal_to_string (signo));
> + return 0;
> + }
> + else
> + {
> + if (debug_threads)
> + fprintf (stderr,
> + "GPS: lwp %s has pending signal %s: delivering it.\n",
> + target_pid_to_str (ptid_of (lp)),
> + target_signal_to_string (signo));
> +
> + return WSTOPSIG (status);
> + }
> +}
> +
> static int
> linux_detach_one_lwp (struct inferior_list_entry *entry, void *args)
> {
> struct thread_info *thread = (struct thread_info *) entry;
> struct lwp_info *lwp = get_thread_lwp (thread);
> int pid = * (int *) args;
> + int sig;
>
> if (ptid_get_pid (entry->id) != pid)
> return 0;
>
> - /* If this process is stopped but is expecting a SIGSTOP, then make
> - sure we take care of that now. This isn't absolutely guaranteed
> - to collect the SIGSTOP, but is fairly likely to. */
> + /* If there is a pending SIGSTOP, get rid of it. */
> if (lwp->stop_expected)
> {
> - int wstat;
> - /* Clear stop_expected, so that the SIGSTOP will be reported. */
> + if (debug_threads)
> + fprintf (stderr,
> + "Sending SIGCONT to %s\n",
> + target_pid_to_str (ptid_of (lwp)));
> +
> + kill_lwp (lwpid_of (lwp), SIGCONT);
> lwp->stop_expected = 0;
> - linux_resume_one_lwp (lwp, 0, 0, NULL);
> - linux_wait_for_event (lwp->head.id, &wstat, __WALL);
> }
>
> /* Flush any pending changes to the process's registers. */
> regcache_invalidate_one ((struct inferior_list_entry *)
> get_lwp_thread (lwp));
>
> + /* Pass on any pending signal for this thread. */
> + sig = get_pending_signal (thread);
> +
> /* Finally, let it resume. */
> if (the_low_target.prepare_to_resume != NULL)
> the_low_target.prepare_to_resume (lwp);
> - ptrace (PTRACE_DETACH, lwpid_of (lwp), 0, 0);
> + if (ptrace (PTRACE_DETACH, lwpid_of (lwp), 0, sig) < 0)
> + error (_("Can't detach %s: %s"),
> + target_pid_to_str (ptid_of (lwp)),
> + strerror (errno));
>
> delete_lwp (lwp);
> return 0;
> diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
> index 37dc8d1..96429cb 100644
> --- a/gdb/gdbserver/server.c
> +++ b/gdb/gdbserver/server.c
> @@ -58,6 +58,8 @@ int debug_threads;
> int debug_hw_points;
>
> int pass_signals[TARGET_SIGNAL_LAST];
> +int program_signals[TARGET_SIGNAL_LAST];
> +int program_signals_p;
>
> jmp_buf toplevel;
>
> @@ -454,6 +456,33 @@ handle_general_set (char *own_buf)
> return;
> }
>
> + if (strncmp ("QProgramSignals:", own_buf, strlen ("QProgramSignals:")) == 0)
> + {
> + int numsigs = (int) TARGET_SIGNAL_LAST, i;
> + const char *p = own_buf + strlen ("QProgramSignals:");
> + CORE_ADDR cursig;
> +
> + program_signals_p = 1;
> +
> + p = decode_address_to_semicolon (&cursig, p);
> + for (i = 0; i < numsigs; i++)
> + {
> + if (i == cursig)
> + {
> + program_signals[i] = 1;
> + if (*p == '\0')
> + /* Keep looping, to clear the remaining signals. */
> + cursig = -1;
> + else
> + p = decode_address_to_semicolon (&cursig, p);
> + }
> + else
> + program_signals[i] = 0;
> + }
> + strcpy (own_buf, "OK");
> + return;
> + }
> +
> if (strcmp (own_buf, "QStartNoAckMode") == 0)
> {
> if (remote_debug)
> @@ -1559,7 +1588,9 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
> free (qsupported);
> }
>
> - sprintf (own_buf, "PacketSize=%x;QPassSignals+", PBUFSIZ - 1);
> + sprintf (own_buf,
> + "PacketSize=%x;QPassSignals+;QProgramSignals+",
> + PBUFSIZ - 1);
>
> if (the_target->qxfer_libraries_svr4 != NULL)
> strcat (own_buf, ";qXfer:libraries-svr4:read+");
> diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h
> index d3b4463..0a19664 100644
> --- a/gdb/gdbserver/server.h
> +++ b/gdb/gdbserver/server.h
> @@ -288,6 +288,8 @@ extern int server_waiting;
> extern int debug_threads;
> extern int debug_hw_points;
> extern int pass_signals[];
> +extern int program_signals[];
> +extern int program_signals_p;
>
> extern jmp_buf toplevel;
>
> diff --git a/gdb/inferior.h b/gdb/inferior.h
> index 65abf26..2cd8be4 100644
> --- a/gdb/inferior.h
> +++ b/gdb/inferior.h
> @@ -645,4 +645,6 @@ extern struct inferior *add_inferior_with_spaces (void);
>
> extern void update_observer_mode (void);
>
> +extern void update_signals_program_target (void);
> +
> #endif /* !defined (INFERIOR_H) */
> diff --git a/gdb/infrun.c b/gdb/infrun.c
> index 1b2da67..ee4b9a7 100644
> --- a/gdb/infrun.c
> +++ b/gdb/infrun.c
> @@ -335,6 +335,15 @@ static unsigned char *signal_pass;
> (flags)[signum] = 0; \
> } while (0)
>
> +/* Update the target's copy of SIGNAL_PROGRAM. The sole purpose of
> + this function is to avoid exporting `signal_program'. */
> +
> +void
> +update_signals_program_target (void)
> +{
> + target_program_signals ((int) TARGET_SIGNAL_LAST, signal_program);
> +}
> +
> /* Value to pass to target_resume() to cause all threads to resume. */
>
> #define RESUME_ALL minus_one_ptid
> @@ -6358,6 +6367,7 @@ Are you sure you want to change it? "),
> {
> signal_cache_update (-1);
> target_pass_signals ((int) TARGET_SIGNAL_LAST, signal_pass);
> + target_program_signals ((int) TARGET_SIGNAL_LAST, signal_program);
>
> if (from_tty)
> {
> diff --git a/gdb/remote.c b/gdb/remote.c
> index 14c343b..4e6ff1c 100644
> --- a/gdb/remote.c
> +++ b/gdb/remote.c
> @@ -1254,6 +1254,7 @@ enum {
> PACKET_qGetTLSAddr,
> PACKET_qSupported,
> PACKET_QPassSignals,
> + PACKET_QProgramSignals,
> PACKET_qSearch_memory,
> PACKET_vAttach,
> PACKET_vRun,
> @@ -1662,6 +1663,65 @@ remote_pass_signals (int numsigs, unsigned char *pass_signals)
> }
> }
>
> +/* The last QProgramSignals packet sent to the target. We bypass
> + sending a new program signals list down to the target if the new
> + packet is exactly the same as the last we sent. IOW, we only let
> + the target know about program signals list changes. */
> +
> +static char *last_program_signals_packet;
> +
> +/* If 'QProgramSignals' is supported, tell the remote stub what
> + signals it should pass through to the inferior when detaching. */
> +
> +static void
> +remote_program_signals (int numsigs, unsigned char *signals)
> +{
> + if (remote_protocol_packets[PACKET_QProgramSignals].support != PACKET_DISABLE)
> + {
> + char *packet, *p;
> + int count = 0, i;
> +
> + gdb_assert (numsigs < 256);
> + for (i = 0; i < numsigs; i++)
> + {
> + if (signals[i])
> + count++;
> + }
> + packet = xmalloc (count * 3 + strlen ("QProgramSignals:") + 1);
> + strcpy (packet, "QProgramSignals:");
> + p = packet + strlen (packet);
> + for (i = 0; i < numsigs; i++)
> + {
> + if (signal_pass_state (i))
> + {
> + if (i >= 16)
> + *p++ = tohex (i >> 4);
> + *p++ = tohex (i & 15);
> + if (count)
> + *p++ = ';';
> + else
> + break;
> + count--;
> + }
> + }
> + *p = 0;
> + if (!last_program_signals_packet
> + || strcmp (last_program_signals_packet, packet) != 0)
> + {
> + struct remote_state *rs = get_remote_state ();
> + char *buf = rs->buf;
> +
> + putpkt (packet);
> + getpkt (&rs->buf, &rs->buf_size, 0);
> + packet_ok (buf, &remote_protocol_packets[PACKET_QProgramSignals]);
> + xfree (last_program_signals_packet);
> + last_program_signals_packet = packet;
> + }
> + else
> + xfree (packet);
> + }
> +}
> +
> /* If PTID is MAGIC_NULL_PTID, don't set any thread. If PTID is
> MINUS_ONE_PTID, set the thread to -1, so the stub returns the
> thread. If GEN is set, set the general thread, if not, then set
> @@ -3253,6 +3313,10 @@ remote_start_remote (int from_tty, struct target_ops *target, int extended_p)
> getpkt (&rs->buf, &rs->buf_size, 0);
> }
>
> + /* Let the target know which signals it is allowed to pass down to
> + the program. */
> + update_signals_program_target ();
> +
> /* Next, if the target can specify a description, read it. We do
> this before anything involving memory or registers. */
> target_find_description ();
> @@ -3800,6 +3864,8 @@ static struct protocol_feature remote_protocol_features[] = {
> PACKET_qXfer_traceframe_info },
> { "QPassSignals", PACKET_DISABLE, remote_supported_packet,
> PACKET_QPassSignals },
> + { "QProgramSignals", PACKET_DISABLE, remote_supported_packet,
> + PACKET_QProgramSignals },
> { "QStartNoAckMode", PACKET_DISABLE, remote_supported_packet,
> PACKET_QStartNoAckMode },
> { "multiprocess", PACKET_DISABLE, remote_multi_process_feature, -1 },
> @@ -4065,6 +4131,11 @@ remote_open_1 (char *name, int from_tty,
> xfree (last_pass_packet);
> last_pass_packet = NULL;
>
> + /* Make sure we send the program signals list the next time we
> + resume. */
> + xfree (last_program_signals_packet);
> + last_program_signals_packet = NULL;
> +
> remote_fileio_reset ();
> reopen_exec_file ();
> reread_symbols ();
> @@ -10715,6 +10786,7 @@ Specify the serial device it is connected to\n\
> remote_ops.to_load = generic_load;
> remote_ops.to_mourn_inferior = remote_mourn;
> remote_ops.to_pass_signals = remote_pass_signals;
> + remote_ops.to_program_signals = remote_program_signals;
> remote_ops.to_thread_alive = remote_thread_alive;
> remote_ops.to_find_new_threads = remote_threads_info;
> remote_ops.to_pid_to_str = remote_pid_to_str;
> @@ -11156,6 +11228,9 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
> add_packet_config_cmd (&remote_protocol_packets[PACKET_QPassSignals],
> "QPassSignals", "pass-signals", 0);
>
> + add_packet_config_cmd (&remote_protocol_packets[PACKET_QProgramSignals],
> + "QProgramSignals", "program-signals", 0);
> +
> add_packet_config_cmd (&remote_protocol_packets[PACKET_qSymbol],
> "qSymbol", "symbol-lookup", 0);
>
> diff --git a/gdb/target.c b/gdb/target.c
> index ad304bc..6098bd9 100644
> --- a/gdb/target.c
> +++ b/gdb/target.c
> @@ -638,6 +638,7 @@ update_current_target (void)
> /* Do not inherit to_mourn_inferior. */
> INHERIT (to_can_run, t);
> /* Do not inherit to_pass_signals. */
> + /* Do not inherit to_program_signals. */
> /* Do not inherit to_thread_alive. */
> /* Do not inherit to_find_new_threads. */
> /* Do not inherit to_pid_to_str. */
> @@ -2713,6 +2714,36 @@ target_pass_signals (int numsigs, unsigned char *pass_signals)
> }
> }
>
> +void
> +target_program_signals (int numsigs, unsigned char *program_signals)
> +{
> + struct target_ops *t;
> +
> + for (t = current_target.beneath; t != NULL; t = t->beneath)
> + {
> + if (t->to_program_signals != NULL)
> + {
> + if (targetdebug)
> + {
> + int i;
> +
> + fprintf_unfiltered (gdb_stdlog, "target_program_signals (%d, {",
> + numsigs);
> +
> + for (i = 0; i < numsigs; i++)
> + if (program_signals[i])
> + fprintf_unfiltered (gdb_stdlog, " %s",
> + target_signal_to_name (i));
> +
> + fprintf_unfiltered (gdb_stdlog, " })\n");
> + }
> +
> + (*t->to_program_signals) (numsigs, program_signals);
> + return;
> + }
> + }
> +}
> +
> /* Look through the list of possible targets for a target that can
> follow forks. */
>
> diff --git a/gdb/target.h b/gdb/target.h
> index d4605ae..f824ed4 100644
> --- a/gdb/target.h
> +++ b/gdb/target.h
> @@ -509,6 +509,10 @@ struct target_ops
> target_* macro. */
> void (*to_pass_signals) (int, unsigned char *);
>
> + /* Documentation of this routine is provided with the
> + corresponding target_* function. */
> + void (*to_program_signals) (int, unsigned char *);
> +
> int (*to_thread_alive) (struct target_ops *, ptid_t ptid);
> void (*to_find_new_threads) (struct target_ops *);
> char *(*to_pid_to_str) (struct target_ops *, ptid_t);
> @@ -1242,6 +1246,22 @@ void target_mourn_inferior (void);
>
> extern void target_pass_signals (int nsig, unsigned char *pass_signals);
>
> +/* Set list of signals the target may pass to the inferior. This
> + directly maps to the "handle SIGNAL pass/nopass" setting.
> +
> + PROGRAM_SIGNALS is an array of size NSIG, indexed by target signal
> + number (enum target_signal). For every signal whose entry in this
> + array is non-zero, the target is allowed to pass the signal to the
> + inferior. Signals not present in the array shall be silently
> + discarded. This does not influence whether to pass signals to the
> + inferior as a result of a target_resume call. This is useful in
> + scenarios where the target needs to decide whether to pass or not a
> + signal to the inferior without GDB core involvement, such as for
> + example, when detaching (as threads may have been suspended with
> + pending signals not reported to GDB). */
> +
> +extern void target_program_signals (int nsig, unsigned char *program_signals);
> +
> /* Check to see if a thread is still alive. */
>
> extern int target_thread_alive (ptid_t ptid);
>