diff mbox

[1/5] Extended-remote exec events

Message ID 1436996979-32350-2-git-send-email-donb@codesourcery.com
State New
Headers show

Commit Message

Don Breazeal July 15, 2015, 9:49 p.m. UTC
This patch is the latest version implementing support for exec
events on extended-remote Linux targets.  Follow-exec-mode and
rerun behave as expected.  Catchpoints and test updates are
implemented in subsequent patches.

This patch was most recently derived from a patch posted last
October: https://sourceware.org/ml/gdb-patches/2014-10/msg00877.html.
It was originally based on some work done by Luis Machado in 2013.

IMPLEMENTATION
----------------
Support for exec events in single-threaded programs was a fairly
straightforward replication of the implementation in native GDB:

1) Enable exec events via ptrace options.

2) Add support for handling the exec events to the handle_extended_wait and
linux_wait_for_event_filtered.  Detect the exec event, then find and save
the pathname of the executable file being exec'd and set event status flags.

3) Implement an additional "stop reason", "exec", in the RSP stop reply
packet "T".

Existing GDB code takes care of handling the exec event on the host side
without modification.

Support for exec events in multi-threaded programs required some additional
work.  When exec is called, the Linux kernel destroys all of the threads
except the execing one.  If the execing thread was not the thread group
leader, the kernel resets the execing thread's tid to the tgid, and no
exit notification is sent for the execing thread -- from the ptracer's
perspective, it appears as though the execing thread just vanishes.

