@@ -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,
@@ -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 */
};
@@ -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);
@@ -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;
@@ -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
@@ -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 \
{ \
@@ -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 */
@@ -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
@@ -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,
@@ -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);
@@ -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. */
{