From patchwork Sun Jun 4 22:18:02 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sergio Durigan Junior X-Patchwork-Id: 20787 Received: (qmail 129329 invoked by alias); 4 Jun 2017 22:18:23 -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 129090 invoked by uid 89); 4 Jun 2017 22:18:22 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-25.9 required=5.0 tests=BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_LAZY_DOMAIN_SECURITY, RP_MATCHES_RCVD, SPF_HELO_PASS autolearn=ham version=3.3.2 spammy=joining, start-up, Share X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Sun, 04 Jun 2017 22:18:05 +0000 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id CEBC2C0587CE for ; Sun, 4 Jun 2017 22:18:08 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com CEBC2C0587CE Authentication-Results: ext-mx08.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx08.extmail.prod.ext.phx2.redhat.com; spf=pass smtp.mailfrom=sergiodj@redhat.com DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.redhat.com CEBC2C0587CE Received: from psique.yyz.redhat.com (unused-10-15-17-193.yyz.redhat.com [10.15.17.193]) by smtp.corp.redhat.com (Postfix) with ESMTP id 49D82816DF; Sun, 4 Jun 2017 22:18:08 +0000 (UTC) From: Sergio Durigan Junior To: GDB Patches Cc: Pedro Alves , Sergio Durigan Junior Subject: [PATCH v7 3/4] Share fork_inferior et al with gdbserver Date: Sun, 4 Jun 2017 18:18:02 -0400 Message-Id: <20170604221803.17649-4-sergiodj@redhat.com> In-Reply-To: <20170604221803.17649-1-sergiodj@redhat.com> References: <1482464361-4068-1-git-send-email-sergiodj@redhat.com> <20170604221803.17649-1-sergiodj@redhat.com> X-IsSubscribed: yes This is the most important (and the biggest, sorry) patch of the series. It moves fork_inferior from gdb/fork-child.c to common/common-fork-child.c and makes all the necessary adjustments to both GDB and gdbserver to make sure everything works OK. There is no "most important change" with this patch; all changes are made in a progressive way, making sure that gdbserver had the necessary features while not breaking GDB at the same time. I decided to go ahead and implement a partial support for starting the inferior with a shell on gdbserver, although the full feature comes in the next patch. The user won't have the option to disable the startup-with-shell, and also won't be able to change which shell gdbserver will use (other than setting the $SHELL environment variable, that is). Everything is working as expected, and no regressions were present during the tests. gdb/ChangeLog: yyyy-mm-dd Sergio Durigan Junior * Makefile.in (HFILES_NO_SRCDIR): Add "common/common-inferior.h" and "nat/fork-inferior.h". * common/common-inferior.h: New file, with contents from "gdb/inferior.h". * commom/common-utils.c: Include "common-utils.h". (stringify_argv): New function. (trace_start_error): Moved from "fork-child.c". (trace_start_error_with_name): Likewise. * configure.nat: Add "fork-inferior.o" as a dependency for "*linux*", "fbsd*" and "nbsd*" hosts. * corefile.c (get_exec_file): Update comment. * darwin-nat.c (darwin_ptrace_him): Call "gdb_startup_inferior" instead of "startup_inferior". (darwin_create_inferior): Call "add_thread_silent" after "fork_inferior". * common/common-utils.h (gdb_flush_out_err): New prototype. (stringify_argv): Likewise. (trace_start_error): Likewise. (trace_start_error_with_name): Likewise. * fork-child.c: Cleanup unnecessary includes. (SHELL_FILE): Move to "common/common-fork-child.c". (environ): Likewise. (exec_wrapper): Initialize. (get_exec_wrapper): New function. (breakup_args): Move to "common/common-fork-child.c"; rename to "breakup_args_for_exec". (escape_bang_in_quoted_argument): Move to "common/common-fork-child.c". (saved_ui): New variable. (prefork_hook): New function. (postfork_hook): Likewise. (postfork_child_hook): Likewise. (gdb_startup_inferior): Likewise. (fork_inferior): Move to "common/common-fork-child.c". Update function to support gdbserver. (startup_inferior): Likewise. * gnu-nat.c (gnu_create_inferior): Call "gdb_startup_inferior" instead of "startup_inferior". Call "add_thread_silent" after "fork_inferior". * inf-ptrace.c: Include "nat/fork-inferior.h" and "utils.h". (inf_ptrace_create_inferior): Call "gdb_startup_inferior" instead of "startup_inferior". Call "add_thread_silent" after "fork_inferior". * inferior.h: Include "common-inferior.h". (trace_start_error): Move to "common/common-utils.h". (trace_start_error_with_name): Likewise. (fork_inferior): Move prototype to "nat/fork-inferior.h". (startup_inferior): Likewise. (gdb_startup_inferior): New prototype. * nat/fork-inferior.c: New file, with contents from "fork-child.c". * nat/fork-inferior.h: New file. * procfs.c (procfs_init_inferior): Call "gdb_startup_inferior" instead of "startup_inferior". Call "add_thread_silent" after "fork_inferior". * target.h (target_terminal_init): Move prototype to "target/target.h". (target_terminal_inferior): Likewise. (target_terminal_ours): Likewise. * target/target.h (target_terminal_init): New prototype, moved from "target.h". (target_terminal_inferior): Likewise. (target_terminal_ours): Likewise. * utils.c (gdb_flush_out_err): New function. gdb/gdbserver/ChangeLog: yyyy-mm-dd Sergio Durigan Junior * Makefile.in (SFILES): Add "nat/fork-inferior.o". * configure: Regenerate. * configure.ac: Adding object file related to safe_strerror to IPA_DEPFILES. * configure.srv (srv_linux_obj): Add "fork-inferior.o". (i[34567]86-*-lynxos*): Likewise. (spu*-*-*): Likewise. * linux-low.c: Include "common-inferior.h", "nat/fork-inferior.h" and "environ.h". (linux_ptrace_fun): New function. (linux_create_inferior): Adjust function prototype to reflect change on "target.h". Adjust function code to use "fork_inferior". * lynx-low.c: Include "common-inferior.h" and "nat/fork-inferior.h". (lynx_ptrace_fun): New function. (lynx_create_inferior): Adjust function prototype to reflect change on "target.h". Adjust function code to use "fork_inferior". * nto-low.c (nto_create_inferior): Adjust function prototype and code to reflect change on "target.h". Update comments. * server.c: Include "common-inferior.h", "nat/fork-inferior.h", "common-terminal.h" and "environ.h". (our_environ): New variable. (startup_with_shell): Likewise. (program_name): Likewise. (program_argv): Rename to... (program_args): ...this. (wrapper_argv): New variable. (start_inferior): Delete function. (get_exec_wrapper): New function. (get_exec_file): Likewise. (get_environ): Likewise. (prefork_hook): Likewise. (post_fork_inferior): Likewise. (postfork_hook): Likewise. (postfork_child_hook): Likewise. (handle_v_run): Update code to deal with arguments coming from the remote host. Update calls from "start_inferior" to "create_inferior". (captured_main): Likewise. Initialize environment variable. Call "have_job_control". * server.h: Include . (post_fork_inferior): New prototype. (get_environ): Likewise. * spu-low.c: Include "common-inferior.h" and "nat/fork-inferior.h". (spu_ptrace_fun): New function. (spu_create_inferior): Adjust function prototype to reflect change on "target.h". Adjust function code to use "fork_inferior". * target.c (target_terminal_init): New function. (target_terminal_inferior): Likewise. (target_terminal_ours): Likewise. * target.h: Include . (struct target_ops) : Update prototype. (create_inferior): Update macro. * utils.c (gdb_flush_out_err): New function. * win32-low.c (win32_create_inferior): Adjust function prototype and code to reflect change on "target.h". gdb/testsuite/ChangeLog: 2017-01-17 Sergio Durigan Junior * gdb.server/non-existing-program.exp: Update regex in order to reflect the fact that gdbserver is now using fork_inferior (with a shell) to startup the inferior. --- gdb/Makefile.in | 2 + gdb/common/common-inferior.h | 33 ++ gdb/common/common-utils.c | 48 ++ gdb/common/common-utils.h | 20 + gdb/configure.nat | 8 +- gdb/corefile.c | 4 +- gdb/darwin-nat.c | 16 +- gdb/fork-child.c | 629 +++------------------- gdb/gdbserver/Makefile.in | 1 + gdb/gdbserver/configure | 4 +- gdb/gdbserver/configure.ac | 4 +- gdb/gdbserver/configure.srv | 6 +- gdb/gdbserver/linux-low.c | 89 +-- gdb/gdbserver/lynx-low.c | 51 +- gdb/gdbserver/nto-low.c | 10 +- gdb/gdbserver/server.c | 250 ++++++--- gdb/gdbserver/server.h | 16 + gdb/gdbserver/spu-low.c | 45 +- gdb/gdbserver/target.c | 27 + gdb/gdbserver/target.h | 13 +- gdb/gdbserver/utils.c | 9 + gdb/gdbserver/win32-low.c | 23 +- gdb/gnu-nat.c | 8 +- gdb/inf-ptrace.c | 15 +- gdb/inferior.h | 33 +- gdb/nat/fork-inferior.c | 569 +++++++++++++++++++ gdb/nat/fork-inferior.h | 90 ++++ gdb/procfs.c | 7 +- gdb/target.h | 18 +- gdb/target/target.h | 14 + gdb/testsuite/gdb.server/non-existing-program.exp | 12 +- gdb/utils.c | 9 + 32 files changed, 1261 insertions(+), 822 deletions(-) create mode 100644 gdb/common/common-inferior.h create mode 100644 gdb/nat/fork-inferior.c create mode 100644 gdb/nat/fork-inferior.h diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 6bb44d7..642b711 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -1524,6 +1524,7 @@ HFILES_NO_SRCDIR = \ common/gdb_termios.h \ common/gdb_vecs.h \ common/gdb_wait.h \ + common/common-inferior.h \ common/host-defs.h \ common/print-utils.h \ common/ptid.h \ @@ -1564,6 +1565,7 @@ HFILES_NO_SRCDIR = \ nat/amd64-linux-siginfo.h \ nat/gdb_ptrace.h \ nat/gdb_thread_db.h \ + nat/fork-inferior.h \ nat/linux-btrace.h \ nat/linux-namespaces.h \ nat/linux-nat.h \ diff --git a/gdb/common/common-inferior.h b/gdb/common/common-inferior.h new file mode 100644 index 0000000..87c1300 --- /dev/null +++ b/gdb/common/common-inferior.h @@ -0,0 +1,33 @@ +/* Functions to deal with the inferior being executed on GDB or + GDBserver. + + Copyright (C) 1986-2017 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef COMMON_INFERIOR_H +#define COMMON_INFERIOR_H + +/* Return the exec wrapper to be used when starting the inferior, or NULL + otherwise. */ +extern const char *get_exec_wrapper (); + +/* Return the name of the executable file as a string. + ERR nonzero means get error if there is none specified; + otherwise return 0 in that case. */ +extern char *get_exec_file (int err); + +#endif /* ! COMMON_INFERIOR_H */ diff --git a/gdb/common/common-utils.c b/gdb/common/common-utils.c index e94fdc4..fbec0da 100644 --- a/gdb/common/common-utils.c +++ b/gdb/common/common-utils.c @@ -18,6 +18,7 @@ along with this program. If not, see . */ #include "common-defs.h" +#include "common-utils.h" #include "host-defs.h" #include @@ -328,3 +329,50 @@ free_vector_argv (std::vector &v) v.clear (); } + +/* See common/common-utils.h. */ + +std::string +stringify_argv (const std::vector &args) +{ + std::string ret; + + if (!args.empty ()) + { + for (auto s : args) + if (s != NULL) + { + ret += s; + ret += ' '; + } + + /* Erase the last whitespace. */ + ret.erase (ret.end () - 1); + } + + return ret; +} + +/* See common/common-inferior.h. */ + +void +trace_start_error (const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + warning ("Could not trace the inferior process.\nError: "); + vwarning (fmt, ap); + va_end (ap); + + gdb_flush_out_err (); + _exit (0177); +} + +/* See common/common-inferior.h. */ + +void +trace_start_error_with_name (const char *string) +{ + trace_start_error ("%s: %s", string, safe_strerror (errno)); +} diff --git a/gdb/common/common-utils.h b/gdb/common/common-utils.h index c331f0d..38505e0 100644 --- a/gdb/common/common-utils.h +++ b/gdb/common/common-utils.h @@ -108,4 +108,24 @@ extern const char *skip_to_space_const (const char *inp); freeing all the elements. */ extern void free_vector_argv (std::vector &v); +/* Given a vector of arguments ARGV, return a string equivalent to + joining all the arguments with a whitespace separating them. */ +extern std::string stringify_argv (const std::vector &argv); + +/* Flush both stdout and stderr. This function needs to be + implemented differently on GDB and GDBserver. */ +extern void gdb_flush_out_err (); + +/* Report an error that happened when starting to trace the inferior + (i.e., when the "traceme_fun" callback is called on fork_inferior) + and bail out. This function does not return. */ +extern void trace_start_error (const char *fmt, ...) + ATTRIBUTE_NORETURN; + +/* Like "trace_start_error", but the error message is constructed by + combining STRING with the system error message for errno. This + function does not return. */ +extern void trace_start_error_with_name (const char *string) + ATTRIBUTE_NORETURN; + #endif diff --git a/gdb/configure.nat b/gdb/configure.nat index e6c96da..e6da599 100644 --- a/gdb/configure.nat +++ b/gdb/configure.nat @@ -54,7 +54,7 @@ case ${gdb_host} in *linux*) NAT_FILE='config/nm-linux.h' - NATDEPFILES='inf-ptrace.o fork-child.o proc-service.o \ + NATDEPFILES='inf-ptrace.o fork-child.o fork-inferior.o proc-service.o \ linux-thread-db.o linux-nat.o linux-osdata.o linux-fork.o \ linux-procfs.o linux-ptrace.o linux-waitpid.o \ linux-personality.o linux-namespaces.o' @@ -62,15 +62,15 @@ case ${gdb_host} in LOADLIBES='-ldl $(RDYNAMIC)' ;; fbsd*) - NATDEPFILES='fork-child.o inf-ptrace.o fbsd-nat.o' + NATDEPFILES='fork-child.o fork-inferior.o inf-ptrace.o fbsd-nat.o' HAVE_NATIVE_GCORE_HOST=1 LOADLIBES='-lkvm' ;; nbsd*) - NATDEPFILES='fork-child.o inf-ptrace.o' + NATDEPFILES='fork-child.o fork-inferior.o inf-ptrace.o' ;; obsd*) - NATDEPFILES='fork-child.o inf-ptrace.o' + NATDEPFILES='fork-child.o fork-inferior.o inf-ptrace.o' ;; cygwin*) NATDEPFILES='x86-nat.o x86-dregs.o windows-nat.o' diff --git a/gdb/corefile.c b/gdb/corefile.c index 13a90b9..33eb4d1 100644 --- a/gdb/corefile.c +++ b/gdb/corefile.c @@ -170,9 +170,7 @@ validate_files (void) } } -/* Return the name of the executable file as a string. - ERR nonzero means get error if there is none specified; - otherwise return 0 in that case. */ +/* See common/common-inferior.h. */ char * get_exec_file (int err) diff --git a/gdb/darwin-nat.c b/gdb/darwin-nat.c index cba84ca..4330a60 100644 --- a/gdb/darwin-nat.c +++ b/gdb/darwin-nat.c @@ -1790,7 +1790,7 @@ darwin_ptrace_him (int pid) darwin_init_thread_list (inf); - startup_inferior (START_INFERIOR_TRAPS_EXPECTED); + gdb_startup_inferior (pid, START_INFERIOR_TRAPS_EXPECTED); } static void @@ -1833,13 +1833,23 @@ darwin_create_inferior (struct target_ops *ops, const std::string &allargs, char **env, int from_tty) { + pid_t pid; + ptid_t ptid; + /* Do the hard work. */ - fork_inferior (exec_file, allargs, env, darwin_ptrace_me, darwin_ptrace_him, - darwin_pre_ptrace, NULL, darwin_execvp); + pid = fork_inferior (exec_file, allargs, env, darwin_ptrace_me, + darwin_ptrace_him, darwin_pre_ptrace, NULL, + darwin_execvp); + ptid = pid_to_ptid (pid); /* Return now in case of error. */ if (ptid_equal (inferior_ptid, null_ptid)) return; + + /* We have something that executes now. We'll be running through + the shell at this point (if startup-with-shell is true), but the + pid shouldn't change. */ + add_thread_silent (ptid); } diff --git a/gdb/fork-child.c b/gdb/fork-child.c index c1b6f53..39e49b5 100644 --- a/gdb/fork-child.c +++ b/gdb/fork-child.c @@ -21,471 +21,55 @@ #include "defs.h" #include "inferior.h" +#include "gdbcmd.h" #include "terminal.h" -#include "target.h" -#include "gdb_wait.h" -#include "gdb_vfork.h" -#include "gdbcore.h" #include "gdbthread.h" -#include "command.h" /* for dont_repeat () */ -#include "gdbcmd.h" -#include "solib.h" -#include "filestuff.h" #include "top.h" -#include "signals-state-save-restore.h" #include "job-control.h" -#include -#include - -/* This just gets used as a default if we can't find SHELL. */ -#define SHELL_FILE "/bin/sh" - -extern char **environ; +#include "filestuff.h" +#include "nat/fork-inferior.h" +#include "common/common-inferior.h" -static char *exec_wrapper; +/* The exec-wrapper, if any, that will be used when starting the + inferior. */ -/* Build the argument vector for execv(3). */ +static char *exec_wrapper = NULL; -class execv_argv -{ -public: - /* EXEC_FILE is the file to run. ALLARGS is a string containing the - arguments to the program. If starting with a shell, SHELL_FILE - is the shell to run. Otherwise, SHELL_FILE is NULL. */ - execv_argv (const char *exec_file, const std::string &allargs, - const char *shell_file); - - /* Return a pointer to the built argv, in the type expected by - execv. The result is (only) valid for as long as this execv_argv - object is live. We return a "char **" because that's the type - that the execv functions expect. Note that it is guaranteed that - the execv functions do not modify the argv[] array nor the - strings to which the array point. */ - char **argv () - { - return const_cast (&m_argv[0]); - } - -private: - /* Disable copying. */ - execv_argv (const execv_argv &) = delete; - void operator= (const execv_argv &) = delete; - - /* Helper methods for constructing the argument vector. */ - - /* Used when building an argv for a straight execv call, without - going via the shell. */ - void init_for_no_shell (const char *exec_file, - const std::string &allargs); - - /* Used when building an argv for execing a shell that execs the - child program. */ - void init_for_shell (const char *exec_file, - const std::string &allargs, - const char *shell_file); - - /* The argument vector built. Holds non-owning pointers. Elements - either point to the strings passed to the execv_argv ctor, or - inside M_STORAGE. */ - std::vector m_argv; - - /* Storage. In the no-shell case, this contains a copy of the - arguments passed to the ctor, split by '\0'. In the shell case, - this contains the quoted shell command. I.e., SHELL_COMMAND in - {"$SHELL" "-c", SHELL_COMMAND, NULL}. */ - std::string m_storage; -}; - -/* Create argument vector for straight call to execvp. Breaks up - ALLARGS into an argument vector suitable for passing to execvp and - stores it in M_ARGV. E.g., on "run a b c d" this routine would get - as input the string "a b c d", and as output it would fill in - M_ARGV with the four arguments "a", "b", "c", "d". Each argument - in M_ARGV points to a substring of a copy of ALLARGS stored in - M_STORAGE. */ +/* See common/common-inferior.h. */ -void -execv_argv::init_for_no_shell (const char *exec_file, - const std::string &allargs) +const char * +get_exec_wrapper () { - - /* Save/work with a copy stored in our storage. The pointers pushed - to M_ARGV point directly into M_STORAGE, which is modified in - place with the necessary NULL terminators. This avoids N heap - allocations and string dups when 1 is sufficient. */ - std::string &args_copy = m_storage = allargs; - - m_argv.push_back (exec_file); - - for (size_t cur_pos = 0; cur_pos < args_copy.size ();) - { - /* Skip whitespace-like chars. */ - std::size_t pos = args_copy.find_first_not_of (" \t\n", cur_pos); - - if (pos != std::string::npos) - cur_pos = pos; - - /* Find the position of the next separator. */ - std::size_t next_sep = args_copy.find_first_of (" \t\n", cur_pos); - - if (next_sep == std::string::npos) - { - /* No separator found, which means this is the last - argument. */ - next_sep = args_copy.size (); - } - else - { - /* Replace the separator with a terminator. */ - args_copy[next_sep++] = '\0'; - } - - m_argv.push_back (&args_copy[cur_pos]); - - cur_pos = next_sep; - } - - /* NULL-terminate the vector. */ - m_argv.push_back (NULL); + return exec_wrapper; } -/* When executing a command under the given shell, return true if the - '!' character should be escaped when embedded in a quoted - command-line argument. */ - -static bool -escape_bang_in_quoted_argument (const char *shell_file) -{ - size_t shell_file_len = strlen (shell_file); - - /* Bang should be escaped only in C Shells. For now, simply check - that the shell name ends with 'csh', which covers at least csh - and tcsh. This should be good enough for now. */ - - if (shell_file_len < 3) - return false; +/* The ui structure that will be saved on 'prefork_hook' and + restored on 'postfork_hook'. */ +static struct ui *saved_ui = NULL; - if (shell_file[shell_file_len - 3] == 'c' - && shell_file[shell_file_len - 2] == 's' - && shell_file[shell_file_len - 1] == 'h') - return true; - - return false; -} - -/* See declaration. */ - -execv_argv::execv_argv (const char *exec_file, - const std::string &allargs, - const char *shell_file) -{ - if (shell_file == NULL) - init_for_no_shell (exec_file, allargs); - else - init_for_shell (exec_file, allargs, shell_file); -} - -/* See declaration. */ +/* See nat/fork-inferior.h. */ void -execv_argv::init_for_shell (const char *exec_file, - const std::string &allargs, - const char *shell_file) +prefork_hook (const char *args) { - /* We're going to call a shell. */ - bool escape_bang = escape_bang_in_quoted_argument (shell_file); - - /* We need to build a new shell command string, and make argv point - to it. So build it in the storage. */ - std::string &shell_command = m_storage; - - shell_command = "exec "; - - /* Add any exec wrapper. That may be a program name with arguments, - so the user must handle quoting. */ - if (exec_wrapper) - { - shell_command += exec_wrapper; - shell_command += ' '; - } - - /* Now add exec_file, quoting as necessary. */ - - /* Quoting in this style is said to work with all shells. But csh - on IRIX 4.0.1 can't deal with it. So we only quote it if we need - to. */ - bool need_to_quote; - const char *p = exec_file; - while (1) - { - switch (*p) - { - case '\'': - case '!': - case '"': - case '(': - case ')': - case '$': - case '&': - case ';': - case '<': - case '>': - case ' ': - case '\n': - case '\t': - need_to_quote = true; - goto end_scan; - - case '\0': - need_to_quote = false; - goto end_scan; - - default: - break; - } - ++p; - } - end_scan: - if (need_to_quote) - { - shell_command += '\''; - for (p = exec_file; *p != '\0'; ++p) - { - if (*p == '\'') - shell_command += "'\\''"; - else if (*p == '!' && escape_bang) - shell_command += "\\!"; - else - shell_command += *p; - } - shell_command += '\''; - } - else - shell_command += exec_file; - - shell_command += ' ' + allargs; - - /* If we decided above to start up with a shell, we exec the shell. - "-c" says to interpret the next arg as a shell command to - execute, and this command is "exec ". */ - m_argv.reserve (4); - m_argv.push_back (shell_file); - m_argv.push_back ("-c"); - m_argv.push_back (shell_command.c_str ()); - m_argv.push_back (NULL); -} - -/* See inferior.h. */ - -void -trace_start_error (const char *fmt, ...) -{ - va_list ap; - - va_start (ap, fmt); - fprintf_unfiltered (gdb_stderr, "Could not trace the inferior " - "process.\nError: "); - vfprintf_unfiltered (gdb_stderr, fmt, ap); - va_end (ap); - - gdb_flush (gdb_stderr); - _exit (0177); -} - -/* See inferior.h. */ - -void -trace_start_error_with_name (const char *string) -{ - trace_start_error ("%s: %s", string, safe_strerror (errno)); -} - -/* Start an inferior Unix child process and sets inferior_ptid to its - pid. EXEC_FILE is the file to run. ALLARGS is a string containing - the arguments to the program. ENV is the environment vector to - pass. SHELL_FILE is the shell file, or NULL if we should pick - one. EXEC_FUN is the exec(2) function to use, or NULL for the default - one. */ - -/* This function is NOT reentrant. Some of the variables have been - made static to ensure that they survive the vfork call. */ - -int -fork_inferior (const char *exec_file_arg, const std::string &allargs, - char **env, void (*traceme_fun) (void), - void (*init_trace_fun) (int), void (*pre_trace_fun) (void), - char *shell_file_arg, - void (*exec_fun)(const char *file, char * const *argv, - char * const *env)) -{ - int pid; - static char default_shell_file[] = SHELL_FILE; - /* Set debug_fork then attach to the child while it sleeps, to debug. */ - static int debug_fork = 0; - /* This is set to the result of setpgrp, which if vforked, will be visible - to you in the parent process. It's only used by humans for debugging. */ - static int debug_setpgrp = 657473; - static char *shell_file; - static const char *exec_file; - char **save_our_env; const char *inferior_io_terminal = get_inferior_io_terminal (); - struct inferior *inf; - int i; - int save_errno; - struct ui *save_ui; - - /* If no exec file handed to us, get it from the exec-file command - -- with a good, common error message if none is specified. */ - if (exec_file_arg == NULL) - exec_file = get_exec_file (1); - else - exec_file = exec_file_arg; - - /* 'startup_with_shell' is declared in inferior.h and bound to the - "set startup-with-shell" option. If 0, we'll just do a - fork/exec, no shell, so don't bother figuring out what shell. */ - if (startup_with_shell) - { - shell_file = shell_file_arg; - /* Figure out what shell to start up the user program under. */ - if (shell_file == NULL) - shell_file = getenv ("SHELL"); - if (shell_file == NULL) - shell_file = default_shell_file; - } - else - shell_file = NULL; - - /* Build the argument vector. */ - execv_argv child_argv (exec_file, allargs, shell_file); - /* Retain a copy of our environment variables, since the child will - replace the value of environ and if we're vforked, we have to - restore it. */ - save_our_env = environ; - - /* Likewise the current UI. */ - save_ui = current_ui; + gdb_assert (saved_ui == NULL); + /* Retain a copy of our UI, since the child will replace this value + and if we're vforked, we have to restore it. */ + saved_ui = current_ui; /* Tell the terminal handling subsystem what tty we plan to run on; it will just record the information for later. */ new_tty_prefork (inferior_io_terminal); +} - /* It is generally good practice to flush any possible pending stdio - output prior to doing a fork, to avoid the possibility of both - the parent and child flushing the same data after the fork. */ - gdb_flush (main_ui->m_gdb_stdout); - gdb_flush (main_ui->m_gdb_stderr); - - /* If there's any initialization of the target layers that must - happen to prepare to handle the child we're about fork, do it - now... */ - if (pre_trace_fun != NULL) - (*pre_trace_fun) (); - - /* Create the child process. Since the child process is going to - exec(3) shortly afterwards, try to reduce the overhead by - calling vfork(2). However, if PRE_TRACE_FUN is non-null, it's - likely that this optimization won't work since there's too much - work to do between the vfork(2) and the exec(3). This is known - to be the case on ttrace(2)-based HP-UX, where some handshaking - between parent and child needs to happen between fork(2) and - exec(2). However, since the parent is suspended in the vforked - state, this doesn't work. Also note that the vfork(2) call might - actually be a call to fork(2) due to the fact that autoconf will - ``#define vfork fork'' on certain platforms. */ - if (pre_trace_fun || debug_fork) - pid = fork (); - else - pid = vfork (); - - if (pid < 0) - perror_with_name (("vfork")); - - if (pid == 0) - { - /* Switch to the main UI, so that gdb_std{in/out/err} in the - child are mapped to std{in/out/err}. This makes it possible - to use fprintf_unfiltered/warning/error/etc. in the child - from here on. */ - current_ui = main_ui; - - /* Close all file descriptors except those that gdb inherited - (usually 0/1/2), so they don't leak to the inferior. Note - that this closes the file descriptors of all secondary - UIs. */ - close_most_fds (); - - if (debug_fork) - sleep (debug_fork); - - /* Create a new session for the inferior process, if necessary. - It will also place the inferior in a separate process group. */ - if (create_tty_session () <= 0) - { - /* No session was created, but we still want to run the inferior - in a separate process group. */ - debug_setpgrp = gdb_setpgid (); - if (debug_setpgrp == -1) - perror (_("setpgrp failed in child")); - } - - /* Ask the tty subsystem to switch to the one we specified - earlier (or to share the current terminal, if none was - specified). */ - new_tty (); - - /* Changing the signal handlers for the inferior after - a vfork can also change them for the superior, so we don't mess - with signals here. See comments in - initialize_signals for how we get the right signal handlers - for the inferior. */ - - /* "Trace me, Dr. Memory!" */ - (*traceme_fun) (); - - /* The call above set this process (the "child") as debuggable - by the original gdb process (the "parent"). Since processes - (unlike people) can have only one parent, if you are debugging - gdb itself (and your debugger is thus _already_ the - controller/parent for this child), code from here on out is - undebuggable. Indeed, you probably got an error message - saying "not parent". Sorry; you'll have to use print - statements! */ - - restore_original_signals_state (); - - /* There is no execlpe call, so we have to set the environment - for our child in the global variable. If we've vforked, this - clobbers the parent, but environ is restored a few lines down - in the parent. By the way, yes we do need to look down the - path to find $SHELL. Rich Pixley says so, and I agree. */ - environ = env; - - char **argv = child_argv.argv (); - - if (exec_fun != NULL) - (*exec_fun) (argv[0], &argv[0], env); - else - execvp (argv[0], &argv[0]); - - /* If we get here, it's an error. */ - save_errno = errno; - fprintf_unfiltered (gdb_stderr, "Cannot exec %s", argv[0]); - for (i = 1; argv[i] != NULL; i++) - fprintf_unfiltered (gdb_stderr, " %s", argv[i]); - fprintf_unfiltered (gdb_stderr, ".\n"); - fprintf_unfiltered (gdb_stderr, "Error: %s\n", - safe_strerror (save_errno)); - gdb_flush (gdb_stderr); - _exit (0177); - } - - /* Restore our environment in case a vforked child clob'd it. */ - environ = save_our_env; +/* See nat/fork-inferior.h. */ - /* Likewise the current UI. */ - current_ui = save_ui; +void +postfork_hook (pid_t pid) +{ + struct inferior *inf; if (!have_inferiors ()) init_thread_list (); @@ -494,147 +78,58 @@ fork_inferior (const char *exec_file_arg, const std::string &allargs, inferior_appeared (inf, pid); - /* Needed for wait_for_inferior stuff below. */ + /* Needed for wait_for_inferior stuff. */ inferior_ptid = pid_to_ptid (pid); - new_tty_postfork (); + gdb_assert (saved_ui != NULL); + current_ui = saved_ui; + saved_ui = NULL; - /* We have something that executes now. We'll be running through - the shell at this point, but the pid shouldn't change. Targets - supporting MT should fill this task's ptid with more data as soon - as they can. */ - add_thread_silent (inferior_ptid); - - /* Now that we have a child process, make it our target, and - initialize anything target-vector-specific that needs - initializing. */ - if (init_trace_fun) - (*init_trace_fun) (pid); - - /* We are now in the child process of interest, having exec'd the - correct program, and are poised at the first instruction of the - new program. */ - return pid; + new_tty_postfork (); } -/* Accept NTRAPS traps from the inferior. */ +/* See nat/fork-inferior.h. */ void -startup_inferior (int ntraps) +postfork_child_hook () { - int pending_execs = ntraps; - int terminal_initted = 0; - ptid_t resume_ptid; + /* This is set to the result of setpgrp, which if vforked, will be + visible to you in the parent process. It's only used by humans + for debugging. */ + static int debug_setpgrp = 657473; + + /* Make sure we switch to main_ui here in order to be able to + use the fprintf_unfiltered/warning/error functions. */ + current_ui = main_ui; - if (startup_with_shell) + /* Create a new session for the inferior process, if necessary. + It will also place the inferior in a separate process group. */ + if (create_tty_session () <= 0) { - /* One trap extra for exec'ing the shell. */ - pending_execs++; + /* No session was created, but we still want to run the inferior + in a separate process group. */ + debug_setpgrp = gdb_setpgid (); + if (debug_setpgrp == -1) + perror (_("setpgrp failed in child")); } - if (target_supports_multi_process ()) - resume_ptid = pid_to_ptid (ptid_get_pid (inferior_ptid)); - else - resume_ptid = minus_one_ptid; - - /* The process was started by the fork that created it, but it will - have stopped one instruction after execing the shell. Here we - must get it up to actual execution of the real program. */ + /* Ask the tty subsystem to switch to the one we specified + earlier (or to share the current terminal, if none was + specified). */ + new_tty (); +} - if (exec_wrapper) - pending_execs++; +/* See inferior.h. */ - while (1) - { - enum gdb_signal resume_signal = GDB_SIGNAL_0; - ptid_t event_ptid; - - struct target_waitstatus ws; - memset (&ws, 0, sizeof (ws)); - event_ptid = target_wait (resume_ptid, &ws, 0); - - if (ws.kind == TARGET_WAITKIND_IGNORE) - /* The inferior didn't really stop, keep waiting. */ - continue; - - switch (ws.kind) - { - case TARGET_WAITKIND_SPURIOUS: - case TARGET_WAITKIND_LOADED: - case TARGET_WAITKIND_FORKED: - case TARGET_WAITKIND_VFORKED: - case TARGET_WAITKIND_SYSCALL_ENTRY: - case TARGET_WAITKIND_SYSCALL_RETURN: - /* Ignore gracefully during startup of the inferior. */ - switch_to_thread (event_ptid); - break; - - case TARGET_WAITKIND_SIGNALLED: - target_terminal_ours (); - target_mourn_inferior (event_ptid); - error (_("During startup program terminated with signal %s, %s."), - gdb_signal_to_name (ws.value.sig), - gdb_signal_to_string (ws.value.sig)); - return; - - case TARGET_WAITKIND_EXITED: - target_terminal_ours (); - target_mourn_inferior (event_ptid); - if (ws.value.integer) - error (_("During startup program exited with code %d."), - ws.value.integer); - else - error (_("During startup program exited normally.")); - return; - - case TARGET_WAITKIND_EXECD: - /* Handle EXEC signals as if they were SIGTRAP signals. */ - xfree (ws.value.execd_pathname); - resume_signal = GDB_SIGNAL_TRAP; - switch_to_thread (event_ptid); - break; - - case TARGET_WAITKIND_STOPPED: - resume_signal = ws.value.sig; - switch_to_thread (event_ptid); - break; - } - - if (resume_signal != GDB_SIGNAL_TRAP) - { - /* Let shell child handle its own signals in its own way. */ - target_continue (resume_ptid, resume_signal); - } - else - { - /* We handle SIGTRAP, however; it means child did an exec. */ - if (!terminal_initted) - { - /* Now that the child has exec'd we know it has already - set its process group. On POSIX systems, tcsetpgrp - will fail with EPERM if we try it before the child's - setpgid. */ - - /* Set up the "saved terminal modes" of the inferior - based on what modes we are starting it with. */ - target_terminal_init (); - - /* Install inferior's terminal modes. */ - target_terminal_inferior (); - - terminal_initted = 1; - } - - if (--pending_execs == 0) - break; - - /* Just make it go on. */ - target_continue_no_signal (resume_ptid); - } - } +ptid_t +gdb_startup_inferior (pid_t pid, int num_traps) +{ + ptid_t ptid = startup_inferior (pid, num_traps, NULL, NULL); /* Mark all threads non-executing. */ - set_executing (resume_ptid, 0); + set_executing (ptid, 0); + + return ptid; } /* Implement the "unset exec-wrapper" command. */ diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in index d9f55de..834425d 100644 --- a/gdb/gdbserver/Makefile.in +++ b/gdb/gdbserver/Makefile.in @@ -218,6 +218,7 @@ SFILES = \ $(srcdir)/nat/linux-personality.c \ $(srcdir)/nat/mips-linux-watch.c \ $(srcdir)/nat/ppc-linux.c \ + $(srcdir)/nat/fork-inferior.c \ $(srcdir)/target/waitstatus.c DEPFILES = @GDBSERVER_DEPFILES@ diff --git a/gdb/gdbserver/configure b/gdb/gdbserver/configure index b314c41..96befef 100755 --- a/gdb/gdbserver/configure +++ b/gdb/gdbserver/configure @@ -8422,7 +8422,9 @@ fi if $want_ipa ; then if $have_ipa ; then - IPA_DEPFILES="$ipa_obj" + # Needed because safe_strerror's definition is host-dependent + strerror_obj="`echo $srv_host_obs | sed 's/\(.*-strerror\)\.o/\1-ipa.o/'`" + IPA_DEPFILES="$ipa_obj $strerror_obj" extra_libraries="$extra_libraries libinproctrace.so" else as_fn_error "inprocess agent not supported for this target" "$LINENO" 5 diff --git a/gdb/gdbserver/configure.ac b/gdb/gdbserver/configure.ac index 4ea7913..8aa85db 100644 --- a/gdb/gdbserver/configure.ac +++ b/gdb/gdbserver/configure.ac @@ -462,7 +462,9 @@ esac], if $want_ipa ; then if $have_ipa ; then - IPA_DEPFILES="$ipa_obj" + # Needed because safe_strerror's definition is host-dependent + strerror_obj="`echo $srv_host_obs | sed 's/\(.*-strerror\)\.o/\1-ipa.o/'`" + IPA_DEPFILES="$ipa_obj $strerror_obj" extra_libraries="$extra_libraries libinproctrace.so" else AC_MSG_ERROR([inprocess agent not supported for this target]) diff --git a/gdb/gdbserver/configure.srv b/gdb/gdbserver/configure.srv index d00d9e2..056ac27 100644 --- a/gdb/gdbserver/configure.srv +++ b/gdb/gdbserver/configure.srv @@ -43,7 +43,7 @@ srv_amd64_linux_xmlfiles="i386/amd64-linux.xml i386/amd64-avx-linux.xml i386/amd # Linux object files. This is so we don't have to repeat # these files over and over again. -srv_linux_obj="linux-low.o linux-osdata.o linux-procfs.o linux-ptrace.o linux-waitpid.o linux-personality.o linux-namespaces.o" +srv_linux_obj="linux-low.o linux-osdata.o linux-procfs.o linux-ptrace.o linux-waitpid.o linux-personality.o linux-namespaces.o fork-inferior.o" # Input is taken from the "${target}" variable. @@ -131,7 +131,7 @@ case "${target}" in ipa_obj="${ipa_i386_linux_regobj} linux-i386-ipa.o" ;; i[34567]86-*-lynxos*) srv_regobj="i386.o" - srv_tgtobj="lynx-low.o lynx-i386-low.o" + srv_tgtobj="lynx-low.o lynx-i386-low.o fork-inferior.o" srv_xmlfiles="i386/i386.xml" srv_xmlfiles="${srv_xmlfiles} i386/32bit-core.xml" srv_xmlfiles="${srv_xmlfiles} i386/32bit-sse.xml" @@ -338,7 +338,7 @@ case "${target}" in srv_linux_thread_db=yes ;; spu*-*-*) srv_regobj=reg-spu.o - srv_tgtobj="spu-low.o" + srv_tgtobj="spu-low.o fork-inferior.o" ;; tic6x-*-uclinux) srv_regobj="tic6x-c64xp-linux.o" srv_regobj="${srv_regobj} tic6x-c64x-linux.o" diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c index ea3c81b..ab6670b 100644 --- a/gdb/gdbserver/linux-low.c +++ b/gdb/gdbserver/linux-low.c @@ -47,6 +47,9 @@ #include "tracepoint.h" #include "hostio.h" #include +#include "common-inferior.h" +#include "nat/fork-inferior.h" +#include "environ.h" #ifndef ELFMAG0 /* Don't include here. If it got included by gdb_proc_service.h then ELFMAG0 will have been defined. If it didn't get included by @@ -946,59 +949,57 @@ add_lwp (ptid_t ptid) return lwp; } +/* Callback to be used when calling fork_inferior, responsible for + actually initiating the tracing of the inferior. */ + +static void +linux_ptrace_fun () +{ + if (ptrace (PTRACE_TRACEME, 0, (PTRACE_TYPE_ARG3) 0, + (PTRACE_TYPE_ARG4) 0) < 0) + trace_start_error_with_name ("ptrace"); + + if (setpgid (0, 0) < 0) + trace_start_error_with_name ("setpgid"); + + /* If GDBserver is connected to gdb via stdio, redirect the inferior's + stdout to stderr so that inferior i/o doesn't corrupt the connection. + Also, redirect stdin to /dev/null. */ + if (remote_connection_is_stdio ()) + { + if (close (0) < 0) + trace_start_error_with_name ("close"); + if (open ("/dev/null", O_RDONLY) < 0) + trace_start_error_with_name ("open"); + if (dup2 (2, 1) < 0) + trace_start_error_with_name ("dup2"); + if (write (2, "stdin/stdout redirected\n", + sizeof ("stdin/stdout redirected\n") - 1) < 0) + { + /* Errors ignored. */; + } + } +} + /* Start an inferior process and returns its pid. - ALLARGS is a vector of program-name and args. */ + PROGRAM is the name of the program to be started, and PROGRAM_ARGS + are its arguments. */ static int -linux_create_inferior (char *program, char **allargs) +linux_create_inferior (const char *program, + const std::vector &program_args) { struct lwp_info *new_lwp; int pid; ptid_t ptid; struct cleanup *restore_personality = maybe_disable_address_space_randomization (disable_randomization); + std::string str_program_args = stringify_argv (program_args); -#if defined(__UCLIBC__) && defined(HAS_NOMMU) - pid = vfork (); -#else - pid = fork (); -#endif - if (pid < 0) - perror_with_name ("fork"); - - if (pid == 0) - { - close_most_fds (); - ptrace (PTRACE_TRACEME, 0, (PTRACE_TYPE_ARG3) 0, (PTRACE_TYPE_ARG4) 0); - - setpgid (0, 0); - - /* If gdbserver is connected to gdb via stdio, redirect the inferior's - stdout to stderr so that inferior i/o doesn't corrupt the connection. - Also, redirect stdin to /dev/null. */ - if (remote_connection_is_stdio ()) - { - close (0); - open ("/dev/null", O_RDONLY); - dup2 (2, 1); - if (write (2, "stdin/stdout redirected\n", - sizeof ("stdin/stdout redirected\n") - 1) < 0) - { - /* Errors ignored. */; - } - } - - restore_original_signals_state (); - - execv (program, allargs); - if (errno == ENOENT) - execvp (program, allargs); - - fprintf (stderr, "Cannot exec %s: %s.\n", program, - strerror (errno)); - fflush (stderr); - _exit (0177); - } + pid = fork_inferior (program, + str_program_args.c_str (), + environ_vector (get_environ ()), linux_ptrace_fun, + NULL, NULL, NULL, NULL); do_cleanups (restore_personality); @@ -1008,6 +1009,8 @@ linux_create_inferior (char *program, char **allargs) new_lwp = add_lwp (ptid); new_lwp->must_set_ptrace_flags = 1; + post_fork_inferior (pid, program, startup_inferior); + return pid; } diff --git a/gdb/gdbserver/lynx-low.c b/gdb/gdbserver/lynx-low.c index d300aae..382d71c 100644 --- a/gdb/gdbserver/lynx-low.c +++ b/gdb/gdbserver/lynx-low.c @@ -28,6 +28,8 @@ #include "gdb_wait.h" #include #include "filestuff.h" +#include "common-inferior.h" +#include "nat/fork-inferior.h" int using_threads = 1; @@ -224,36 +226,43 @@ lynx_add_process (int pid, int attached) return proc; } +/* Callback used by fork_inferior to start tracing the inferior. */ + +static void +lynx_ptrace_fun () +{ + int pgrp; + + /* Switch child to its own process group so that signals won't + directly affect GDBserver. */ + pgrp = getpid(); + if (pgrp < 0) + trace_start_error_with_name ("pgrp"); + if (setpgid (0, pgrp) < 0) + trace_start_error_with_name ("setpgid"); + if (ioctl (0, TIOCSPGRP, &pgrp) < 0) + trace_start_error_with_name ("ioctl"); + if (lynx_ptrace (PTRACE_TRACEME, null_ptid, 0, 0, 0) < 0) + trace_start_error_with_name ("lynx_ptrace"); +} + /* Implement the create_inferior method of the target_ops vector. */ static int -lynx_create_inferior (char *program, char **allargs) +lynx_create_inferior (const char *program, + const std::vector &program_args) { int pid; + std::string str_program_args = stringify_argv (program_args); lynx_debug ("lynx_create_inferior ()"); - pid = fork (); - if (pid < 0) - perror_with_name ("fork"); + pid = fork_inferior (program, + str_program_args.c_str (), + environ_vector (get_environ ()), lynx_ptrace_fun, + NULL, NULL, NULL, NULL); - if (pid == 0) - { - int pgrp; - - close_most_fds (); - - /* Switch child to its own process group so that signals won't - directly affect gdbserver. */ - pgrp = getpid(); - setpgid (0, pgrp); - ioctl (0, TIOCSPGRP, &pgrp); - lynx_ptrace (PTRACE_TRACEME, null_ptid, 0, 0, 0); - execv (program, allargs); - fprintf (stderr, "Cannot exec %s: %s.\n", program, strerror (errno)); - fflush (stderr); - _exit (0177); - } + post_fork_inferior (pid, program, startup_inferior); lynx_add_process (pid, 0); /* Do not add the process thread just yet, as we do not know its tid. diff --git a/gdb/gdbserver/nto-low.c b/gdb/gdbserver/nto-low.c index 6229b4c..a5f1543 100644 --- a/gdb/gdbserver/nto-low.c +++ b/gdb/gdbserver/nto-low.c @@ -347,14 +347,17 @@ nto_read_auxv_from_initial_stack (CORE_ADDR initial_stack, return len_read; } -/* Start inferior specified by PROGRAM passing arguments ALLARGS. */ +/* Start inferior specified by PROGRAM, using PROGRAM_ARGS as its + arguments. */ static int -nto_create_inferior (char *program, char **allargs) +nto_create_inferior (const char *program, + const std::vector &program_args) { struct inheritance inherit; pid_t pid; sigset_t set; + std::string str_program_args = stringify_argv (program_args); TRACE ("%s %s\n", __func__, program); /* Clear any pending SIGUSR1's but keep the behavior the same. */ @@ -367,7 +370,8 @@ nto_create_inferior (char *program, char **allargs) memset (&inherit, 0, sizeof (inherit)); inherit.flags |= SPAWN_SETGROUP | SPAWN_HOLD; inherit.pgroup = SPAWN_NEWPGROUP; - pid = spawnp (program, 0, NULL, &inherit, allargs, 0); + pid = spawnp (program, 0, NULL, &inherit, + (char *) str_program_args.c_str (), 0); sigprocmask (SIG_BLOCK, &set, NULL); if (pid == -1) diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c index 69fcab1..5a76927 100644 --- a/gdb/gdbserver/server.c +++ b/gdb/gdbserver/server.c @@ -36,6 +36,20 @@ #include "dll.h" #include "hostio.h" #include +#include "common-inferior.h" +#include "nat/fork-inferior.h" +#include "job-control.h" +#include "environ.h" + +/* The environment to pass to the inferior when creating it. */ + +struct gdb_environ *our_environ = NULL; + +/* Start the inferior using a shell. */ + +/* We always try to start the inferior using a shell. */ + +int startup_with_shell = 1; /* The thread set with an `Hc' packet. `Hc' is deprecated in favor of `vCont'. Note the multi-process extensions made `vCont' a @@ -79,8 +93,9 @@ static int vCont_supported; space randomization feature before starting an inferior. */ int disable_randomization = 1; -static std::vector program_argv; -static std::vector wrapper_argv; +static char *program_name = NULL; +static std::vector program_args; +static std::string wrapper_argv; int pass_signals[GDB_SIGNAL_LAST]; int program_signals[GDB_SIGNAL_LAST]; @@ -238,25 +253,41 @@ target_running (void) return get_first_thread () != NULL; } -static int -start_inferior (char **argv) +/* See common/common-inferior.h. */ + +const char * +get_exec_wrapper () { - std::vector new_argv; + return wrapper_argv.size () > 0 ? wrapper_argv.c_str () : NULL; +} + +/* See common/common-inferior.h. */ - if (!wrapper_argv.empty ()) - new_argv.insert (new_argv.begin (), - wrapper_argv.begin (), - wrapper_argv.end ()); +char * +get_exec_file (int err) +{ + if (err && program_name == NULL) + error (_("No executable file specified.")); - for (int i = 0; argv[i] != NULL; ++i) - new_argv.push_back (argv[i]); + return program_name; +} - new_argv.push_back (NULL); +/* See server.h. */ + +struct gdb_environ * +get_environ () +{ + return our_environ; +} + +/* See nat/fork-inferior.h. */ +void +prefork_hook (const char *args) +{ if (debug_threads) { - for (int i = 0; i < new_argv.size (); ++i) - debug_printf ("new_argv[%d] = \"%s\"\n", i, new_argv[i]); + debug_printf ("args: %s\n", args); debug_flush (); } @@ -265,67 +296,63 @@ start_inferior (char **argv) signal (SIGTTIN, SIG_DFL); #endif - signal_pid = create_inferior (new_argv[0], &new_argv[0]); + /* Clear this so the backend doesn't get confused, thinking + CONT_THREAD died, and it needs to resume all threads. */ + cont_thread = null_ptid; +} - /* FIXME: we don't actually know at this point that the create - actually succeeded. We won't know that until we wait. */ - fprintf (stderr, "Process %s created; pid = %ld\n", argv[0], - signal_pid); - fflush (stderr); +/* See server.h. */ + +void +post_fork_inferior (int pid, const char *program, + ptid_t (*my_startup_inferior) (pid_t pid, int ntraps, + struct target_waitstatus + *mystatus, + ptid_t *myptid)) + +{ + /* Number of traps to be expected by startup_inferior. We always + expect at least one trap for the main executable. */ + int num_traps = START_INFERIOR_TRAPS_EXPECTED; #ifdef SIGTTOU signal (SIGTTOU, SIG_IGN); signal (SIGTTIN, SIG_IGN); terminal_fd = fileno (stderr); old_foreground_pgrp = tcgetpgrp (terminal_fd); - tcsetpgrp (terminal_fd, signal_pid); + tcsetpgrp (terminal_fd, pid); atexit (restore_old_foreground_pgrp); #endif - if (!wrapper_argv.empty ()) - { - ptid_t ptid = pid_to_ptid (signal_pid); - - last_ptid = mywait (pid_to_ptid (signal_pid), &last_status, 0, 0); - - if (last_status.kind == TARGET_WAITKIND_STOPPED) - { - do - { - target_continue_no_signal (ptid); - - last_ptid = mywait (pid_to_ptid (signal_pid), &last_status, 0, 0); - if (last_status.kind != TARGET_WAITKIND_STOPPED) - break; + my_startup_inferior (pid, num_traps, &last_status, &last_ptid); + current_thread->last_resume_kind = resume_stop; + current_thread->last_status = last_status; + signal_pid = pid; + target_post_create_inferior (); + fprintf (stderr, "Process %s created; pid = %d\n", program, pid); + fflush (stderr); +} - current_thread->last_resume_kind = resume_stop; - current_thread->last_status = last_status; - } - while (last_status.value.sig != GDB_SIGNAL_TRAP); - } - target_post_create_inferior (); - return signal_pid; - } +/* See nat/fork-inferior.h. */ - /* Wait till we are at 1st instruction in program, return new pid - (assuming success). */ - last_ptid = mywait (pid_to_ptid (signal_pid), &last_status, 0, 0); +void +postfork_hook (pid_t pid) +{ +} - /* At this point, the target process, if it exits, is stopped. Do not call - the function target_post_create_inferior if the process has already - exited, as the target implementation of the routine may rely on the - process being live. */ - if (last_status.kind != TARGET_WAITKIND_EXITED - && last_status.kind != TARGET_WAITKIND_SIGNALLED) - { - target_post_create_inferior (); - current_thread->last_resume_kind = resume_stop; - current_thread->last_status = last_status; - } - else - target_mourn_inferior (last_ptid); +/* See nat/fork-inferior.h. */ - return signal_pid; +void +postfork_child_hook () +{ + /* This is set to the result of setpgrp, which if vforked, will be + visible to you in the parent process. It's only used by humans + for debugging. */ + static int debug_setpgrp = 657473; + + debug_setpgrp = gdb_setpgid (); + if (debug_setpgrp == -1) + perror (_("setpgrp failed in child")); } static int @@ -2848,6 +2875,7 @@ handle_v_run (char *own_buf) { char *p, *next_p; std::vector new_argv; + char *new_program_name = NULL; int i, new_argc; new_argc = 0; @@ -2866,42 +2894,94 @@ handle_v_run (char *own_buf) if (i == 0 && p == next_p) { /* No program specified. */ - new_argv.push_back (NULL); + new_program_name = NULL; + } + else if (p == next_p) + { + /* Empty argument. */ + new_argv.push_back (xstrdup ("''")); } else { size_t len = (next_p - p) / 2; + /* ARG is the unquoted argument received via the RSP. */ char *arg = (char *) xmalloc (len + 1); + /* FULL_ARGS will contain the quoted version of ARG. */ + char *full_arg = (char *) xmalloc ((len + 1) * 2); + /* These are pointers used to navigate the strings above. */ + char *tmp_arg = arg; + char *tmp_full_arg = full_arg; + int need_quote = 0; hex2bin (p, (gdb_byte *) arg, len); arg[len] = '\0'; - new_argv.push_back (arg); - } + while (*tmp_arg != '\0') + { + switch (*tmp_arg) + { + case '\n': + /* Quote \n. */ + *tmp_full_arg = '\''; + ++tmp_full_arg; + need_quote = 1; + break; + + case '\'': + /* Quote single quote. */ + *tmp_full_arg = '\\'; + ++tmp_full_arg; + break; + + default: + break; + } + + *tmp_full_arg = *tmp_arg; + ++tmp_full_arg; + ++tmp_arg; + } + + if (need_quote) + *tmp_full_arg++ = '\''; + + /* Finish FULL_ARG and push it into the vector containing + the argv. */ + *tmp_full_arg = '\0'; + if (i == 0) + new_program_name = full_arg; + else + new_argv.push_back (full_arg); + xfree (arg); + } if (*next_p) next_p++; } new_argv.push_back (NULL); - if (new_argv[0] == NULL) + if (new_program_name == NULL) { /* GDB didn't specify a program to run. Use the program from the last run with the new argument list. */ - if (program_argv.empty ()) + if (program_name == NULL) { write_enn (own_buf); free_vector_argv (new_argv); return 0; } - - new_argv.push_back (xstrdup (program_argv[0])); + } + else + { + xfree (program_name); + program_name = new_program_name; } /* Free the old argv and install the new one. */ - free_vector_argv (program_argv); - program_argv = new_argv; + free_vector_argv (program_args); + program_args = new_argv; + + create_inferior (program_name, program_args); - start_inferior (&program_argv[0]); if (last_status.kind == TARGET_WAITKIND_STOPPED) { prepare_resume_reply (own_buf, last_ptid, &last_status); @@ -3527,10 +3607,17 @@ captured_main (int argc, char *argv[]) tmp = next_arg; while (*next_arg != NULL && strcmp (*next_arg, "--") != 0) { - wrapper_argv.push_back (*next_arg); + wrapper_argv += *next_arg; + wrapper_argv += ' '; next_arg++; } + if (wrapper_argv.size () > 0) + { + /* Erase the last whitespace. */ + wrapper_argv.erase (wrapper_argv.end () - 1); + } + if (next_arg == tmp || *next_arg == NULL) { gdbserver_usage (stderr); @@ -3666,8 +3753,13 @@ captured_main (int argc, char *argv[]) exit (1); } + /* Gather information about the environment. */ + our_environ = make_environ (); + init_environ (our_environ); + initialize_async_io (); initialize_low (); + have_job_control (); initialize_event_loop (); if (target_supports_tracepoints ()) initialize_tracepoint (); @@ -3681,12 +3773,13 @@ captured_main (int argc, char *argv[]) int i, n; n = argc - (next_arg - argv); - for (i = 0; i < n; i++) - program_argv.push_back (xstrdup (next_arg[i])); - program_argv.push_back (NULL); + program_name = xstrdup (next_arg[0]); + for (i = 1; i < n; i++) + program_args.push_back (xstrdup (next_arg[i])); + program_args.push_back (NULL); /* Wait till we are at first instruction in program. */ - start_inferior (&program_argv[0]); + create_inferior (program_name, program_args); /* We are now (hopefully) stopped at the first instruction of the target process. This assumes that the target process was @@ -4301,9 +4394,10 @@ process_serial_event (void) fprintf (stderr, "GDBserver restarting\n"); /* Wait till we are at 1st instruction in prog. */ - if (!program_argv.empty ()) + if (program_name != NULL) { - start_inferior (&program_argv[0]); + create_inferior (program_name, program_args); + if (last_status.kind == TARGET_WAITKIND_STOPPED) { /* Stopped at the first instruction of the target diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h index d5fee38..96566fe 100644 --- a/gdb/gdbserver/server.h +++ b/gdb/gdbserver/server.h @@ -148,4 +148,20 @@ extern int in_queued_stop_replies (ptid_t ptid); /* Definition for any syscall, used for unfiltered syscall reporting. */ #define ANY_SYSCALL (-2) +/* After fork_inferior has been called, we need to adjust a few + signals and call startup_inferior. This is done here. PID is the + pid of the new inferior, PROGRAM is its name, and + MY_STARTUP_INFERIOR is the function that should be called to start + the inferior and consume its first events. In most cases, this + function should be "startup_inferior" itself. */ +extern void post_fork_inferior (int pid, const char *program, + ptid_t (*my_startup_inferior) + (pid_t pid, int ntraps, + struct target_waitstatus *mystatus, + ptid_t *myptid)); + +/* Get the 'struct gdb_environ *' being used in the current + session. */ +extern struct gdb_environ *get_environ (); + #endif /* SERVER_H */ diff --git a/gdb/gdbserver/spu-low.c b/gdb/gdbserver/spu-low.c index 117b871..c71e8ba 100644 --- a/gdb/gdbserver/spu-low.c +++ b/gdb/gdbserver/spu-low.c @@ -27,6 +27,8 @@ #include #include "filestuff.h" #include "hostio.h" +#include "common-inferior.h" +#include "nat/fork-inferior.h" /* Some older glibc versions do not define this. */ #ifndef __WNOTHREAD @@ -261,36 +263,37 @@ spu_proc_xfer_spu (const char *annex, unsigned char *readbuf, return ret; } +/* Callback to be used when calling fork_inferior, responsible for + actually initiating the tracing of the inferior. */ + +static void +spu_ptrace_fun () +{ + if (ptrace (PTRACE_TRACEME, 0, 0, 0) < 0) + trace_start_error_with_name ("ptrace"); + if (setpgid (0, 0) < 0) + trace_start_error_with_name ("setpgid"); +} /* Start an inferior process and returns its pid. - ALLARGS is a vector of program-name and args. */ + PROGRAM is the name of the program to be started, and PROGRAM_ARGS + are its arguments. */ + static int -spu_create_inferior (char *program, char **allargs) +spu_create_inferior (const char *program, + const std::vector &program_argv) { int pid; ptid_t ptid; struct process_info *proc; + std::string str_program_args = stringify_argv (program_args); - pid = fork (); - if (pid < 0) - perror_with_name ("fork"); - - if (pid == 0) - { - close_most_fds (); - ptrace (PTRACE_TRACEME, 0, 0, 0); - - setpgid (0, 0); - - execv (program, allargs); - if (errno == ENOENT) - execvp (program, allargs); + pid = fork_inferior (program, + str_program_args.c_str (), + environ_vector (get_environ ()), spu_ptrace_fun, + NULL, NULL, NULL, NULL); - fprintf (stderr, "Cannot exec %s: %s.\n", program, - strerror (errno)); - fflush (stderr); - _exit (0177); - } + post_fork_inferior (pid, program, startup_inferior); proc = add_process (pid, 0); proc->tdesc = tdesc_spu; diff --git a/gdb/gdbserver/target.c b/gdb/gdbserver/target.c index fda72e8..7526463 100644 --- a/gdb/gdbserver/target.c +++ b/gdb/gdbserver/target.c @@ -387,3 +387,30 @@ default_breakpoint_kind_from_pc (CORE_ADDR *pcptr) (*the_target->sw_breakpoint_from_kind) (0, &size); return size; } + +/* See target/target.h. */ + +void +target_terminal_init () +{ + /* Placeholder needed because of fork_inferior. Not necessary on + GDBserver. */ +} + +/* See target/target.h. */ + +void +target_terminal_inferior () +{ + /* Placeholder needed because of fork_inferior. Not necessary on + GDBserver. */ +} + +/* See target/target.h. */ + +void +target_terminal_ours () +{ + /* Placeholder needed because of fork_inferior. Not necessary on + GDBserver. */ +} diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h index 3cc2bc4..be89258 100644 --- a/gdb/gdbserver/target.h +++ b/gdb/gdbserver/target.h @@ -28,6 +28,7 @@ #include "target/waitstatus.h" #include "mem-break.h" #include "btrace-common.h" +#include struct emit_ops; struct buffer; @@ -67,13 +68,13 @@ struct target_ops /* Start a new process. PROGRAM is a path to the program to execute. - ARGS is a standard NULL-terminated array of arguments, - to be passed to the inferior as ``argv''. + PROGRAM_ARGS is a standard NULL-terminated array of arguments, + to be passed to the inferior as ``argv'' (along with PROGRAM). Returns the new PID on success, -1 on failure. Registers the new process with the process list. */ - - int (*create_inferior) (char *program, char **args); + int (*create_inferior) (const char *program, + const std::vector &program_args); /* Do additional setup after a new process is created, including exec-wrapper completion. */ @@ -480,8 +481,8 @@ extern struct target_ops *the_target; void set_target_ops (struct target_ops *); -#define create_inferior(program, args) \ - (*the_target->create_inferior) (program, args) +#define create_inferior(program, program_args) \ + (*the_target->create_inferior) (program, program_args) #define target_post_create_inferior() \ do \ diff --git a/gdb/gdbserver/utils.c b/gdb/gdbserver/utils.c index 307d15a..3f96a6c 100644 --- a/gdb/gdbserver/utils.c +++ b/gdb/gdbserver/utils.c @@ -137,3 +137,12 @@ pfildes (gdb_fildes_t fd) return plongest (fd); #endif } + +/* See common/common-utils.h. */ + +void +gdb_flush_out_err () +{ + fflush (stdout); + fflush (stderr); +} diff --git a/gdb/gdbserver/win32-low.c b/gdb/gdbserver/win32-low.c index 7b09f4b..88f6911 100644 --- a/gdb/gdbserver/win32-low.c +++ b/gdb/gdbserver/win32-low.c @@ -608,13 +608,13 @@ create_process (const char *program, char *args, } /* Start a new process. - PROGRAM is a path to the program to execute. - ARGS is a standard NULL-terminated array of arguments, - to be passed to the inferior as ``argv''. + PROGRAM is the program name. + PROGRAM_ARGS is the vector containing the inferior's args. Returns the new PID on success, -1 on failure. Registers the new process with the process list. */ static int -win32_create_inferior (char *program, char **program_args) +win32_create_inferior (const char *program, + const std::vector &program_args) { #ifndef USE_WIN32API char real_path[PATH_MAX]; @@ -622,11 +622,12 @@ win32_create_inferior (char *program, char **program_args) #endif BOOL ret; DWORD flags; - char *args; int argslen; int argc; PROCESS_INFORMATION pi; DWORD err; + std::string str_program_args = stringify_argv (program_args); + char *args = (char *) str_program_args.c_str (); /* win32_wait needs to know we're not attaching. */ attaching = 0; @@ -652,18 +653,6 @@ win32_create_inferior (char *program, char **program_args) program = real_path; #endif - argslen = 1; - for (argc = 1; program_args[argc]; argc++) - argslen += strlen (program_args[argc]) + 1; - args = (char *) alloca (argslen); - args[0] = '\0'; - for (argc = 1; program_args[argc]; argc++) - { - /* FIXME: Can we do better about quoting? How does Cygwin - handle this? */ - strcat (args, " "); - strcat (args, program_args[argc]); - } OUTMSG2 (("Command line is \"%s\"\n", args)); #ifdef CREATE_NEW_PROCESS_GROUP diff --git a/gdb/gnu-nat.c b/gdb/gnu-nat.c index 6298103..d5e3841 100644 --- a/gdb/gnu-nat.c +++ b/gdb/gnu-nat.c @@ -2143,6 +2143,11 @@ gnu_create_inferior (struct target_ops *ops, pid = fork_inferior (exec_file, allargs, env, gnu_ptrace_me, NULL, NULL, NULL, NULL); + /* We have something that executes now. We'll be running through + the shell at this point (if startup-with-shell is true), but the + pid shouldn't change. */ + add_thread_silent (pid_to_ptid (pid)); + /* Attach to the now stopped child, which is actually a shell... */ inf_debug (inf, "attaching to child: %d", pid); @@ -2162,7 +2167,8 @@ gnu_create_inferior (struct target_ops *ops, thread_change_ptid (inferior_ptid, ptid_build (inf->pid, inf_pick_first_thread (), 0)); - startup_inferior (START_INFERIOR_TRAPS_EXPECTED); + gdb_startup_inferior (pid, START_INFERIOR_TRAPS_EXPECTED); + inf->pending_execs = 0; /* Get rid of the old shell threads. */ prune_threads (); diff --git a/gdb/inf-ptrace.c b/gdb/inf-ptrace.c index b19aaf9..2a01dd2 100644 --- a/gdb/inf-ptrace.c +++ b/gdb/inf-ptrace.c @@ -31,6 +31,8 @@ #include "inf-ptrace.h" #include "inf-child.h" #include "gdbthread.h" +#include "nat/fork-inferior.h" +#include "utils.h" @@ -93,7 +95,8 @@ inf_ptrace_create_inferior (struct target_ops *ops, const char *exec_file, const std::string &allargs, char **env, int from_tty) { - int pid; + pid_t pid; + ptid_t ptid; /* Do not change either targets above or the same target if already present. The reason is the target stack is shared across multiple inferiors. */ @@ -110,13 +113,19 @@ inf_ptrace_create_inferior (struct target_ops *ops, pid = fork_inferior (exec_file, allargs, env, inf_ptrace_me, NULL, NULL, NULL, NULL); + ptid = pid_to_ptid (pid); + /* We have something that executes now. We'll be running through + the shell at this point (if startup-with-shell is true), but the + pid shouldn't change. */ + add_thread_silent (ptid); + discard_cleanups (back_to); - startup_inferior (START_INFERIOR_TRAPS_EXPECTED); + gdb_startup_inferior (pid, START_INFERIOR_TRAPS_EXPECTED); /* On some targets, there must be some explicit actions taken after the inferior has been started up. */ - target_post_startup_inferior (pid_to_ptid (pid)); + target_post_startup_inferior (ptid); } #ifdef PT_GET_PROCESS_STATE diff --git a/gdb/inferior.h b/gdb/inferior.h index 7ee92ed..cdaeb11 100644 --- a/gdb/inferior.h +++ b/gdb/inferior.h @@ -45,6 +45,7 @@ struct inferior; #include "progspace.h" #include "registry.h" +#include "common-inferior.h" #include "symfile-add-flags.h" #include "common/refcounted-object.h" @@ -136,28 +137,10 @@ extern void child_terminal_init_with_pgrp (int pgrp); /* From fork-child.c */ -/* Report an error that happened when starting to trace the inferior - (i.e., when the "traceme_fun" callback is called on fork_inferior) - and bail out. This function does not return. */ - -extern void trace_start_error (const char *fmt, ...) - ATTRIBUTE_NORETURN; - -/* Like "trace_start_error", but the error message is constructed by - combining STRING with the system error message for errno. This - function does not return. */ - -extern void trace_start_error_with_name (const char *string) - ATTRIBUTE_NORETURN; - -extern int fork_inferior (const char *, const std::string &, char **, - void (*)(void), - void (*)(int), void (*)(void), char *, - void (*)(const char *, - char * const *, char * const *)); - - -extern void startup_inferior (int); +/* Helper function to call STARTUP_INFERIOR with PID and NUM_TRAPS. + This function already calls set_executing. Return the ptid_t from + STARTUP_INFERIOR. */ +extern ptid_t gdb_startup_inferior (pid_t pid, int num_traps); extern char *construct_inferior_arguments (int, char **); @@ -282,12 +265,6 @@ enum stop_kind #define ON_STACK 1 #define AT_ENTRY_POINT 4 -/* Number of traps that happen between exec'ing the shell to run an - inferior and when we finally get to the inferior code, not counting - the exec for the shell. This is 1 on all supported - implementations. */ -#define START_INFERIOR_TRAPS_EXPECTED 1 - struct private_inferior; /* Inferior process specific part of `struct infcall_control_state'. diff --git a/gdb/nat/fork-inferior.c b/gdb/nat/fork-inferior.c new file mode 100644 index 0000000..7fd93ef --- /dev/null +++ b/gdb/nat/fork-inferior.c @@ -0,0 +1,569 @@ +/* Fork a Unix child process, and set up to debug it, for GDB and GDBserver. + + Copyright (C) 1990-2017 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include "common-defs.h" +#include "fork-inferior.h" +#include "target/waitstatus.h" +#include "filestuff.h" +#include "target/target.h" +#include "common-inferior.h" +#include "common-gdbthread.h" +#include "signals-state-save-restore.h" +#include + +extern char **environ; + +/* Default shell file to be used if 'startup-with-shell' is set but + $SHELL is not. */ +#define SHELL_FILE "/bin/sh" + +/* Build the argument vector for execv(3). */ + +class execv_argv +{ +public: + /* EXEC_FILE is the file to run. ALLARGS is a string containing the + arguments to the program. If starting with a shell, SHELL_FILE + is the shell to run. Otherwise, SHELL_FILE is NULL. */ + execv_argv (const char *exec_file, const std::string &allargs, + const char *shell_file); + + /* Return a pointer to the built argv, in the type expected by + execv. The result is (only) valid for as long as this execv_argv + object is live. We return a "char **" because that's the type + that the execv functions expect. Note that it is guaranteed that + the execv functions do not modify the argv[] array nor the + strings to which the array point. */ + char **argv () + { + return const_cast (&m_argv[0]); + } + +private: + /* Disable copying. */ + execv_argv (const execv_argv &) = delete; + void operator= (const execv_argv &) = delete; + + /* Helper methods for constructing the argument vector. */ + + /* Used when building an argv for a straight execv call, without + going via the shell. */ + void init_for_no_shell (const char *exec_file, + const std::string &allargs); + + /* Used when building an argv for execing a shell that execs the + child program. */ + void init_for_shell (const char *exec_file, + const std::string &allargs, + const char *shell_file); + + /* The argument vector built. Holds non-owning pointers. Elements + either point to the strings passed to the execv_argv ctor, or + inside M_STORAGE. */ + std::vector m_argv; + + /* Storage. In the no-shell case, this contains a copy of the + arguments passed to the ctor, split by '\0'. In the shell case, + this contains the quoted shell command. I.e., SHELL_COMMAND in + {"$SHELL" "-c", SHELL_COMMAND, NULL}. */ + std::string m_storage; +}; + +/* Create argument vector for straight call to execvp. Breaks up + ALLARGS into an argument vector suitable for passing to execvp and + stores it in M_ARGV. E.g., on "run a b c d" this routine would get + as input the string "a b c d", and as output it would fill in + M_ARGV with the four arguments "a", "b", "c", "d". Each argument + in M_ARGV points to a substring of a copy of ALLARGS stored in + M_STORAGE. */ + +void +execv_argv::init_for_no_shell (const char *exec_file, + const std::string &allargs) +{ + + /* Save/work with a copy stored in our storage. The pointers pushed + to M_ARGV point directly into M_STORAGE, which is modified in + place with the necessary NULL terminators. This avoids N heap + allocations and string dups when 1 is sufficient. */ + std::string &args_copy = m_storage = allargs; + + m_argv.push_back (exec_file); + + for (size_t cur_pos = 0; cur_pos < args_copy.size ();) + { + /* Skip whitespace-like chars. */ + std::size_t pos = args_copy.find_first_not_of (" \t\n", cur_pos); + + if (pos != std::string::npos) + cur_pos = pos; + + /* Find the position of the next separator. */ + std::size_t next_sep = args_copy.find_first_of (" \t\n", cur_pos); + + if (next_sep == std::string::npos) + { + /* No separator found, which means this is the last + argument. */ + next_sep = args_copy.size (); + } + else + { + /* Replace the separator with a terminator. */ + args_copy[next_sep++] = '\0'; + } + + m_argv.push_back (&args_copy[cur_pos]); + + cur_pos = next_sep; + } + + /* NULL-terminate the vector. */ + m_argv.push_back (NULL); +} + +/* When executing a command under the given shell, return true if the + '!' character should be escaped when embedded in a quoted + command-line argument. */ + +static bool +escape_bang_in_quoted_argument (const char *shell_file) +{ + size_t shell_file_len = strlen (shell_file); + + /* Bang should be escaped only in C Shells. For now, simply check + that the shell name ends with 'csh', which covers at least csh + and tcsh. This should be good enough for now. */ + + if (shell_file_len < 3) + return false; + + if (shell_file[shell_file_len - 3] == 'c' + && shell_file[shell_file_len - 2] == 's' + && shell_file[shell_file_len - 1] == 'h') + return true; + + return false; +} + +/* See declaration. */ + +execv_argv::execv_argv (const char *exec_file, + const std::string &allargs, + const char *shell_file) +{ + if (shell_file == NULL) + init_for_no_shell (exec_file, allargs); + else + init_for_shell (exec_file, allargs, shell_file); +} + +/* See declaration. */ + +void +execv_argv::init_for_shell (const char *exec_file, + const std::string &allargs, + const char *shell_file) +{ + const char *exec_wrapper = get_exec_wrapper (); + + /* We're going to call a shell. */ + bool escape_bang = escape_bang_in_quoted_argument (shell_file); + + /* We need to build a new shell command string, and make argv point + to it. So build it in the storage. */ + std::string &shell_command = m_storage; + + shell_command = "exec "; + + /* Add any exec wrapper. That may be a program name with arguments, + so the user must handle quoting. */ + if (exec_wrapper != NULL) + { + shell_command += exec_wrapper; + shell_command += ' '; + } + + /* Now add exec_file, quoting as necessary. */ + + /* Quoting in this style is said to work with all shells. But csh + on IRIX 4.0.1 can't deal with it. So we only quote it if we need + to. */ + bool need_to_quote; + const char *p = exec_file; + while (1) + { + switch (*p) + { + case '\'': + case '!': + case '"': + case '(': + case ')': + case '$': + case '&': + case ';': + case '<': + case '>': + case ' ': + case '\n': + case '\t': + need_to_quote = true; + goto end_scan; + + case '\0': + need_to_quote = false; + goto end_scan; + + default: + break; + } + ++p; + } + end_scan: + if (need_to_quote) + { + shell_command += '\''; + for (p = exec_file; *p != '\0'; ++p) + { + if (*p == '\'') + shell_command += "'\\''"; + else if (*p == '!' && escape_bang) + shell_command += "\\!"; + else + shell_command += *p; + } + shell_command += '\''; + } + else + shell_command += exec_file; + + shell_command += ' ' + allargs; + + /* If we decided above to start up with a shell, we exec the shell. + "-c" says to interpret the next arg as a shell command to + execute, and this command is "exec ". */ + m_argv.reserve (4); + m_argv.push_back (shell_file); + m_argv.push_back ("-c"); + m_argv.push_back (shell_command.c_str ()); + m_argv.push_back (NULL); +} + +/* Return the shell that must be used to startup the inferior. The + first attempt is the environment variable SHELL; if it is not set, + then we default to SHELL_FILE. */ + +static const char * +get_startup_shell () +{ + static const char *ret; + + ret = getenv ("SHELL"); + if (ret == NULL) + ret = SHELL_FILE; + + return ret; +} + +/* See nat/fork-inferior.h. */ + +pid_t +fork_inferior (const char *exec_file_arg, const std::string &allargs, + char **env, void (*traceme_fun) (), + void (*init_trace_fun) (int), void (*pre_trace_fun) (), + const char *shell_file_arg, + void (*exec_fun)(const char *file, char * const *argv, + char * const *env)) +{ + pid_t pid; + /* Set debug_fork then attach to the child while it sleeps, to debug. */ + int debug_fork = 0; + const char *shell_file; + const char *exec_file; + char **save_our_env; + int i; + int save_errno; + + /* If no exec file handed to us, get it from the exec-file command + -- with a good, common error message if none is specified. */ + if (exec_file_arg == NULL) + exec_file = get_exec_file (1); + else + exec_file = exec_file_arg; + + /* 'startup_with_shell' is declared in inferior.h and bound to the + "set startup-with-shell" option. If 0, we'll just do a + fork/exec, no shell, so don't bother figuring out what shell. */ + if (startup_with_shell) + { + shell_file = shell_file_arg; + + /* Figure out what shell to start up the user program under. */ + if (shell_file == NULL) + shell_file = get_startup_shell (); + + gdb_assert (shell_file != NULL); + } + else + shell_file = NULL; + + /* Build the argument vector. */ + execv_argv child_argv (exec_file, allargs, shell_file); + + /* Retain a copy of our environment variables, since the child will + replace the value of environ and if we're vforked, we have to + restore it. */ + save_our_env = environ; + + /* Perform any necessary actions regarding to TTY before the + fork/vfork call. */ + prefork_hook (allargs.c_str ()); + + /* It is generally good practice to flush any possible pending stdio + output prior to doing a fork, to avoid the possibility of both + the parent and child flushing the same data after the fork. */ + gdb_flush_out_err (); + + /* If there's any initialization of the target layers that must + happen to prepare to handle the child we're about fork, do it + now... */ + if (pre_trace_fun != NULL) + (*pre_trace_fun) (); + + /* Create the child process. Since the child process is going to + exec(3) shortly afterwards, try to reduce the overhead by + calling vfork(2). However, if PRE_TRACE_FUN is non-null, it's + likely that this optimization won't work since there's too much + work to do between the vfork(2) and the exec(3). This is known + to be the case on ttrace(2)-based HP-UX, where some handshaking + between parent and child needs to happen between fork(2) and + exec(2). However, since the parent is suspended in the vforked + state, this doesn't work. Also note that the vfork(2) call might + actually be a call to fork(2) due to the fact that autoconf will + ``#define vfork fork'' on certain platforms. */ + if (pre_trace_fun || debug_fork) + pid = fork (); + else + pid = vfork (); + + if (pid < 0) + perror_with_name (("vfork")); + + if (pid == 0) + { + /* Close all file descriptors except those that gdb inherited + (usually 0/1/2), so they don't leak to the inferior. Note + that this closes the file descriptors of all secondary + UIs. */ + close_most_fds (); + + if (debug_fork) + sleep (debug_fork); + + /* Execute any necessary post-fork actions before we exec. */ + postfork_child_hook (); + + /* Changing the signal handlers for the inferior after + a vfork can also change them for the superior, so we don't mess + with signals here. See comments in + initialize_signals for how we get the right signal handlers + for the inferior. */ + + /* "Trace me, Dr. Memory!" */ + (*traceme_fun) (); + + /* The call above set this process (the "child") as debuggable + by the original gdb process (the "parent"). Since processes + (unlike people) can have only one parent, if you are debugging + gdb itself (and your debugger is thus _already_ the + controller/parent for this child), code from here on out is + undebuggable. Indeed, you probably got an error message + saying "not parent". Sorry; you'll have to use print + statements! */ + + restore_original_signals_state (); + + /* There is no execlpe call, so we have to set the environment + for our child in the global variable. If we've vforked, this + clobbers the parent, but environ is restored a few lines down + in the parent. By the way, yes we do need to look down the + path to find $SHELL. Rich Pixley says so, and I agree. */ + environ = env; + + char **argv = child_argv.argv (); + + if (exec_fun != NULL) + (*exec_fun) (argv[0], &argv[0], env); + else + execvp (argv[0], &argv[0]); + + /* If we get here, it's an error. */ + save_errno = errno; + warning ("Cannot exec %s", argv[0]); + + for (i = 1; argv[i] != NULL; i++) + warning (" %s", argv[i]); + + warning ("Error: %s\n", safe_strerror (save_errno)); + + _exit (0177); + } + + /* Restore our environment in case a vforked child clob'd it. */ + environ = save_our_env; + + postfork_hook (pid); + + /* Now that we have a child process, make it our target, and + initialize anything target-vector-specific that needs + initializing. */ + if (init_trace_fun) + (*init_trace_fun) (pid); + + /* We are now in the child process of interest, having exec'd the + correct program, and are poised at the first instruction of the + new program. */ + return pid; +} + +/* See nat/fork-inferior.h. */ + +ptid_t +startup_inferior (pid_t pid, int ntraps, + struct target_waitstatus *last_waitstatus, + ptid_t *last_ptid) +{ + int pending_execs = ntraps; + int terminal_initted = 0; + ptid_t resume_ptid; + + if (startup_with_shell) + { + /* One trap extra for exec'ing the shell. */ + pending_execs++; + } + + if (target_supports_multi_process ()) + resume_ptid = pid_to_ptid (pid); + else + resume_ptid = minus_one_ptid; + + /* The process was started by the fork that created it, but it will + have stopped one instruction after execing the shell. Here we + must get it up to actual execution of the real program. */ + if (get_exec_wrapper () != NULL) + pending_execs++; + + while (1) + { + enum gdb_signal resume_signal = GDB_SIGNAL_0; + ptid_t event_ptid; + + struct target_waitstatus ws; + memset (&ws, 0, sizeof (ws)); + event_ptid = target_wait (resume_ptid, &ws, 0); + + if (last_waitstatus != NULL) + *last_waitstatus = ws; + if (last_ptid != NULL) + *last_ptid = event_ptid; + + if (ws.kind == TARGET_WAITKIND_IGNORE) + /* The inferior didn't really stop, keep waiting. */ + continue; + + switch (ws.kind) + { + case TARGET_WAITKIND_SPURIOUS: + case TARGET_WAITKIND_LOADED: + case TARGET_WAITKIND_FORKED: + case TARGET_WAITKIND_VFORKED: + case TARGET_WAITKIND_SYSCALL_ENTRY: + case TARGET_WAITKIND_SYSCALL_RETURN: + /* Ignore gracefully during startup of the inferior. */ + switch_to_thread (event_ptid); + break; + + case TARGET_WAITKIND_SIGNALLED: + target_terminal_ours (); + target_mourn_inferior (event_ptid); + error (_("During startup program terminated with signal %s, %s."), + gdb_signal_to_name (ws.value.sig), + gdb_signal_to_string (ws.value.sig)); + return resume_ptid; + + case TARGET_WAITKIND_EXITED: + target_terminal_ours (); + target_mourn_inferior (event_ptid); + if (ws.value.integer) + error (_("During startup program exited with code %d."), + ws.value.integer); + else + error (_("During startup program exited normally.")); + return resume_ptid; + + case TARGET_WAITKIND_EXECD: + /* Handle EXEC signals as if they were SIGTRAP signals. */ + xfree (ws.value.execd_pathname); + resume_signal = GDB_SIGNAL_TRAP; + switch_to_thread (event_ptid); + break; + + case TARGET_WAITKIND_STOPPED: + resume_signal = ws.value.sig; + switch_to_thread (event_ptid); + break; + } + + if (resume_signal != GDB_SIGNAL_TRAP) + { + /* Let shell child handle its own signals in its own way. */ + target_continue (resume_ptid, resume_signal); + } + else + { + /* We handle SIGTRAP, however; it means child did an exec. */ + if (!terminal_initted) + { + /* Now that the child has exec'd we know it has already + set its process group. On POSIX systems, tcsetpgrp + will fail with EPERM if we try it before the child's + setpgid. */ + + /* Set up the "saved terminal modes" of the inferior + based on what modes we are starting it with. */ + target_terminal_init (); + + /* Install inferior's terminal modes. */ + target_terminal_inferior (); + + terminal_initted = 1; + } + + if (--pending_execs == 0) + break; + + /* Just make it go on. */ + target_continue_no_signal (resume_ptid); + } + } + + return resume_ptid; +} diff --git a/gdb/nat/fork-inferior.h b/gdb/nat/fork-inferior.h new file mode 100644 index 0000000..39bdd8d --- /dev/null +++ b/gdb/nat/fork-inferior.h @@ -0,0 +1,90 @@ +/* Functions and data responsible for forking the inferior process. + + Copyright (C) 1986-2017 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef FORK_INFERIOR_H +#define FORK_INFERIOR_H + +#include + +/* Number of traps that happen between exec'ing the shell to run an + inferior and when we finally get to the inferior code, not counting + the exec for the shell. This is 1 on all supported + implementations. */ +#define START_INFERIOR_TRAPS_EXPECTED 1 + +/* Start an inferior Unix child process and sets inferior_ptid to its + pid. EXEC_FILE is the file to run. ALLARGS is a string containing + the arguments to the program. ENV is the environment vector to + pass. SHELL_FILE is the shell file, or NULL if we should pick + one. EXEC_FUN is the exec(2) function to use, or NULL for the default + one. */ + +/* This function is NOT reentrant. Some of the variables have been + made static to ensure that they survive the vfork call. */ +extern pid_t fork_inferior (const char *exec_file_arg, + const std::string &allargs, + char **env, void (*traceme_fun) (), + void (*init_trace_fun) (int), + void (*pre_trace_fun) (), + const char *shell_file_arg, + void (*exec_fun) (const char *file, + char * const *argv, + char * const *env)); + +/* Accept NTRAPS traps from the inferior. + + Return the ptid of the inferior being started. */ +extern ptid_t startup_inferior (pid_t pid, int ntraps, + struct target_waitstatus *mystatus, + ptid_t *myptid); + +/* Whether to start up the debuggee under a shell. + + If startup-with-shell is set, GDB's "run" will attempt to start up + the debuggee under a shell. This also happens when using GDBserver + under extended remote mode. + + This is in order for argument-expansion to occur. E.g., + + (gdb) run * + + The "*" gets expanded by the shell into a list of files. + + While this is a nice feature, it may be handy to bypass the shell + in some cases. To disable this feature, do "set startup-with-shell + false". + + The catch-exec traps expected during start-up will be one more if + the target is started up with a shell. */ +extern int startup_with_shell; + +/* Perform any necessary tasks before a fork/vfork takes place. ARGS + is a string containing all the arguments received by the inferior. + This function is mainly used by fork_inferior. */ +extern void prefork_hook (const char *args); + +/* Perform any necessary tasks after a fork/vfork takes place. This + function is mainly used by fork_inferior. */ +extern void postfork_hook (pid_t pid); + +/* Perform any necessary tasks *on the child* after a fork/vfork takes + place. This function is mainly used by fork_inferior. */ +extern void postfork_child_hook (); + +#endif /* ! FORK_INFERIOR_H */ diff --git a/gdb/procfs.c b/gdb/procfs.c index adb2e84..b03809c 100644 --- a/gdb/procfs.c +++ b/gdb/procfs.c @@ -4377,7 +4377,7 @@ procfs_init_inferior (struct target_ops *ops, int pid) thread_change_ptid (pid_to_ptid (pid), ptid_build (pid, lwpid, 0)); - startup_inferior (START_INFERIOR_TRAPS_EXPECTED); + gdb_startup_inferior (pid, START_INFERIOR_TRAPS_EXPECTED); #ifdef SYS_syssgi /* On mips-irix, we need to stop the inferior early enough during @@ -4604,6 +4604,11 @@ procfs_create_inferior (struct target_ops *ops, const char *exec_file, pid = fork_inferior (exec_file, allargs, env, procfs_set_exec_trap, NULL, NULL, shell_file, NULL); + /* We have something that executes now. We'll be running through + the shell at this point (if startup-with-shell is true), but the + pid shouldn't change. */ + add_thread_silent (pid_to_ptid (pid)); + procfs_init_inferior (ops, pid); } diff --git a/gdb/target.h b/gdb/target.h index a971adf..083d2bc 100644 --- a/gdb/target.h +++ b/gdb/target.h @@ -1538,16 +1538,8 @@ extern int target_terminal_is_inferior (void); extern int target_terminal_is_ours (void); -/* Initialize the terminal settings we record for the inferior, - before we actually run the inferior. */ - -extern void target_terminal_init (void); - -/* Put the inferior's terminal settings into effect. This is - preparation for starting or resuming the inferior. This is a no-op - unless called with the main UI as current UI. */ - -extern void target_terminal_inferior (void); +/* For target_terminal_init, target_terminal_inferior and + target_terminal_ours, see target/target.h. */ /* Put some of our terminal settings into effect, enough to get proper results from our output, but do not change into or out of RAW mode @@ -1557,12 +1549,6 @@ extern void target_terminal_inferior (void); extern void target_terminal_ours_for_output (void); -/* Put our terminal settings into effect. First record the inferior's - terminal settings so they can be restored properly later. This is - a no-op unless called with the main UI as current UI. */ - -extern void target_terminal_ours (void); - /* Return true if the target stack has a non-default "to_terminal_ours" method. */ diff --git a/gdb/target/target.h b/gdb/target/target.h index 582852c..0528766 100644 --- a/gdb/target/target.h +++ b/gdb/target/target.h @@ -95,4 +95,18 @@ extern void target_mourn_inferior (ptid_t ptid); extern int target_supports_multi_process (void); +/* Initialize the terminal settings we record for the inferior, + before we actually run the inferior. */ +extern void target_terminal_init (); + +/* Put the inferior's terminal settings into effect. This is + preparation for starting or resuming the inferior. This is a no-op + unless called with the main UI as current UI. */ +extern void target_terminal_inferior (); + +/* Put our terminal settings into effect. First record the inferior's + terminal settings so they can be restored properly later. This is + a no-op unless called with the main UI as current UI. */ +extern void target_terminal_ours (); + #endif /* TARGET_COMMON_H */ diff --git a/gdb/testsuite/gdb.server/non-existing-program.exp b/gdb/testsuite/gdb.server/non-existing-program.exp index d404564..68d4d53 100644 --- a/gdb/testsuite/gdb.server/non-existing-program.exp +++ b/gdb/testsuite/gdb.server/non-existing-program.exp @@ -39,8 +39,16 @@ set spawn_id [remote_spawn target "$gdbserver stdio non-existing-program"] set msg "gdbserver exits cleanly" set saw_exiting 0 expect { - # This is what we get on ptrace-based targets. - -re "stdin/stdout redirected.*No program to debug\r\nExiting\r\n$" { + # This is what we get on ptrace-based targets with + # startup-with-shell disabled (e.g., when the SHELL variable is + # unset). + -re "stdin/stdout redirected.*gdbserver: Cannot exec non-existing-program\r\ngdbserver: Error: No such file or directory\r\n\r\nDuring startup program exited with code 127\.\r\nExiting\r\n$" { + set saw_exiting 1 + exp_continue + } + # Likewise, but with startup-with-shell enabled, which is the + # default behaviour. + -re "stdin/stdout redirected.*exec: non-existing-program: not found\r\nDuring startup program exited with code 127\.\r\nExiting\r\n$" { set saw_exiting 1 exp_continue } diff --git a/gdb/utils.c b/gdb/utils.c index c61557e..bd0a0d8 100644 --- a/gdb/utils.c +++ b/gdb/utils.c @@ -3391,6 +3391,15 @@ strip_leading_path_elements (const char *path, int n) return p; } +/* See common/common-utils.h. */ + +void +gdb_flush_out_err () +{ + gdb_flush (main_ui->m_gdb_stdout); + gdb_flush (main_ui->m_gdb_stderr); +} + /* Provide a prototype to silence -Wmissing-prototypes. */ extern initialize_file_ftype _initialize_utils;