The non-leader exec leaves gdbserver with one or more of several
potential scenarios which require it to bring its thread lists (e.g.
struct thread_info, struct lwp_info) in sync with reality.

 - The leader thread exited before the exec event was reported, and
   the execing thread cannot re-use its data structures.

   In this case gdbserver must recognize that an exec event occurred
   and there are no thread structures for the leader thread, so it
   must add new structures to the lists for the 'new' leader thread.

  if (WIFSTOPPED (wstat) && (child == NULL) && (WSTOPSIG (wstat) == SIGTRAP)
      && (linux_ptrace_get_extended_event (wstat) == PTRACE_EVENT_EXEC))
    {
      ptid_t child_ptid;

      /* A multi-thread exec after we had seen the leader exiting.  */
      if (debug_threads)
	{
	  debug_printf ("LLW: Re-adding thread group leader LWP %d"
			"after exec.\n", lwpid);
	}

      child_ptid = ptid_build (lwpid, lwpid, 0);
      child = add_lwp (child_ptid);
      child->stopped = 1;
      current_thread = child->thread;
    }

 - The execing thread can re-use the previous leader thread's data
   structures, and the old data structures used for the execing thread
   prior to the exec are left with a running status and no actual
   thread associated with it.

   When a non-leader execing thread re-uses the previous leader's
   thread_info structure, it inherits the old thread's register
   cache.  If this is left as-is it will eventually be flushed to
   the target, clobbering the valid register values with those from
   the old thread.  So when an EXEC event occurs we always invalidate
   the register cache.  Note that we can't call regcache_invalidate,
   since that flushes the cache to the target.

  else if (event == PTRACE_EVENT_EXEC && report_exec_events)
    {
      struct regcache *regcache;
      ---snip snip---
      regcache = (struct regcache *) inferior_regcache_data (event_thr);
      free_register_cache (regcache);
      set_inferior_regcache_data (event_thr, NULL);
      event_lwp->stop_pc = get_pc (event_lwp);

   In this case (stale thread data structures for execing thread) when
   in all-stop mode, gdbserver must clean up any stale thread/lwp structures
   before it tries to stop all the threads and hangs in sigsuspend, waiting
   for an event from a non-existent thread.  We do this by checking the
   return value from kill_lwp in send_sigstop, eventually called after
   calling stop_all_lwps, which is used to stop the threads in all-stop
   mode.  If kill_lwp returns an error and errno is ESRCH, we know that the
   lwp with that pid is gone, and we delete the associated data structures.

-  kill_lwp (pid, SIGSTOP);
+  errno = 0;
+  ret = kill_lwp (pid, SIGSTOP);
+  if (ret == -1 && errno == ESRCH)
+    {
+      /* If the kill fails with "No such process", on GNU/Linux we know
+	 that the LWP has vanished - it is not a zombie, it is gone.
+	 This is because a thread that was not the thread group leader
+	 called exec and took over the leader's lwp.  */
+      delete_lwp (lwp);
+      set_desired_thread (0);

   In the same case in non-stop mode, we don't need to stop all the
   lwps, but in order to utilize the same mechanism used in all-stop
   mode, we call stop_all_lwps/unstop_all_lwps in succession, just
   to check for ESRCH errors and to delete any stale thread structures.

      if (non_stop && stopping_threads == NOT_STOPPING_THREADS)
	{
	  /* In non-stop mode, make sure we delete the lwp entry for a
	     non-leader exec'ing thread, which will have vanished.  We
	     do this by sending a signal to all the other threads in the
	     lwp list, deleting any that are not found.  Note that in
	     all-stop mode this will happen when we stop all the threads.  */
	  stop_all_lwps (0, event_lwp);
	  unstop_all_lwps (0, event_lwp);
	}

Note that the native implementation uses a different mechanism for
identifying the stale data structure scenario.  It determines that the
execing thread has "vanished" by calling waitpid(PID) and checking for a
return value of ECHILD, which means that the thread is gone.  We don't want
to use waitpid(PID) in gdbserver, based on the discussion in:

  https://www.sourceware.org/ml/gdb-patches/2014-02/msg00828.html

so we use the send_sigstop method described above instead.

TESTING
--------
x86_64 GNU/Linux for native, native-gdbserver, and
native-extended-gdbserver targets.  Most of the exec-related tests fail
due to the lack of catchpoints and extended-remote support in the tests.

Thanks
--Don

gdb/gdbserver/
2015-07-15  Don Breazeal  <donb@codesourcery.com>
	    Luis Machado  <lgustavo@codesourcery.com>

	* linux-low.c (handle_extended_wait): Handle exec events.
	(check_zombie_leaders): Do not check stopped threads.
	(linux_low_ptrace_options): Add PTRACE_O_TRACEEXEC.
	(linux_low_filter_event): Add thread structures for exec'ing
	non-leader thread after leader thread had been deleted.
	(linux_wait_for_event_filtered): Fix comment saying exec events
	are not supported in remote.
	(extended_event_reported): Add TARGET_WAITKIND_EXECD.
	(linux_wait_1): Prevent clobbering extended event status.
	(send_sigstop): Check return from kill_lwp, and if ESRCH then
	call delete_lwp.
	(linux_supports_exec_events): New function.
	* lynx-low.c (lynx_target_ops): Initialize new structure
	member 'supports_exec_events'.
	* remote-utils.c (prepare_resume_reply): New stop reason 'exec'.
	* server.c (report_exec_events): New global variable.
	(handle_query): Handle qSupported query for exec-events feature.
	(captured_main): Initialize report_exec_events.
	* server.h (report_exec_events): Declare new global variable.
	* target.h (struct target_ops) <supports_exec_events>: New
	member.
        (target_supports_exec_events): New macro.
	* win32-low.c (win32_target_ops): Initialize new structure
	member 'supports_exec_events'.

gdb/
2015-07-15  Don Breazeal  <donb@codesourcery.com>
	    Luis Machado  <lgustavo@codesourcery.com>

	* nat/linux-ptrace.c (linux_supports_tracefork): Delete
	out-of-date comment verbiage.
	(linux_supports_traceexec): New function.
	* nat/linux-ptrace.h (linux_supports_traceexec): Declare.
	* remote.c (anonymous enum) <PACKET_exec_event_feature> New
	enumeration constant.
	(remote_protocol_features): Add entry for exec-events feature.
	(remote_query_supported): Add client side of qSupported query
	for exec-events feature.
	(_initialize_remote): Call add_packet_config_cmd for remote
	exec-events feature.

---
 gdb/gdbserver/linux-low.c    | 117 ++++++++++++++++++++++++++++++++++++++++---
 gdb/gdbserver/lynx-low.c     |   1 +
 gdb/gdbserver/remote-utils.c |  20 ++++++++
 gdb/gdbserver/server.c       |  11 ++++
 gdb/gdbserver/server.h       |   1 +
 gdb/gdbserver/target.h       |   7 +++
 gdb/gdbserver/win32-low.c    |   1 +
 gdb/nat/linux-ptrace.c       |  13 +++--
 gdb/nat/linux-ptrace.h       |   1 +
 gdb/remote.c                 |  30 +++++++++++
 10 files changed, 193 insertions(+), 9 deletions(-)

Comments

Yao Qi July 16, 2015, 2 p.m. UTC | #1
Don Breazeal <donb@codesourcery.com> writes:

> IMPLEMENTATION
> ----------------
> Support for exec events in single-threaded programs was a fairly
> straightforward replication of the implementation in native GDB:
>
> 1) Enable exec events via ptrace options.
>
> 2) Add support for handling the exec events to the handle_extended_wait and
> linux_wait_for_event_filtered.  Detect the exec event, then find and save
> the pathname of the executable file being exec'd and set event status flags.
>
> 3) Implement an additional "stop reason", "exec", in the RSP stop reply
> packet "T".
>
> Existing GDB code takes care of handling the exec event on the host side
> without modification.

