| /* Fork a Unix child process, and set up to debug it, for GDB and GDBserver. |
| |
| Copyright (C) 1990-2024 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 "fork-inferior.h" |
| #include "target/waitstatus.h" |
| #include "gdbsupport/filestuff.h" |
| #include "target/target.h" |
| #include "gdbsupport/common-inferior.h" |
| #include "gdbsupport/common-gdbthread.h" |
| #include "gdbsupport/pathstuff.h" |
| #include "gdbsupport/signals-state-save-restore.h" |
| #include "gdbsupport/gdb_tilde_expand.h" |
| #include "gdbsupport/gdb_signals.h" |
| #include <vector> |
| |
| extern char **environ; |
| |
| /* 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_COPY_AND_ASSIGN (execv_argv); |
| |
| /* 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); |
| } |
| |
| /* See nat/fork-inferior.h. */ |
| |
| pid_t |
| fork_inferior (const char *exec_file, const std::string &allargs, char **env, |
| traceme_ftype traceme_fun, init_trace_ftype init_trace_fun, |
| pre_trace_ftype pre_trace_fun, const char *shell_file_arg, |
| exec_ftype exec_fun) |
| { |
| pid_t pid; |
| /* Set debug_fork then attach to the child while it sleeps, to debug. */ |
| int debug_fork = 0; |
| const char *shell_file; |
| char **save_our_env; |
| int i; |
| int save_errno; |
| |
| gdb_assert (exec_file != nullptr); |
| |
| /* '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_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 (); |
| |
| /* Check if the user wants to set a different working directory for |
| the inferior. */ |
| std::string inferior_cwd = get_inferior_cwd (); |
| |
| if (!inferior_cwd.empty ()) |
| { |
| /* Expand before forking because between fork and exec, the child |
| process may only execute async-signal-safe operations. */ |
| inferior_cwd = gdb_tilde_expand (inferior_cwd); |
| } |
| |
| /* 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 (); |
| |
| /* Change to the requested working directory if the user |
| requested it. */ |
| if (!inferior_cwd.empty ()) |
| { |
| if (chdir (inferior_cwd.c_str ()) < 0) |
| trace_start_error_with_name (inferior_cwd.c_str ()); |
| } |
| |
| 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", 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 (process_stratum_target *proc_target, 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 = ptid_t (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; |
| 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 (proc_target, 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.sig ()), |
| gdb_signal_to_string (ws.sig ())); |
| return resume_ptid; |
| |
| case TARGET_WAITKIND_EXITED: |
| target_terminal::ours (); |
| target_mourn_inferior (event_ptid); |
| if (ws.exit_status ()) |
| error (_("During startup program exited with code %d."), |
| ws.exit_status ()); |
| else |
| error (_("During startup program exited normally.")); |
| return resume_ptid; |
| |
| case TARGET_WAITKIND_EXECD: |
| /* Handle EXEC signals as if they were SIGTRAP signals. */ |
| resume_signal = GDB_SIGNAL_TRAP; |
| switch_to_thread (proc_target, event_ptid); |
| break; |
| |
| case TARGET_WAITKIND_STOPPED: |
| resume_signal = ws.sig (); |
| switch_to_thread (proc_target, 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."); |
| 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)); |
| } |