[PATCH v2 1/5] Extended-remote follow exec

Don Breazeal
2015-07-30T23:19:44Z

This is an updated patch that implements support for multi-arch exec
(e.g. a 64-bit executable execs a 32-bit executable). The changes
from the previous version reinitialize the target architecture in both
gdbserver and gdb when an exec event is first reported. The architecture
re-init is done unconditionally for simplicity's sake, rather than
only doing it if the architecture of the new executable is different
from the existing target architecture.

In addition we make sure that the exec event message uses a process-style
ptid, so as to match the message emitted in the native case. This is the
same change that was done for fork event messages.

Updated commit message, ChangeLog, and patch follow.

Thanks
--Don

----
This patch implements 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 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
----------------
I. Basic Exec Events
Basic 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.

II. Multi-Arch Support
We re-use the lwp, thread, and process data structures of the original
execing inferior for the exec'd program. However, we don't know if the
architecture of the new executable is the same as that of the original
program. In order to ensure that the architecture is updated correctly,
we unconditionally update it after every exec event. In gdbserver, we
call a new wrapper function that call the target's arch_update function
for the specified thread. We also update the previously-invalidated
register cache, now that we know we will access the registers correctly
with the updated architecture, and we clear the process's r_debug
pointer to force a reset of the address of the solib event hook for the
(possibly) new architecture. The old solib breakpoint itself is
deleted in infrun.c:follow_exec.

linux_arch_setup_thread (event_thr);
event_lwp->stop_pc = get_pc (event_lwp);
proc = get_thread_process (event_thr);
proc->priv->r_debug = 0;

We also need to update the target architecture on the GDB side. This
must be done before it has a chance to try to access the registers, so
we do this in remote.c:remote_parse_stop_reply as soon as we see the
"exec" stop reason.

else if (strncmp (p, "exec", p1 - p) == 0)
{
---snip---
target_clear_description ();
exec_file_attach (remote_exec_file, 0);

III. Multi-Thread Support
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. There are two major issues to address because of
this.

First, 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, clobbering all the registers.

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

The second issue is that gdbserver must clean up any stale thread/lwp
structures before it eventually tries to stop all the threads. If it
doesn't, it will hang in sigsuspend, waiting for an event from a
non-existent thread.

For all-stop mode 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 aren't going to stop all the
lwps. 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,
both of which are resolved in subsequent patches in this patchset.

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

* linux-low.c (linux_arch_setup): Move in front of
handle_extended_wait.
(linux_arch_setup_thread): New function.
(handle_extended_wait): Handle exec events. Call
linux_arch_setup_thread.
(check_zombie_leaders): Do not check stopped threads.
(linux_low_ptrace_options): Add PTRACE_O_TRACEEXEC.
(linux_low_filter_event): Add lwp and thread for exec'ing
non-leader thread if leader thread has been deleted.
Refactor code into linux_arch_setup_thread and call it.
(linux_wait_for_event_filtered): Update comment.
(extended_event_reported): Add TARGET_WAITKIND_EXECD.
(linux_wait_1): Prevent clobbering extended event status.
(send_sigstop): Check return from kill_lwp and delete non-
existent lwps.
(linux_supports_exec_events): New function.
* lynx-low.c (lynx_target_ops) <supports_exec_events>:
Initialize new member.
* remote-utils.c (prepare_resume_reply): New stop reason 'exec'.
* server.c (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.
* win32-low.c (win32_target_ops) <supports_exec_events>:
Initialize new member.

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

* infrun.c (follow_exec): Use process-style ptid for
exec message.
* nat/linux-ptrace.c (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.
(remote_parse_stop_reply): Handle 'exec' stop reason.
(_initialize_remote): Call add_packet_config_cmd for remote
exec-events feature.

---
gdb/gdbserver/linux-low.c | 161 +++++++++++++++++++++++++++++++++++++------
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/infrun.c | 4 +-
gdb/nat/linux-ptrace.c | 13 +++-
gdb/nat/linux-ptrace.h | 1 +
gdb/remote.c | 36 ++++++++++
11 files changed, 230 insertions(+), 26 deletions(-)

diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 9bc9fa3..af4619f 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -413,6 +413,29 @@ linux_add_process (int pid, int attached)

static CORE_ADDR get_pc (struct lwp_info *lwp);

+/* Implement the arch_setup target_ops method. */
+
+static void
+linux_arch_setup (void)
+{
+ the_low_target.arch_setup ();
+}
+
+/* Call the target arch_setup function on THREAD. */
+
+static void
+linux_arch_setup_thread (struct thread_info *thread)
+{
+ struct thread_info *saved_thread;
+
+ saved_thread = current_thread;
+ current_thread = thread;
+
+ linux_arch_setup ();
+
+ current_thread = saved_thread;
+}
+
/* Handle a GNU/Linux extended wait response. If we see a clone
event, we need to add the new LWP to our list (and return 0 so as
not to report the trap to higher layers). */
@@ -554,6 +577,56 @@ 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. */
+ regcache = (struct regcache *) inferior_regcache_data (event_thr);
+ free_register_cache (regcache);
+ set_inferior_regcache_data (event_thr, NULL);
+
+ /* The new executable may be for a different architecture than
+ that of the execing process, so re-initialize the architecture.
+ The call to get_pc will refill the register cache. */
+ linux_arch_setup_thread (event_thr);
+ 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);
}
@@ -819,14 +892,6 @@ linux_create_inferior (char *program, char **allargs)
return pid;
}