Hi Don,
How does GDBserver handle the multi-arch case? say, 64-bit process call
exec to a 32-bit program.  At least, the target description of that
process in GDBserver should be updated.
Don Breazeal July 16, 2015, 3:51 p.m. UTC | #2
On 7/16/2015 7:00 AM, Yao Qi wrote:
> Don Breazeal <donb@codesourcery.com> writes:
> 
>> IMPLEMENTATION
>> ----------------
>> Support for exec events in single-threaded programs was a fairly
>> straightforward replication of the implementation in native GDB:
>>
>> 1) Enable exec events via ptrace options.
>>
>> 2) Add support for handling the exec events to the handle_extended_wait and
>> linux_wait_for_event_filtered.  Detect the exec event, then find and save
>> the pathname of the executable file being exec'd and set event status flags.
>>
>> 3) Implement an additional "stop reason", "exec", in the RSP stop reply
>> packet "T".
>>
>> Existing GDB code takes care of handling the exec event on the host side
>> without modification.
> 
> Hi Don,
> How does GDBserver handle the multi-arch case? say, 64-bit process call
> exec to a 32-bit program.  At least, the target description of that
> process in GDBserver should be updated.
> 
Hi Yao,
You make a good point, GDBserver doesn't handle that case.  I assume
that's what this is about:
---
warning: Selected architecture i386 is not compatible with reported
target architecture i386:x86-64
---
I'll investigate.
thanks
--Don
Yao Qi July 16, 2015, 4:35 p.m. UTC | #3
On 16/07/15 16:51, Don Breazeal wrote:
> You make a good point, GDBserver doesn't handle that case.  I assume
> that's what this is about:
> ---
> warning: Selected architecture i386 is not compatible with reported
> target architecture i386:x86-64
> ---
> I'll investigate.

This messages shows that GDB (rather than GDBserver) doesn't handle
that case.  AFAIK, GDBserver doesn't handle that case either.

I am working on patches create target description at the right time
in GDBserver, derived from this patch
https://sourceware.org/ml/gdb-patches/2015-07/msg00403.html
Current GDBserver creates target description too early, if we use
--wrapper option, GDBserver created target description according
to wrapper program, instead of the program we want to debug, which
is wrong.
Don Breazeal July 16, 2015, 5:06 p.m. UTC | #4
On 7/16/2015 9:35 AM, Yao Qi wrote:
> On 16/07/15 16:51, Don Breazeal wrote:
>> You make a good point, GDBserver doesn't handle that case.  I assume
>> that's what this is about:
>> ---
>> warning: Selected architecture i386 is not compatible with reported
>> target architecture i386:x86-64
>> ---
>> I'll investigate.
> 
> This messages shows that GDB (rather than GDBserver) doesn't handle
> that case.  AFAIK, GDBserver doesn't handle that case either.
> 
> I am working on patches create target description at the right time
> in GDBserver, derived from this patch
> https://sourceware.org/ml/gdb-patches/2015-07/msg00403.html
> Current GDBserver creates target description too early, if we use
> --wrapper option, GDBserver created target description according
> to wrapper program, instead of the program we want to debug, which
> is wrong.
> 

There is a difference between the native and gdbserver behavior with
multi-arch exec.  Native seems to handle multi-arch exec events:
-------------------------------------------------------------------
Reading symbols from ./execler64...done.
(gdb) b main
Breakpoint 1 at 0x4006a4: file execler.c, line 19.
(gdb) r
Starting program: /home/dbreazea/junk/execler64

Breakpoint 1, main (argc=1, argv=0x7fffffffe848) at execler.c:19
19	  printf ("starting %s\n", argv[0]);
(gdb) info reg
rax            0x7ffff7dd9ea8	140737351884456
rbx            0x0	0
rcx            0x0	0
---etc---
(gdb) catch exec
Catchpoint 2 (exec)
(gdb) c
Continuing.
starting /home/dbreazea/junk/execler64
in execler
process 5588 is executing new program: /home/dbreazea/junk/execee32
warning: the debug information found in "/lib/ld-2.11.1.so" does not
match "/lib/ld-linux.so.2" (CRC mismatch).


