/* Main code for remote server for GDB.
- Copyright (C) 1989-2016 Free Software Foundation, Inc.
+ Copyright (C) 1989-2017 Free Software Foundation, Inc.
This file is part of GDB.
#include "tracepoint.h"
#include "dll.h"
#include "hostio.h"
+#include <vector>
+#include "common-inferior.h"
+#include "job-control.h"
+#include "environ.h"
+
+#include "common/selftest.h"
+
+/* The environment to pass to the inferior when creating it. */
+
+static gdb_environ our_environ;
+
+/* 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
space randomization feature before starting an inferior. */
int disable_randomization = 1;
-static char **program_argv, **wrapper_argv;
+static char *program_name = NULL;
+static std::vector<char *> program_args;
+static std::string wrapper_argv;
int pass_signals[GDB_SIGNAL_LAST];
int program_signals[GDB_SIGNAL_LAST];
unsigned long signal_pid;
-#ifdef SIGTTOU
-/* A file descriptor for the controlling terminal. */
-int terminal_fd;
-
-/* TERMINAL_FD's original foreground group. */
-pid_t old_foreground_pgrp;
-
-/* Hand back terminal ownership to the original foreground group. */
-
-static void
-restore_old_foreground_pgrp (void)
-{
- tcsetpgrp (terminal_fd, old_foreground_pgrp);
-}
-#endif
-
/* Set if you want to disable optional thread related packets support
in gdbserver, for the sake of testing GDB against stubs that don't
support them. */
int disable_packet_qfThreadInfo;
/* Last status reported to GDB. */
-static struct target_waitstatus last_status;
-static ptid_t last_ptid;
+struct target_waitstatus last_status;
+ptid_t last_ptid;
char *own_buf;
static unsigned char *mem_buf;
prepare_resume_reply (own_buf, vstop->ptid, &vstop->status);
}
-struct notif_server notif_stop =
-{
- "vStopped", "Stop", NULL, vstop_notif_reply,
-};
-
-static int
-target_running (void)
-{
- return get_first_thread () != NULL;
-}
+/* QUEUE_iterate callback helper for in_queued_stop_replies. */
static int
-start_inferior (char **argv)
+in_queued_stop_replies_ptid (QUEUE (notif_event_p) *q,
+ QUEUE_ITER (notif_event_p) *iter,
+ struct notif_event *event,
+ void *data)
{
- char **new_argv = argv;
+ ptid_t filter_ptid = *(ptid_t *) data;
+ struct vstop_notif *vstop_event = (struct vstop_notif *) event;
- if (wrapper_argv != NULL)
- {
- int i, count = 1;
-
- for (i = 0; wrapper_argv[i] != NULL; i++)
- count++;
- for (i = 0; argv[i] != NULL; i++)
- count++;
- new_argv = XALLOCAVEC (char *, count);
- count = 0;
- for (i = 0; wrapper_argv[i] != NULL; i++)
- new_argv[count++] = wrapper_argv[i];
- for (i = 0; argv[i] != NULL; i++)
- new_argv[count++] = argv[i];
- new_argv[count] = NULL;
- }
+ if (ptid_match (vstop_event->ptid, filter_ptid))
+ return 0;
- if (debug_threads)
- {
- int i;
- for (i = 0; new_argv[i]; ++i)
- debug_printf ("new_argv[%d] = \"%s\"\n", i, new_argv[i]);
- debug_flush ();
- }
+ /* Don't resume fork children that GDB does not know about yet. */
+ if ((vstop_event->status.kind == TARGET_WAITKIND_FORKED
+ || vstop_event->status.kind == TARGET_WAITKIND_VFORKED)
+ && ptid_match (vstop_event->status.value.related_pid, filter_ptid))
+ return 0;
-#ifdef SIGTTOU
- signal (SIGTTOU, SIG_DFL);
- signal (SIGTTIN, SIG_DFL);
-#endif
+ return 1;
+}
- signal_pid = create_inferior (new_argv[0], new_argv);
+/* See server.h. */
- /* 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);
+int
+in_queued_stop_replies (ptid_t ptid)
+{
+ return !QUEUE_iterate (notif_event_p, notif_stop.queue,
+ in_queued_stop_replies_ptid, &ptid);
+}
-#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);
- atexit (restore_old_foreground_pgrp);
-#endif
+struct notif_server notif_stop =
+{
+ "vStopped", "Stop", NULL, vstop_notif_reply,
+};
- if (wrapper_argv != NULL)
- {
- ptid_t ptid = pid_to_ptid (signal_pid);
+static int
+target_running (void)
+{
+ return get_first_thread () != NULL;
+}
- last_ptid = mywait (pid_to_ptid (signal_pid), &last_status, 0, 0);
+/* See common/common-inferior.h. */
- if (last_status.kind == TARGET_WAITKIND_STOPPED)
- {
- do
- {
- target_continue_no_signal (ptid);
+const char *
+get_exec_wrapper ()
+{
+ return !wrapper_argv.empty () ? wrapper_argv.c_str () : NULL;
+}
- last_ptid = mywait (pid_to_ptid (signal_pid), &last_status, 0, 0);
- if (last_status.kind != TARGET_WAITKIND_STOPPED)
- break;
+/* See common/common-inferior.h. */
- 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;
- }
+char *
+get_exec_file (int err)
+{
+ if (err && program_name == NULL)
+ error (_("No executable file specified."));
- /* 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);
+ return program_name;
+}
- /* 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
- mourn_inferior (find_process_pid (ptid_get_pid (last_ptid)));
+/* See server.h. */
- return signal_pid;
+gdb_environ *
+get_environ ()
+{
+ return &our_environ;
}
static int
return;
}
+ if (strcmp (own_buf, "QEnvironmentReset") == 0)
+ {
+ our_environ = gdb_environ::from_host_environ ();
+
+ write_ok (own_buf);
+ return;
+ }
+
+ if (startswith (own_buf, "QEnvironmentHexEncoded:"))
+ {
+ const char *p = own_buf + sizeof ("QEnvironmentHexEncoded:") - 1;
+ /* The final form of the environment variable. FINAL_VAR will
+ hold the 'VAR=VALUE' format. */
+ std::string final_var = hex2str (p);
+ std::string var_name, var_value;
+
+ if (remote_debug)
+ {
+ debug_printf (_("[QEnvironmentHexEncoded received '%s']\n"), p);
+ debug_printf (_("[Environment variable to be set: '%s']\n"),
+ final_var.c_str ());
+ debug_flush ();
+ }
+
+ size_t pos = final_var.find ('=');
+ if (pos == std::string::npos)
+ {
+ warning (_("Unexpected format for environment variable: '%s'"),
+ final_var.c_str ());
+ write_enn (own_buf);
+ return;
+ }
+
+ var_name = final_var.substr (0, pos);
+ var_value = final_var.substr (pos + 1, std::string::npos);
+
+ our_environ.set (var_name.c_str (), var_value.c_str ());
+
+ write_ok (own_buf);
+ return;
+ }
+
+ if (startswith (own_buf, "QEnvironmentUnset:"))
+ {
+ const char *p = own_buf + sizeof ("QEnvironmentUnset:") - 1;
+ std::string varname = hex2str (p);
+
+ if (remote_debug)
+ {
+ debug_printf (_("[QEnvironmentUnset received '%s']\n"), p);
+ debug_printf (_("[Environment variable to be unset: '%s']\n"),
+ varname.c_str ());
+ debug_flush ();
+ }
+
+ our_environ.unset (varname.c_str ());
+
+ write_ok (own_buf);
+ return;
+ }
+
if (strcmp (own_buf, "QStartNoAckMode") == 0)
{
if (remote_debug)
{
- fprintf (stderr, "[noack mode enabled]\n");
- fflush (stderr);
+ debug_printf ("[noack mode enabled]\n");
+ debug_flush ();
}
noack_mode = 1;
non_stop = req;
if (remote_debug)
- fprintf (stderr, "[%s mode enabled]\n", req_str);
+ debug_printf ("[%s mode enabled]\n", req_str);
write_ok (own_buf);
return;
if (remote_debug)
{
- if (disable_randomization)
- fprintf (stderr, "[address space randomization disabled]\n");
- else
- fprintf (stderr, "[address space randomization enabled]\n");
+ debug_printf (disable_randomization
+ ? "[address space randomization disabled]\n"
+ : "[address space randomization enabled]\n");
}
write_ok (own_buf);
/* Update the flag. */
use_agent = req;
if (remote_debug)
- fprintf (stderr, "[%s agent]\n", req ? "Enable" : "Disable");
+ debug_printf ("[%s agent]\n", req ? "Enable" : "Disable");
write_ok (own_buf);
return;
}
{
const char *req_str = report_thread_events ? "enabled" : "disabled";
- fprintf (stderr, "[thread events are now %s]\n", req_str);
+ debug_printf ("[thread events are now %s]\n", req_str);
+ }
+
+ write_ok (own_buf);
+ return;
+ }
+
+ if (startswith (own_buf, "QStartupWithShell:"))
+ {
+ const char *value = own_buf + strlen ("QStartupWithShell:");
+
+ if (strcmp (value, "1") == 0)
+ startup_with_shell = true;
+ else if (strcmp (value, "0") == 0)
+ startup_with_shell = false;
+ else
+ {
+ /* Unknown value. */
+ fprintf (stderr, "Unknown value to startup-with-shell: %s\n",
+ own_buf);
+ write_enn (own_buf);
+ return;
}
+ if (remote_debug)
+ debug_printf (_("[Inferior will %s started with shell]"),
+ startup_with_shell ? "be" : "not be");
+
write_ok (own_buf);
return;
}
}
sprintf (own_buf,
- "PacketSize=%x;QPassSignals+;QProgramSignals+",
+ "PacketSize=%x;QPassSignals+;QProgramSignals+;"
+ "QStartupWithShell+;QEnvironmentHexEncoded+;"
+ "QEnvironmentReset+;QEnvironmentUnset+",
PBUFSIZ - 1);
if (target_supports_catch_syscall ())
char *p, *q;
int n = 0, i = 0;
struct thread_resume *resume_info;
- struct thread_resume default_action = {{0}};
+ struct thread_resume default_action { null_ptid };
/* Count the number of semicolons in the packet. There should be one
for every action. */
if (last_status.kind == TARGET_WAITKIND_EXITED
|| last_status.kind == TARGET_WAITKIND_SIGNALLED)
- mourn_inferior (find_process_pid (ptid_get_pid (last_ptid)));
+ target_mourn_inferior (last_ptid);
}
}
static int
handle_v_run (char *own_buf)
{
- char *p, *next_p, **new_argv;
+ char *p, *next_p;
+ std::vector<char *> new_argv;
+ char *new_program_name = NULL;
int i, new_argc;
new_argc = 0;
new_argc++;
}
- new_argv = (char **) calloc (new_argc + 2, sizeof (char *));
- if (new_argv == NULL)
- {
- write_enn (own_buf);
- return 0;
- }
-
- i = 0;
- for (p = own_buf + strlen ("vRun;"); *p; p = next_p)
+ for (i = 0, p = own_buf + strlen ("vRun;"); *p; p = next_p, ++i)
{
next_p = strchr (p, ';');
if (next_p == NULL)
next_p = p + strlen (p);
if (i == 0 && p == next_p)
- new_argv[i] = NULL;
- else
{
- /* FIXME: Fail request if out of memory instead of dying. */
- new_argv[i] = (char *) xmalloc (1 + (next_p - p) / 2);
- hex2bin (p, (gdb_byte *) new_argv[i], (next_p - p) / 2);
- new_argv[i][(next_p - p) / 2] = '\0';
+ /* No program specified. */
+ 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';
+
+ 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++;
- i++;
}
- new_argv[i] = NULL;
+ 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 == NULL)
+ if (program_name == NULL)
{
write_enn (own_buf);
- freeargv (new_argv);
- return 0;
- }
-
- new_argv[0] = strdup (program_argv[0]);
- if (new_argv[0] == NULL)
- {
- write_enn (own_buf);
- freeargv (new_argv);
+ free_vector_argv (new_argv);
return 0;
}
}
+ else
+ {
+ xfree (program_name);
+ program_name = new_program_name;
+ }
/* Free the old argv and install the new one. */
- freeargv (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);
if (last_status.kind == TARGET_WAITKIND_STOPPED)
{
prepare_resume_reply (own_buf, last_ptid, &last_status);
if (startswith (own_buf, "vCont;"))
{
- require_running (own_buf);
handle_v_cont (own_buf);
return;
}
{
if (debug_threads)
{
- char *status_string
+ std::string status_string
= target_waitstatus_to_string (&thread->last_status);
debug_printf ("Reporting thread %s as already stopped with %s\n",
target_pid_to_str (entry->id),
- status_string);
-
- xfree (status_string);
+ status_string.c_str ());
}
gdb_assert (thread->last_status.kind != TARGET_WAITKIND_IGNORE);
gdbserver_version (void)
{
printf ("GNU gdbserver %s%s\n"
- "Copyright (C) 2016 Free Software Foundation, Inc.\n"
+ "Copyright (C) 2017 Free Software Foundation, Inc.\n"
"gdbserver is free software, covered by the "
"GNU General Public License.\n"
"This gdbserver was configured as \"%s\"\n",
" --no-disable-randomization\n"
" Don't disable address space randomization when\n"
" starting PROG.\n"
+ " --startup-with-shell\n"
+ " Start PROG using a shell. I.e., execs a shell that\n"
+ " then execs PROG. (default)\n"
+ " --no-startup-with-shell\n"
+ " Exec PROG directly instead of using a shell.\n"
+ " Disables argument globbing and variable substitution\n"
+ " on UNIX-like systems.\n"
"\n"
"Debug options:\n"
"\n"
{
int bad_attach;
int pid;
- char *arg_end, *port;
+ char *arg_end;
+ const char *port = NULL;
char **next_arg = &argv[1];
volatile int multi_mode = 0;
volatile int attach = 0;
int was_running;
+ bool selftest = false;
while (*next_arg != NULL && **next_arg == '-')
{
multi_mode = 1;
else if (strcmp (*next_arg, "--wrapper") == 0)
{
+ char **tmp;
+
next_arg++;
- wrapper_argv = next_arg;
+ tmp = next_arg;
while (*next_arg != NULL && strcmp (*next_arg, "--") != 0)
- next_arg++;
+ {
+ wrapper_argv += *next_arg;
+ wrapper_argv += ' ';
+ next_arg++;
+ }
- if (next_arg == wrapper_argv || *next_arg == NULL)
+ if (!wrapper_argv.empty ())
+ {
+ /* Erase the last whitespace. */
+ wrapper_argv.erase (wrapper_argv.end () - 1);
+ }
+
+ if (next_arg == tmp || *next_arg == NULL)
{
gdbserver_usage (stderr);
exit (1);
{
/* "-" specifies a stdio connection and is a form of port
specification. */
- *next_arg = STDIO_CONNECTION_NAME;
+ port = STDIO_CONNECTION_NAME;
+ next_arg++;
break;
}
else if (strcmp (*next_arg, "--disable-randomization") == 0)
disable_randomization = 1;
else if (strcmp (*next_arg, "--no-disable-randomization") == 0)
disable_randomization = 0;
+ else if (strcmp (*next_arg, "--startup-with-shell") == 0)
+ startup_with_shell = true;
+ else if (strcmp (*next_arg, "--no-startup-with-shell") == 0)
+ startup_with_shell = false;
else if (strcmp (*next_arg, "--once") == 0)
run_once = 1;
+ else if (strcmp (*next_arg, "--selftest") == 0)
+ selftest = true;
else
{
fprintf (stderr, "Unknown argument: %s\n", *next_arg);
continue;
}
- port = *next_arg;
- next_arg++;
- if (port == NULL || (!attach && !multi_mode && *next_arg == NULL))
+ if (port == NULL)
+ {
+ port = *next_arg;
+ next_arg++;
+ }
+ if ((port == NULL || (!attach && !multi_mode && *next_arg == NULL))
+ && !selftest)
{
gdbserver_usage (stderr);
exit (1);
starting the inferior. Inferiors created in this scenario have
stdin,stdout redirected. So do this here before we call
start_inferior. */
- remote_prepare (port);
+ if (port != NULL)
+ remote_prepare (port);
bad_attach = 0;
pid = 0;
exit (1);
}
+ /* Gather information about the environment. */
+ our_environ = gdb_environ::from_host_environ ();
+
initialize_async_io ();
initialize_low ();
+ have_job_control ();
initialize_event_loop ();
if (target_supports_tracepoints ())
initialize_tracepoint ();
own_buf = (char *) xmalloc (PBUFSIZ + 1);
mem_buf = (unsigned char *) xmalloc (PBUFSIZ);
+ if (selftest)
+ {
+ selftests::run_tests ();
+ throw_quit ("Quit");
+ }
+
if (pid == 0 && *next_arg != NULL)
{
int i, n;
n = argc - (next_arg - argv);
- program_argv = XNEWVEC (char *, n + 1);
- for (i = 0; i < n; i++)
- program_argv[i] = xstrdup (next_arg[i]);
- program_argv[i] = 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);
+ 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
fprintf (stderr, "GDBserver restarting\n");
/* Wait till we are at 1st instruction in prog. */
- if (program_argv != NULL)
+ if (program_name != NULL)
{
- start_inferior (program_argv);
+ create_inferior (program_name, program_args);
+
if (last_status.kind == TARGET_WAITKIND_STOPPED)
{
/* Stopped at the first instruction of the target
|| last_status.kind == TARGET_WAITKIND_SIGNALLED)
{
mark_breakpoints_out (process);
- mourn_inferior (process);
+ target_mourn_inferior (last_ptid);
}
else if (last_status.kind == TARGET_WAITKIND_THREAD_EXITED)
;
return 0;
}
+
+#if GDB_SELF_TEST
+namespace selftests
+{
+
+void
+reset ()
+{}
+
+} // namespace selftests
+#endif /* GDB_SELF_TEST */