-/* Implement the arch_setup target_ops method. */
-
-static void
-linux_arch_setup (void)
-{
- the_low_target.arch_setup ();
-}
-
/* Attach to an inferior process. Returns 0 on success, ERRNO on
error. */

@@ -1614,7 +1679,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)
@@ -2040,6 +2105,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;
}

@@ -2056,6 +2124,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
@@ -2113,17 +2213,10 @@ linux_low_filter_event (int lwpid, int wstat)
{
if (proc->attached)
{
- struct thread_info *saved_thread;
-
/* This needs to happen after we have attached to the
inferior and it is stopped for the first time, but
before we access any inferior registers. */
- saved_thread = current_thread;
- current_thread = thread;
-
- the_low_target.arch_setup ();
-
- current_thread = saved_thread;
+ linux_arch_setup_thread (thread);
}
else
{
@@ -2357,8 +2450,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);

@@ -2756,7 +2848,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. */
@@ -3301,7 +3394,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. */
@@ -3419,6 +3513,7 @@ static void
send_sigstop (struct lwp_info *lwp)
{
int pid;
+ int ret;

pid = lwpid_of (get_lwp_thread (lwp));

@@ -3436,7 +3531,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
@@ -5623,6 +5731,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. */

@@ -6711,6 +6827,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 5cf03be..f56d982 100644
--- a/gdb/gdbserver/lynx-low.c
+++ b/gdb/gdbserver/lynx-low.c
@@ -765,6 +765,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 2918770..ee0a044 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;
@@ -2109,6 +2110,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);

@@ -2165,6 +2172,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+");

@@ -3547,6 +3557,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 fefd8d1..83a55cd 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -290,6 +290,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);

@@ -468,6 +471,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 7ccb3dd..53f6696 100644
--- a/gdb/gdbserver/win32-low.c
+++ b/gdb/gdbserver/win32-low.c
@@ -1832,6 +1832,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/infrun.c b/gdb/infrun.c
index 445a612..249d867 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -1056,6 +1056,7 @@ follow_exec (ptid_t ptid, char *execd_pathname)
struct thread_info *th, *tmp;
struct inferior *inf = current_inferior ();
int pid = ptid_get_pid (ptid);
+ ptid_t process_ptid;

/* This is an exec event that we actually wish to pay attention to.
Refresh our symbol table to the newly exec'd program, remove any
@@ -1122,8 +1123,9 @@ follow_exec (ptid_t ptid, char *execd_pathname)
update_breakpoints_after_exec ();

/* What is this a.out's name? */
+ process_ptid = pid_to_ptid (pid);
printf_unfiltered (_("%s is executing new program: %s\n"),
- target_pid_to_str (inferior_ptid),
+ target_pid_to_str (process_ptid),
execd_pathname);

/* We've followed the inferior through an exec. Therefore, the
diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
index f097c8a..235dfba 100644
--- a/gdb/nat/linux-ptrace.c
+++ b/gdb/nat/linux-ptrace.c
@@ -528,9 +528,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)
@@ -538,6 +536,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 41f668c..c2ecf3c 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 4ac393b..1aeba30 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -1367,6 +1367,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
};

@@ -4187,6 +4190,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 }
};
@@ -4275,6 +4280,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);
@@ -5934,6 +5942,31 @@ 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);
+ /* Reset the architecture in case the new executable is
+ different from the execing executable. We need to
+ do this right now, before any register access, to
+ keep the client and remote architectures in sync. */
+ target_clear_description ();
+ exec_file_attach (remote_exec_file, 0);
+ }
else
{
ULONGEST pnum;
@@ -12983,6 +13016,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. */
{
--
1.8.1.1