Catchpoint 2 (exec'd /home/dbreazea/junk/execee32), 0xf7fe0850 in ?? ()
   from /lib/ld-linux.so.2
(gdb) info reg
eax            0x0	0
ecx            0x0	0
---etc---
---------------------------------------------------------------------

While the gdbserver case looks like this, unfortunately:

---------------------------------------------------------------------
Reading symbols from ./execler64...done.
(gdb) tar ext localhost:51111
Remote debugging using localhost:51111
Reading symbols from target:/lib64/ld-linux-x86-64.so.2...(no debugging
symbols found)...done.
0x00007ffff7dddaf0 in ?? () from target:/lib64/ld-linux-x86-64.so.2
(gdb) b main
Breakpoint 1 at 0x4006a4: file execler.c, line 19.
(gdb) c
Continuing.

Breakpoint 1, main (argc=1, argv=0x7fffffffe9d8) at execler.c:19
19	  printf ("starting %s\n", argv[0]);
(gdb) info reg
rax            0x7ffff7dd9ea8	140737351884456
rbx            0x0	0
rcx            0x0	0
---etc---
(gdb) catch exec
Catchpoint 2 (exec)
(gdb) c
Continuing.
Thread 5561.5561 is executing new program: /home/dbreazea/junk/execee32
warning: Selected architecture i386 is not compatible with reported
target architecture i386:x86-64
warning: the debug information found in "target:/lib/ld-2.11.1.so" does
not match "target:/lib/ld-linux.so.2" (CRC mismatch).

Remote 'g' packet reply is too long: 000000000000000000000000000000...
---several 'g' packet errors
(gdb) q
A debugging session is active.

	Inferior 1 [process 5561] will be killed.

Quit anyway? (y or n) y
warning: Selected architecture i386 is not compatible with reported
target architecture i386:x86-64
--------------------------------------------------------------------

At first glance it looks like in linux_low_filter_event, the execing
inferior needs to be marked as a 'new_inferior'
(proc->priv->new_inferior) in order to do the right thing and call
the_low_target.arch_setup ().  That may require some re-ordering of
things in linux-low.c:linux_low_filter_event, since handle_extended_wait
and the exec event handling happen after the arch setup.

Do you think the work you are doing will address this, or should I
continue looking at a fix for the problem above?  They seem like they
are related, but separate issues.

thanks
--Don
Yao Qi July 17, 2015, 11:55 a.m. UTC | #5
Don Breazeal <donb@codesourcery.com> writes:

> While the gdbserver case looks like this, unfortunately:
>
> ---------------------------------------------------------------------
> Reading symbols from ./execler64...done.
> (gdb) tar ext localhost:51111
> Remote debugging using localhost:51111
> Reading symbols from target:/lib64/ld-linux-x86-64.so.2...(no debugging
> symbols found)...done.
> 0x00007ffff7dddaf0 in ?? () from target:/lib64/ld-linux-x86-64.so.2
> (gdb) b main
> Breakpoint 1 at 0x4006a4: file execler.c, line 19.
> (gdb) c
> Continuing.
>
> Breakpoint 1, main (argc=1, argv=0x7fffffffe9d8) at execler.c:19
> 19	  printf ("starting %s\n", argv[0]);
> (gdb) info reg
> rax            0x7ffff7dd9ea8	140737351884456
> rbx            0x0	0
> rcx            0x0	0
> ---etc---
> (gdb) catch exec
> Catchpoint 2 (exec)
> (gdb) c
> Continuing.
> Thread 5561.5561 is executing new program: /home/dbreazea/junk/execee32
> warning: Selected architecture i386 is not compatible with reported
> target architecture i386:x86-64
> warning: the debug information found in "target:/lib/ld-2.11.1.so" does
> not match "target:/lib/ld-linux.so.2" (CRC mismatch).
>
> Remote 'g' packet reply is too long: 000000000000000000000000000000...
> ---several 'g' packet errors
> (gdb) q
> A debugging session is active.
>
> 	Inferior 1 [process 5561] will be killed.
>
> Quit anyway? (y or n) y
> warning: Selected architecture i386 is not compatible with reported
> target architecture i386:x86-64
> --------------------------------------------------------------------
>

I thought gdb.multi/multi-arch-exec.exp has already covered the case
above.  It fails in my clean GDB build.  If it doesn't cover, we need to
improve it or write a new one to cover the case.

> At first glance it looks like in linux_low_filter_event, the execing
> inferior needs to be marked as a 'new_inferior'
> (proc->priv->new_inferior) in order to do the right thing and call
> the_low_target.arch_setup ().  That may require some re-ordering of
> things in linux-low.c:linux_low_filter_event, since handle_extended_wait
> and the exec event handling happen after the arch setup.

Yes, this needs some re-ordering...

>
> Do you think the work you are doing will address this, or should I
> continue looking at a fix for the problem above?  They seem like they
> are related, but separate issues.

I think my patch series will address this, and I am testing them.  In
the mean time, I don't think this multi-arch issue blocks the review to
this patch series.  As you said, they are related, but separated issues.
diff mbox

Patch

diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 2dafb03..47d8bc3 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -557,6 +557,52 @@  handle_extended_wait (struct lwp_info *event_lwp, int wstat)
       /* Report the event.  */
       return 0;
     }
