From patchwork Thu Jul 30 23:19:16 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Don Breazeal X-Patchwork-Id: 7945 Received: (qmail 15008 invoked by alias); 30 Jul 2015 23:19:44 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 14999 invoked by uid 89); 30 Jul 2015 23:19:43 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.3 required=5.0 tests=AWL, BAYES_40, RCVD_IN_DNSWL_LOW, SPF_PASS autolearn=ham version=3.3.2 X-HELO: relay1.mentorg.com Received: from relay1.mentorg.com (HELO relay1.mentorg.com) (192.94.38.131) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Thu, 30 Jul 2015 23:19:40 +0000 Received: from svr-orw-fem-04.mgc.mentorg.com ([147.34.97.41]) by relay1.mentorg.com with esmtp id 1ZKx6u-0004uW-SG from Don_Breazeal@mentor.com ; Thu, 30 Jul 2015 16:19:36 -0700 Received: from build4-lucid-cs (147.34.91.1) by svr-orw-fem-04.mgc.mentorg.com (147.34.97.41) with Microsoft SMTP Server id 14.3.224.2; Thu, 30 Jul 2015 16:19:36 -0700 Received: by build4-lucid-cs (Postfix, from userid 1905) id 21A8E41034; Thu, 30 Jul 2015 16:19:36 -0700 (PDT) From: Don Breazeal To: , Subject: [PATCH v2 1/5] Extended-remote follow exec Date: Thu, 30 Jul 2015 16:19:16 -0700 Message-ID: <1438298360-29594-2-git-send-email-donb@codesourcery.com> In-Reply-To: <1438298360-29594-1-git-send-email-donb@codesourcery.com> References: <1436996979-32350-1-git-send-email-donb@codesourcery.com> <1438298360-29594-1-git-send-email-donb@codesourcery.com> MIME-Version: 1.0 X-IsSubscribed: yes 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 Luis Machado * 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) : 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) : New member. * win32-low.c (win32_target_ops) : Initialize new member. gdb/ 2015-07-30 Don Breazeal Luis Machado * 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) 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. */ {