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

Pedro Alves palves@redhat.com
Fri Feb 24 21:29:00 GMT 2012


On 02/23/2012 03:31 AM, Yao Qi wrote:
> On 02/23/2012 01:19 AM, Pedro Alves wrote:
>> 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.
>>
> 
> Can we install target_ops hook to_program_signals in linux-nat, and move
> existings code in it?

The only existing code that would would change (other than the
new target method implementations) would be:

in linux-nat.c:get_pending_status:

>  if (signo == TARGET_SIGNAL_0)
>    {
>      if (debug_linux_nat)
>	fprintf_unfiltered (gdb_stdlog,
>			    "GPT: lwp %s has no pending signal\n",
>			    target_pid_to_str (lp->ptid));
>    }
>  else if (!signal_pass_state (signo))
           ^^^^^^^^^^^^^^^^^^^^^^^^^

here, we'd check a local copy of the array signal_pass_state
checks.

>
>    {
>      if (debug_linux_nat)
>	fprintf_unfiltered (gdb_stdlog,
>			    "GPT: lwp %s had signal %s, "
>			    "but it is in no pass state\n",
>			    target_pid_to_str (lp->ptid),
>			    target_signal_to_string (signo));
>    }

> As you said, this problem doesn't exit on
> linux-nat, and a new target_ops hook to_program_signals is created, so
> it is natural to move existing linux-nat code to
> linux_nat_program_signals (it is to be created).

The problem that doesn't exist is that linux-nat can do that call
to infrun.c:signal_pass_state directly.  gdbserver of course can, so we need to
give it a copy of the array signal_pass_state checks its argument against.  We
can do the same to linux-nat, so the separation between core and target is a
bit stronger, but that'd just mean we'd end up with two copies of the same
array.  If you ask me, it doesn't seem worth it any trouble.

> 
> 
>> 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.  */
>                             ^^ "THREAD"
> Can we remove the 2nd half of this setense "for detaching purpose".
> IIUC, this routine is quite general, so it might be used for other
> purpose in the future.

I don't think it's that general.  I'm least I'm not imagining any
other use for it.  I've renamed it and added some more comments:

/* Get pending signal of THREAD, for detaching purposes.  This is the
   signal the thread last stopped for, which we need to deliver to the
   thread when detaching, otherwise, it'd be suppressed/lost.  */

static int
get_detach_signal (struct thread_info *thread)
{

> 
>> +
>> +static int
>> +get_pending_signal (struct thread_info *thread)
> 
>> +
>>  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);
>>      }
>>  
> 
> As described in changelog entry,
> 
> 	(linux_detach_one_lwp): Get rid of a pending SIGSTOP with SIGCONT.
> 	Pass on pending signals to PTRACE_DETACH.
> 
> Pending SIGSTOP is replaced with (pending?) SIGCONT.  

Right... It's a lesser of two evils.  The existing code doesn't work when we
want to forward the last signal to PTRACE_DETACH, as it changes the
thread's last intercepted signal.  And to make it more reliable we'd
have to loop, until we saw the SIGSTOP, and requeue all other signals.
But requeing signals isn't transparent.  So...  a SIGCONT cancels
the SIGSTOP, and the thread is left with a SIGCONT pending.  SIGCONT
as a signal itself is pretty harmless, so until we get more
serious about proper job control, I don't think we need to bother. The same change
was done on linux-nat when the attach-into-signal issue was fixed there.  See:

http://sourceware.org/ml/gdb-patches/2008-05/msg00022.html

Since we've been doing the same in native gdb for years, I think it's
pretty fine to do it in gdbserver as well.


> In original code,
> resume lwp -> wait -> PTRACE_DETACH (0).  With this patch, it changed to
> PTRACE_DETACH (SIGCONT) directly, IIUC.  

No.  It is changed to PTRACE_DETACH (last signal the thread stopped with),
or in the case of the attach-into-signal.exp test, PTRACE_DETACH(SIGALRM).
PTRACE_DETACH(0) suppresses the signal delivery.  Just like
PTRACE_CONT(0) --- when we want to forward the signal to the inferior
we do PTRACE_CONT(SIG).

> I am wondering it may the
> status of program after detach, especiall when attach to a `T (stopped)'
> process at the beginning of debug session.  I am unable to find a test
> case to proof, so I may be wrong.  Just rise this question here.

Yeah, to test these things the easiest is to hack the code and queue
a SIGSTOP manually, to force the state you want.  You're right that
we shouldn't SIGCONT in that case (or rather never).  But, this (job control
stops) is something that is just impossible to get right as long as we
need to stop processes with SIGSTOP.  The way to fix this all is to
implement support for the new very new PTRACE_SEIZE and PTRACE_INTERRUPT
ptrace features.  With those, we'll never need to get rid of a pending
SIGSTOP we sent ourselves, so the evil is eliminated permanently.
I plan to work on that, but it's a bit low on my TODO at the moment.

Below's the updated patch.  Essentially the same, with that function
renamed, and more comments.

-- 
Pedro Alves

2012-02-24  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_detach_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 |  107 ++++++++++++++++++++++++++++++++++++++++++---
 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, 317 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..20057bd 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -928,36 +928,127 @@ linux_kill (int pid)
   return 0;
 }

+/* Get pending signal of THREAD, for detaching purposes.  This is the
+   signal the thread last stopped for, which we need to deliver to the
+   thread when detaching, otherwise, it'd be suppressed/lost.  */
+
+static int
+get_detach_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_detach_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);



More information about the Gdb-patches mailing list