+  else if (event == PTRACE_EVENT_EXEC && report_exec_events)
+    {
+      struct regcache *regcache;
+
+      if (debug_threads)
+	{
+	  debug_printf ("HEW: Got exec event from LWP %ld\n",
+			lwpid_of (event_thr));
+	}
+
+      /* If the exec was not called by the thread group leader, then
+	 the lwp_info and thread_info structures are out-of-date,
+	 containing information about the original leader thread and
+	 not the new exec'ing leader thread.  Invalidate the register
+	 cache without flushing it to the target, and reset the stop
+	 pc value in the lwp.  */
+      regcache = (struct regcache *) inferior_regcache_data (event_thr);
+      free_register_cache (regcache);
+      set_inferior_regcache_data (event_thr, NULL);
+      event_lwp->stop_pc = get_pc (event_lwp);
+
+      event_lwp->waitstatus.kind = TARGET_WAITKIND_EXECD;
+      event_lwp->waitstatus.value.execd_pathname
+	= xstrdup (linux_proc_pid_to_exec_file (lwpid_of (event_thr)));
+
+      /* Mark the exec status as pending.  */
+      event_lwp->stopped = 1;
+      event_lwp->status_pending_p = 1;
+      event_lwp->status_pending = wstat;
+      event_thr->last_resume_kind = resume_stop;
+      event_thr->last_status.kind = TARGET_WAITKIND_IGNORE;
+
+      if (non_stop && stopping_threads == NOT_STOPPING_THREADS)
+	{
+	  /* In non-stop mode, make sure we delete the lwp entry for a
+	     non-leader exec'ing thread, which will have vanished.  We
+	     do this by sending a signal to all the other threads in the
+	     lwp list, deleting any that are not found.  Note that in
+	     all-stop mode this will happen when we stop all the threads.  */
+	  stop_all_lwps (0, event_lwp);
+	  unstop_all_lwps (0, event_lwp);
+	}
+
+      /* Report the event.  */
+      return 0;
+    }
 
   internal_error (__FILE__, __LINE__, _("unknown ptrace event %d"), event);
 }
@@ -1609,7 +1655,7 @@  check_zombie_leaders (void)
 		      leader_pid, leader_lp!= NULL, num_lwps (leader_pid),
 		      linux_proc_pid_is_zombie (leader_pid));
 
-      if (leader_lp != NULL
+      if (leader_lp != NULL && !leader_lp->stopped
 	  /* Check if there are other threads in the group, as we may
 	     have raced with the inferior simply exiting.  */
 	  && !last_thread_of_process_p (leader_pid)
@@ -2035,6 +2081,9 @@  linux_low_ptrace_options (int attached)
   if (report_vfork_events)
     options |= (PTRACE_O_TRACEVFORK | PTRACE_O_TRACEVFORKDONE);
 
+  if (report_exec_events)
+    options |= PTRACE_O_TRACEEXEC;
+
   return options;
 }
 
@@ -2051,6 +2100,38 @@  linux_low_filter_event (int lwpid, int wstat)
 
   child = find_lwp_pid (pid_to_ptid (lwpid));
 
+  /* Check for stop events reported by a process we didn't already
+     know about - anything not already in our LWP list.
+
+     If we're expecting to receive stopped processes after
+     fork, vfork, and clone events, then we'll just add the
+     new one to our list and go back to waiting for the event
+     to be reported - the stopped process might be returned
+     from waitpid before or after the event is.
+
+     But note the case of a non-leader thread exec'ing after the
+     leader having exited, and gone from our lists (because
+     check_zombie_leaders deleted it).  The non-leader thread
+     changes its tid to the tgid.  */
+
+  if (WIFSTOPPED (wstat) && (child == NULL) && (WSTOPSIG (wstat) == SIGTRAP)
+      && (linux_ptrace_get_extended_event (wstat) == PTRACE_EVENT_EXEC))
+    {
+      ptid_t child_ptid;
+
+      /* A multi-thread exec after we had seen the leader exiting.  */
+      if (debug_threads)
+	{
+	  debug_printf ("LLW: Re-adding thread group leader LWP %d"
+			"after exec.\n", lwpid);
+	}
+
+      child_ptid = ptid_build (lwpid, lwpid, 0);
+      child = add_lwp (child_ptid);
+      child->stopped = 1;
+      current_thread = child->thread;
+    }
+
   /* If we didn't find a process, one of two things presumably happened:
      - A process we started and then detached from has exited.  Ignore it.
      - A process we are controlling has forked and the new child's stop
@@ -2342,8 +2423,7 @@  linux_wait_for_event_filtered (ptid_t wait_ptid, ptid_t filter_ptid,
 	 - When a non-leader thread execs, that thread just vanishes
 	   without reporting an exit (so we'd hang if we waited for it
 	   explicitly in that case).  The exec event is reported to
-	   the TGID pid (although we don't currently enable exec
-	   events).  */
+	   the TGID pid.  */
       errno = 0;
       ret = my_waitpid (-1, wstatp, options | WNOHANG);
 
@@ -2741,7 +2821,8 @@  extended_event_reported (const struct target_waitstatus *waitstatus)
 
   return (waitstatus->kind == TARGET_WAITKIND_FORKED
 	  || waitstatus->kind == TARGET_WAITKIND_VFORKED
-	  || waitstatus->kind == TARGET_WAITKIND_VFORK_DONE);
+	  || waitstatus->kind == TARGET_WAITKIND_VFORK_DONE
+	  || waitstatus->kind == TARGET_WAITKIND_EXECD);
 }
 
 /* Wait for process, returns status.  */
