@@ -366,6 +366,14 @@ linux_add_process (int pid, int attached)
return proc;
}
+/* Find the main executable for the process with PID. */
+
+static
+char *linux_pid_to_exec_file (struct target_ops *self, int pid)
+{
+ return linux_proc_pid_to_exec_file (pid);
+}
+
/* Handle a GNU/Linux extended wait response. If we see a clone
event, we need to add the new lwp EVENT_CHILD to our list (and not
report the trap to higher layers). This function returns non-zero
@@ -562,6 +570,21 @@ handle_extended_wait (struct lwp_info *event_child, int *wstatp)
ptrace (PTRACE_CONT, lwpid, (PTRACE_TYPE_ARG3) 0, (PTRACE_TYPE_ARG4) 0);
return ret;
}
+ else if (event == PTRACE_EVENT_EXEC)
+ {
+ if (debug_threads)
+ {
+ debug_printf ("HEW: Got exec event from LWP %ld\n",
+ lwpid_of (event_thr));
+ }
+
+ event_child->waitstatus.kind = TARGET_WAITKIND_EXECD;
+ event_child->waitstatus.value.execd_pathname
+ = xstrdup (linux_pid_to_exec_file (NULL, lwpid_of (event_thr)));
+
+ /* Report the event. */
+ return 0;
+ }
internal_error (__FILE__, __LINE__, _("unknown ptrace event %d"), event);
}
@@ -2047,6 +2070,56 @@ linux_low_filter_event (ptid_t filter_ptid, int lwpid, int *wstatp)
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. On an exec,
+ the Linux kernel destroys all other threads (except the execing
+ one) in the thread group, and resets the execing thread's tid
+ to the tgid. No exit notification is sent for the execing
+ thread -- from the ptracer's perspective, it appears as though
+ the execing thread just vanishes. When they are available, we
+ use exit events (PTRACE_EVENT_EXIT) to detect thread exit
+ reliably. As soon as all other threads (if any) are reaped or
+ have reported their PTRACE_EVENT_EXIT events, the execing
+ thread changes it's tid to the tgid, and the previous (zombie)
+ leader vanishes, giving place to the "new" leader. The lwp
+ entry for the previous leader is deleted when we handle its
+ exit event, and we re-add the new one here. */
+
+ 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.\n", lwpid);
+
+ child_ptid = ptid_build (lwpid, lwpid, 0);
+ child = add_lwp (child_ptid);
+ child->stopped = 1;
+ current_thread = child->thread;
+
+ if (non_stop && stopping_threads == NOT_STOPPING_THREADS)
+ {
+ /* Make sure we delete the lwp entry for the 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 before reporting the event. */
+ stop_all_lwps (0, child);
+ unstop_all_lwps (0, child);
+ }
+ }
+
/* 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
@@ -2383,8 +2456,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);
@@ -2786,7 +2858,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. */
@@ -3413,6 +3486,7 @@ static void
send_sigstop (struct lwp_info *lwp)
{
int pid;
+ int ret;
pid = lwpid_of (get_lwp_thread (lwp));
@@ -3430,7 +3504,21 @@ 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 due to a thread other than the thread group leader
+ calling exec. See comments in linux_low_filter_event regarding
+ PTRACE_EVENT_EXEC. */
+ delete_lwp (lwp);
+ set_desired_thread (0);
+
+ if (debug_threads)
+ debug_printf ("send_sigstop: lwp %d has vanished\n", pid);
+ }
}
static int
@@ -6530,6 +6618,7 @@ initialize_low (void)
linux_ptrace_set_requested_options (PTRACE_O_TRACEFORK
| PTRACE_O_TRACEVFORK
| PTRACE_O_TRACEVFORKDONE
- | PTRACE_O_TRACEEXIT);
+ | PTRACE_O_TRACEEXIT
+ | PTRACE_O_TRACEEXEC);
linux_ptrace_check_options ();
}
@@ -1107,6 +1107,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;
@@ -1123,6 +1124,24 @@ prepare_resume_reply (char *buf, ptid_t ptid,
ptid_get_pid (status->value.related_pid),
ptid_get_lwp (status->value.related_pid));
}
+ 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);
@@ -686,7 +686,6 @@ linux_nat_pass_signals (struct target_ops *self,
/* Prototypes for local functions. */
static int stop_wait_callback (struct lwp_info *lp, void *data);
static int linux_thread_alive (ptid_t ptid);
-static char *linux_child_pid_to_exec_file (struct target_ops *self, int pid);
@@ -1793,6 +1792,14 @@ linux_handle_syscall_trap (struct lwp_info *lp, int stopping)
return 1;
}
+/* Find the main executable for the process with PID. */
+
+static
+char *linux_pid_to_exec_file (struct target_ops *self, int pid)
+{
+ return linux_proc_pid_to_exec_file (pid);
+}
+
/* Handle a GNU/Linux extended wait response. If we see a clone
event, we need to add the new LWP to our list (and not report the
trap to higher layers). This function returns non-zero if the
@@ -2015,7 +2022,7 @@ linux_handle_extended_wait (struct lwp_info *lp, int status,
ourstatus->kind = TARGET_WAITKIND_EXECD;
ourstatus->value.execd_pathname
- = xstrdup (linux_child_pid_to_exec_file (NULL, pid));
+ = xstrdup (linux_pid_to_exec_file (NULL, pid));
return 0;
}
@@ -3862,23 +3869,6 @@ linux_nat_thread_name (struct target_ops *self, struct thread_info *thr)
return result;
}
-/* Accepts an integer PID; Returns a string representing a file that
- can be opened to get the symbols for the child process. */
-
-static char *
-linux_child_pid_to_exec_file (struct target_ops *self, int pid)
-{
- static char buf[PATH_MAX];
- char name[PATH_MAX];
-
- xsnprintf (name, PATH_MAX, "/proc/%d/exe", pid);
- memset (buf, 0, PATH_MAX);
- if (readlink (name, buf, PATH_MAX - 1) <= 0)
- strcpy (buf, name);
-
- return buf;
-}
-
/* Implement the to_xfer_partial interface for memory reads using the /proc
filesystem. Because we can use a single read() call for /proc, this
can be much more efficient than banging away at PTRACE_PEEKTEXT,
@@ -4271,7 +4261,7 @@ linux_target_install_ops (struct target_ops *t)
t->to_insert_exec_catchpoint = linux_child_insert_exec_catchpoint;
t->to_remove_exec_catchpoint = linux_child_remove_exec_catchpoint;
t->to_set_syscall_catchpoint = linux_child_set_syscall_catchpoint;
- t->to_pid_to_exec_file = linux_child_pid_to_exec_file;
+ t->to_pid_to_exec_file = linux_pid_to_exec_file;
t->to_post_startup_inferior = linux_child_post_startup_inferior;
t->to_post_attach = linux_child_post_attach;
t->to_follow_fork = linux_child_follow_fork;
@@ -113,3 +113,20 @@ linux_proc_pid_is_zombie (pid_t pid)
{
return linux_proc_pid_has_state (pid, "Z (zombie)");
}
+
+/* Accepts an integer PID; Returns a string representing a file that
+ can be opened to get the symbols for the child process. */
+
+char *
+linux_proc_pid_to_exec_file (int pid)
+{
+ static char buf[PATH_MAX];
+ char name[PATH_MAX];
+
+ xsnprintf (name, PATH_MAX, "/proc/%d/exe", pid);
+ memset (buf, 0, PATH_MAX);
+ if (readlink (name, buf, PATH_MAX - 1) <= 0)
+ strcpy (buf, name);
+
+ return buf;
+}
@@ -40,4 +40,8 @@ extern int linux_proc_pid_is_stopped (pid_t pid);
extern int linux_proc_pid_is_zombie (pid_t pid);
+/* Return pathname of exec file for process with PID. */
+
+extern char *linux_proc_pid_to_exec_file (int pid);
+
#endif /* COMMON_LINUX_PROCFS_H */
@@ -488,6 +488,7 @@ linux_test_for_traceexit (int child_pid)
if (ret != 0)
return;
+
/* We don't know for sure that the feature is available; old
versions of PTRACE_SETOPTIONS ignored unknown options. So
see if the process exit will generate a PTRACE_EVENT_EXIT.
@@ -508,8 +509,12 @@ linux_test_for_traceexit (int child_pid)
if (ret == child_pid && WIFSTOPPED (status)
&& linux_ptrace_get_extended_event (status) == PTRACE_EVENT_EXIT)
{
- /* PTRACE_O_TRACEEXIT is supported. */
- available_ptrace_options |= PTRACE_O_TRACEEXIT;
+ /* PTRACE_O_TRACEEXIT is supported.
+
+ We use exit events to implement support for exec events.
+ Because exit events are supported, we can assume exec events
+ are also supported, so we add them as well. */
+ available_ptrace_options |= PTRACE_O_TRACEEXIT | PTRACE_O_TRACEEXEC;
}
}
}
@@ -5614,11 +5614,13 @@ remote_parse_stop_reply (char *buf, struct stop_reply *event)
pnum and set p1 to point to the character following it.
Otherwise p1 points to p. */
- /* If this packet is an awatch packet, don't parse the 'a'
- as a register number. */
+ /* If this packet has a stop reason string that starts
+ with a character that could be a hex digit, don't parse
+ it as a register number. */
if (strncmp (p, "awatch", strlen("awatch")) != 0
&& strncmp (p, "core", strlen ("core") != 0)
+ && strncmp (p, "exec", strlen ("exec") != 0)
&& strncmp (p, "fork", strlen ("fork") != 0))
{
/* Read the ``P'' register number. */
@@ -5691,6 +5693,25 @@ Packet: '%s'\n"),
event->ws.kind = TARGET_WAITKIND_VFORK_DONE;
p = p_temp;
}
+ 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
{
/* Silently skip unknown optional info. */