aboutsummaryrefslogtreecommitdiff
path: root/gdb/nat
diff options
context:
space:
mode:
authorSergio Durigan Junior <sergiodj@redhat.com>2016-12-22 21:11:11 -0500
committerSergio Durigan Junior <sergiodj@redhat.com>2017-06-07 19:56:09 -0400
commit2090129c36c7e582943b7d300968d19b46160d84 (patch)
treed7abde0c9c147cb404a6a0794adb49c26c29b015 /gdb/nat
parent043a49349c713dc329a2dfc413b082c3826ecdb8 (diff)
downloadgdb-2090129c36c7e582943b7d300968d19b46160d84.zip
gdb-2090129c36c7e582943b7d300968d19b46160d84.tar.gz
gdb-2090129c36c7e582943b7d300968d19b46160d84.tar.bz2
Share fork_inferior et al with gdbserver
This is the most important (and the biggest, sorry) patch of the series. It moves fork_inferior from gdb/fork-child.c to nat/fork-inferior.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: 2017-06-07 Sergio Durigan Junior <sergiodj@redhat.com> Pedro Alves <palves@redhat.com> * 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. * common/common-utils.h (stringify_argv): New prototype. * 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". * 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. * gdbcore.h (get_exec_file): Remove declaration. * 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: 2017-06-07 Sergio Durigan Junior <sergiodj@redhat.com> Pedro Alves <palves@redhat.com> * Makefile.in (SFILES): Add "nat/fork-inferior.o". * configure: Regenerate. * configure.srv (srv_linux_obj): Add "fork-child.o" and "fork-inferior.o". (i[34567]86-*-lynxos*): Likewise. (spu*-*-*): Likewise. * fork-child.c: New file. * 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". (linux_request_interrupt): Delete "signal_pid". * 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". (terminal_fd): Moved to fork-child.c. (old_foreground_pgrp): Likewise. (restore_old_foreground_pgrp): Likewise. (last_status): Make it global. (last_ptid): Likewise. (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 (post_fork_inferior): New prototype. (get_environ): Likewise. (last_status): Declare. (last_ptid): Likewise. (signal_pid): 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 <vector>. (struct target_ops) <create_inferior>: 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-06-07 Sergio Durigan Junior <sergiodj@redhat.com> * 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.
Diffstat (limited to 'gdb/nat')
-rw-r--r--gdb/nat/fork-inferior.c595
-rw-r--r--gdb/nat/fork-inferior.h106
2 files changed, 701 insertions, 0 deletions
diff --git a/gdb/nat/fork-inferior.c b/gdb/nat/fork-inferior.c
new file mode 100644
index 0000000..0913409
--- /dev/null
+++ b/gdb/nat/fork-inferior.c
@@ -0,0 +1,595 @@
+/* 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 <http://www.gnu.org/licenses/>. */
+
+#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 <vector>
+
+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<char **> (&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<const char *> 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 <target-program> <args>". */
+ 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 !(defined(__UCLIBC__) && defined(HAS_NOMMU))
+ if (pre_trace_fun || debug_fork)
+ pid = fork ();
+ else
+#endif
+ 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;
+}
+
+/* See nat/fork-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 nat/fork-inferior.h. */
+
+void
+trace_start_error_with_name (const char *string)
+{
+ trace_start_error ("%s: %s", string, safe_strerror (errno));
+}
diff --git a/gdb/nat/fork-inferior.h b/gdb/nat/fork-inferior.h
new file mode 100644
index 0000000..10e3832
--- /dev/null
+++ b/gdb/nat/fork-inferior.h
@@ -0,0 +1,106 @@
+/* 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef FORK_INFERIOR_H
+#define FORK_INFERIOR_H
+
+#include <string>
+
+/* 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 ();
+
+/* 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 /* ! FORK_INFERIOR_H */