@@ -3286,7 +3367,8 @@  linux_wait_1 (ptid_t ptid,
       ourstatus->value.sig = GDB_SIGNAL_0;
     }
   else if (current_thread->last_resume_kind == resume_stop
-	   && WSTOPSIG (w) != SIGSTOP)
+	   && WSTOPSIG (w) != SIGSTOP
+	   && !extended_event_reported (ourstatus))
     {
       /* A thread that has been requested to stop by GDB with vCont;t,
 	 but, it stopped for other reasons.  */
@@ -3404,6 +3486,7 @@  static void
 send_sigstop (struct lwp_info *lwp)
 {
   int pid;
+  int ret;
 
   pid = lwpid_of (get_lwp_thread (lwp));
 
@@ -3421,7 +3504,20 @@  send_sigstop (struct lwp_info *lwp)
     debug_printf ("Sending sigstop to lwp %d\n", pid);
 
   lwp->stop_expected = 1;
-  kill_lwp (pid, SIGSTOP);
+  errno = 0;
+  ret = kill_lwp (pid, SIGSTOP);
+  if (ret == -1 && errno == ESRCH)
+    {
+      /* If the kill fails with "No such process", on GNU/Linux we know
+	 that the LWP has vanished - it is not a zombie, it is gone.
+	 This is because a thread that was not the thread group leader
+	 called exec and took over the leader's lwp.  */
+      delete_lwp (lwp);
+      set_desired_thread (0);
+
+      if (debug_threads)
+	debug_printf ("send_sigstop: lwp %d has vanished\n", pid);
+    }
 }
 
 static int
@@ -5594,6 +5690,14 @@  linux_supports_vfork_events (void)
   return linux_supports_tracefork ();
 }
 
+/* Check if exec events are supported.  */
+
+static int
+linux_supports_exec_events (void)
+{
+  return linux_supports_traceexec ();
+}
+
 /* Callback for 'find_inferior'.  Set the (possibly changed) ptrace
    options for the specified lwp.  */
 
