[PATCH] [RFC, docs RFA] gdbserver and gdb.threads/attach-into-signal.exp.

Pedro Alves
2012-02-22T17:20:13Z

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);