@@ -6681,6 +6785,7 @@  static struct target_ops linux_target_ops = {
   linux_supports_multi_process,
   linux_supports_fork_events,
   linux_supports_vfork_events,
+  linux_supports_exec_events,
   linux_handle_new_gdb_connection,
 #ifdef USE_THREAD_DB
   thread_db_handle_monitor_command,
diff --git a/gdb/gdbserver/lynx-low.c b/gdb/gdbserver/lynx-low.c
index ee7b28a..fd89869 100644
--- a/gdb/gdbserver/lynx-low.c
+++ b/gdb/gdbserver/lynx-low.c
@@ -764,6 +764,7 @@  static struct target_ops lynx_target_ops = {
   NULL,  /* supports_multi_process */
   NULL,  /* supports_fork_events */
   NULL,  /* supports_vfork_events */
+  NULL,  /* supports_exec_events */
   NULL,  /* handle_new_gdb_connection */
   NULL,  /* handle_monitor_command */
 };
diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
index bb31456..79d5ee8 100644
--- a/gdb/gdbserver/remote-utils.c
+++ b/gdb/gdbserver/remote-utils.c
@@ -1116,6 +1116,7 @@  prepare_resume_reply (char *buf, ptid_t ptid,
     case TARGET_WAITKIND_STOPPED:
     case TARGET_WAITKIND_FORKED:
     case TARGET_WAITKIND_VFORKED:
+    case TARGET_WAITKIND_EXECD:
       {
 	struct thread_info *saved_thread;
 	const char **regp;
@@ -1133,6 +1134,25 @@  prepare_resume_reply (char *buf, ptid_t ptid,
 	    buf = write_ptid (buf, status->value.related_pid);
 	    strcat (buf, ";");
 	  }
+	else if ((status->kind == TARGET_WAITKIND_EXECD) && multi_process)
+	  {
+	    enum gdb_signal signal = GDB_SIGNAL_TRAP;
+	    const char *event = "exec";
+	    char hexified_pathname[PATH_MAX];
+
+	    sprintf (buf, "T%02x%s:", signal, event);
+	    buf += strlen (buf);
+
+	    /* Encode pathname to hexified format.  */
+	    bin2hex ((const gdb_byte *) status->value.execd_pathname,
+		     hexified_pathname,
+		     strlen (status->value.execd_pathname));
+
+	    sprintf (buf, "%s;", hexified_pathname);
+	    xfree (status->value.execd_pathname);
+	    status->value.execd_pathname = NULL;
+	    buf += strlen (buf);
+	  }
 	else
 	  sprintf (buf, "T%02x", status->value.sig);
 
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 7e388dd..2b98d7c 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -59,6 +59,7 @@  int run_once;
 int multi_process;
 int report_fork_events;
 int report_vfork_events;
+int report_exec_events;
 int non_stop;
 int swbreak_feature;
 int hwbreak_feature;
@@ -2107,6 +2108,12 @@  handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
 		  if (target_supports_vfork_events ())
 		    report_vfork_events = 1;
 		}
+	      if (strcmp (p, "exec-events+") == 0)
+		{
+		  /* GDB supports and wants exec events if possible.  */
+		  if (target_supports_exec_events ())
+		    report_exec_events = 1;
+		}
 	      else
 		target_process_qsupported (p);
 
@@ -2163,6 +2170,9 @@  handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
       if (target_supports_vfork_events ())
 	strcat (own_buf, ";vfork-events+");
 
+      if (target_supports_exec_events ())
+	strcat (own_buf, ";exec-events+");
+
       if (target_supports_non_stop ())
 	strcat (own_buf, ";QNonStop+");
 
@@ -3545,6 +3555,7 @@  captured_main (int argc, char *argv[])
       multi_process = 0;
       report_fork_events = 0;
       report_vfork_events = 0;
+      report_exec_events = 0;
       /* Be sure we're out of tfind mode.  */
       current_traceframe = -1;
       cont_thread = null_ptid;
diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h
index 09a5624..258909d 100644
--- a/gdb/gdbserver/server.h
+++ b/gdb/gdbserver/server.h
@@ -86,6 +86,7 @@  extern int run_once;
 extern int multi_process;
 extern int report_fork_events;
 extern int report_vfork_events;
+extern int report_exec_events;
 extern int non_stop;
 
 /* True if the "swbreak+" feature is active.  In that case, GDB wants
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index 9a40867..dd5eb3e 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -287,6 +287,9 @@  struct target_ops
   /* Returns true if vfork events are supported.  */
   int (*supports_vfork_events) (void);
 
+  /* Returns true if exec events are supported.  */
+  int (*supports_exec_events) (void);
+
   /* Allows target to re-initialize connection-specific settings.  */
   void (*handle_new_gdb_connection) (void);
 
@@ -458,6 +461,10 @@  int kill_inferior (int);
   (the_target->supports_vfork_events ? \
    (*the_target->supports_vfork_events) () : 0)
 
+#define target_supports_exec_events() \
+  (the_target->supports_exec_events ? \
+   (*the_target->supports_exec_events) () : 0)
+
 #define target_handle_new_gdb_connection()		 \
   do							 \
     {							 \
diff --git a/gdb/gdbserver/win32-low.c b/gdb/gdbserver/win32-low.c
index 64caf24..f7a3c0b 100644
--- a/gdb/gdbserver/win32-low.c
+++ b/gdb/gdbserver/win32-low.c
@@ -1831,6 +1831,7 @@  static struct target_ops win32_target_ops = {
   NULL, /* supports_multi_process */
   NULL, /* supports_fork_events */
   NULL, /* supports_vfork_events */
+  NULL, /* supports_exec_events */
   NULL, /* handle_new_gdb_connection */
   NULL, /* handle_monitor_command */
   NULL, /* core_of_thread */
diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
index 1a926f9..db3c4ce 100644
--- a/gdb/nat/linux-ptrace.c
+++ b/gdb/nat/linux-ptrace.c
@@ -527,9 +527,7 @@  ptrace_supports_feature (int ptrace_options)
 }
 
 /* Returns non-zero if PTRACE_EVENT_FORK is supported by ptrace,
-   0 otherwise.  Note that if PTRACE_EVENT_FORK is supported so is
-   PTRACE_EVENT_CLONE, PTRACE_EVENT_EXEC and PTRACE_EVENT_VFORK,
-   since they were all added to the kernel at the same time.  */
+   0 otherwise.  */
 
 int
 linux_supports_tracefork (void)
@@ -537,6 +535,15 @@  linux_supports_tracefork (void)
   return ptrace_supports_feature (PTRACE_O_TRACEFORK);
 }
 
+/* Returns non-zero if PTRACE_EVENT_EXEC is supported by ptrace,
+   0 otherwise.  */
+
+int
+linux_supports_traceexec (void)
+{
+  return ptrace_supports_feature (PTRACE_O_TRACEEXEC);
+}
+
 /* Returns non-zero if PTRACE_EVENT_CLONE is supported by ptrace,
    0 otherwise.  Note that if PTRACE_EVENT_CLONE is supported so is
    PTRACE_EVENT_FORK, PTRACE_EVENT_EXEC and PTRACE_EVENT_VFORK,
diff --git a/gdb/nat/linux-ptrace.h b/gdb/nat/linux-ptrace.h
index be6c395..6d534ee 100644
--- a/gdb/nat/linux-ptrace.h
+++ b/gdb/nat/linux-ptrace.h
@@ -161,6 +161,7 @@  extern void linux_check_ptrace_features (void);
 extern void linux_enable_event_reporting (pid_t pid, int attached);
 extern void linux_disable_event_reporting (pid_t pid);
 extern int linux_supports_tracefork (void);
+extern int linux_supports_traceexec (void);
 extern int linux_supports_traceclone (void);
 extern int linux_supports_tracevforkdone (void);
 extern int linux_supports_tracesysgood (void);
diff --git a/gdb/remote.c b/gdb/remote.c
index 9d97f6b..c0b3423 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -1366,6 +1366,9 @@  enum {
   /* Support for the Qbtrace-conf:pt:size packet.  */
   PACKET_Qbtrace_conf_pt_size,
 
+  /* Support for exec events.  */
+  PACKET_exec_event_feature,
+
   PACKET_MAX
 };
 
@@ -4162,6 +4165,8 @@  static const struct protocol_feature remote_protocol_features[] = {
     PACKET_fork_event_feature },
   { "vfork-events", PACKET_DISABLE, remote_supported_packet,
     PACKET_vfork_event_feature },
+  { "exec-events", PACKET_DISABLE, remote_supported_packet,
+    PACKET_exec_event_feature },
   { "Qbtrace-conf:pt:size", PACKET_DISABLE, remote_supported_packet,
     PACKET_Qbtrace_conf_pt_size }
 };
@@ -4250,6 +4255,9 @@  remote_query_supported (void)
 	  if (packet_set_cmd_state (PACKET_vfork_event_feature)
 	      != AUTO_BOOLEAN_FALSE)
 	    q = remote_query_supported_append (q, "vfork-events+");
+	  if (packet_set_cmd_state (PACKET_exec_event_feature)
+	      != AUTO_BOOLEAN_FALSE)
+	    q = remote_query_supported_append (q, "exec-events+");
 	}
 
       q = reconcat (q, "qSupported:", q, (char *) NULL);
@@ -5909,6 +5917,25 @@  Packet: '%s'\n"),
 	      event->ws.kind = TARGET_WAITKIND_VFORK_DONE;
 	      p = skip_to_semicolon (p1 + 1);
 	    }
+	  else if (strncmp (p, "exec", p1 - p) == 0)
+	    {
+	      ULONGEST pid;
+	      char pathname[PATH_MAX];
+
+	      p = unpack_varlen_hex (++p1, &pid);
+
+	      /* Save the pathname for event reporting and for
+		 the next run command.  */
+	      hex2bin (p1, (gdb_byte *) pathname, (p - p1)/2);
+	      /* Add the null terminator.  */
+	      pathname[(p - p1)/2] = '\0';
+	      /* This is freed during event handling.  */
+	      event->ws.value.execd_pathname = xstrdup (pathname);
+	      event->ws.kind = TARGET_WAITKIND_EXECD;
+	      /* Save the pathname for the next run command.  */
+	      xfree (remote_exec_file);
+	      remote_exec_file = xstrdup (pathname);
+	    }
 	  else
 	    {
 	      ULONGEST pnum;
@@ -12960,6 +12987,9 @@  Show the maximum size of the address (in bits) in a memory packet."), NULL,
   add_packet_config_cmd (&remote_protocol_packets[PACKET_Qbtrace_conf_pt_size],
        "Qbtrace-conf:pt:size", "btrace-conf-pt-size", 0);
 
+  add_packet_config_cmd (&remote_protocol_packets[PACKET_exec_event_feature],
+			 "exec-event-feature", "exec-event-feature", 0);
+
   /* Assert that we've registered "set remote foo-packet" commands
      for all packet configs.  */
   {