X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gdb%2Fgdbserver%2Fserver.c;h=4ec3548d64444ac11e5417f4d7b40acb6dcfe27d;hb=8ecfd7bd4acd69213c06fac6de9af38299123547;hp=ad6626eafe5a98022029dab920775d7f2db2ce6c;hpb=2716529498941971b698f603e9aa0edc89905cb8;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c index ad6626eafe..4ec3548d64 100644 --- a/gdb/gdbserver/server.c +++ b/gdb/gdbserver/server.c @@ -1,5 +1,5 @@ /* Main code for remote server for GDB. - Copyright (C) 1989-2015 Free Software Foundation, Inc. + Copyright (C) 1989-2018 Free Software Foundation, Inc. This file is part of GDB. @@ -22,7 +22,7 @@ #include "notif.h" #include "tdesc.h" #include "rsp-low.h" - +#include "signals-state-save-restore.h" #include #include #if HAVE_SIGNAL_H @@ -35,49 +35,88 @@ #include "tracepoint.h" #include "dll.h" #include "hostio.h" +#include +#include "common-inferior.h" +#include "job-control.h" +#include "environ.h" +#include "filenames.h" +#include "pathstuff.h" + +#include "common/selftest.h" + +#define require_running_or_return(BUF) \ + if (!target_running ()) \ + { \ + write_enn (BUF); \ + return; \ + } + +#define require_running_or_break(BUF) \ + if (!target_running ()) \ + { \ + write_enn (BUF); \ + break; \ + } + +/* String containing the current directory (what getwd would return). */ + +char *current_directory; + +/* The environment to pass to the inferior when creating it. */ + +static gdb_environ our_environ; -/* The thread set with an `Hc' packet. `Hc' is deprecated in favor of - `vCont'. Note the multi-process extensions made `vCont' a - requirement, so `Hc pPID.TID' is pretty much undefined. So - CONT_THREAD can be null_ptid for no `Hc' thread, minus_one_ptid for - resuming all threads of the process (again, `Hc' isn't used for - multi-process), or a specific thread ptid_t. */ -ptid_t cont_thread; +/* Start the inferior using a shell. */ -/* The thread set with an `Hg' packet. */ -ptid_t general_thread; +/* We always try to start the inferior using a shell. */ + +int startup_with_shell = 1; int server_waiting; -int extended_protocol; +static int extended_protocol; static int response_needed; static int exit_requested; /* --once: Exit after the first connection has closed. */ int run_once; -int multi_process; -int report_fork_events; -int report_vfork_events; -int report_exec_events; +/* Whether to report TARGET_WAITKING_NO_RESUMED events. */ +static int report_no_resumed; + int non_stop; -int swbreak_feature; -int hwbreak_feature; -/* True if the "vContSupported" feature is active. In that case, GDB - wants us to report whether single step is supported in the reply to - "vCont?" packet. */ -static int vCont_supported; +static struct { + /* Set the PROGRAM_PATH. Here we adjust the path of the provided + binary if needed. */ + void set (gdb::unique_xmalloc_ptr &&path) + { + m_path = std::move (path); + + /* Make sure we're using the absolute path of the inferior when + creating it. */ + if (!contains_dir_separator (m_path.get ())) + { + int reg_file_errno; -/* Whether we should attempt to disable the operating system's address - space randomization feature before starting an inferior. */ -int disable_randomization = 1; + /* Check if the file is in our CWD. If it is, then we prefix + its name with CURRENT_DIRECTORY. Otherwise, we leave the + name as-is because we'll try searching for it in $PATH. */ + if (is_regular_file (m_path.get (), ®_file_errno)) + m_path = gdb_abspath (m_path.get ()); + } + } -static char **program_argv, **wrapper_argv; + /* Return the PROGRAM_PATH. */ + char *get () + { return m_path.get (); } -int pass_signals[GDB_SIGNAL_LAST]; -int program_signals[GDB_SIGNAL_LAST]; -int program_signals_p; +private: + /* The program name, adjusted if needed. */ + gdb::unique_xmalloc_ptr m_path; +} program_path; +static std::vector program_args; +static std::string wrapper_argv; /* The PID of the originally created or attached inferior. Used to send signals to the process when GDB sends us an asynchronous interrupt @@ -86,22 +125,6 @@ int program_signals_p; 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. */ @@ -110,11 +133,6 @@ int disable_packet_Tthread; int disable_packet_qC; int disable_packet_qfThreadInfo; -/* Last status reported to GDB. */ -static struct target_waitstatus last_status; -static ptid_t last_ptid; - -static char *own_buf; static unsigned char *mem_buf; /* A sub-class of 'struct notif_event' for stop, holding information @@ -138,6 +156,18 @@ static struct btrace_config current_btrace_conf; DEFINE_QUEUE_P (notif_event_p); +/* The client remote protocol state. */ + +static client_state g_client_state; + +client_state & +get_client_state () +{ + client_state &cs = g_client_state; + return cs; +} + + /* Put a stop reply to the stop reply queue. */ static void @@ -160,7 +190,7 @@ remove_all_on_match_ptid (QUEUE (notif_event_p) *q, ptid_t filter_ptid = *(ptid_t *) data; struct vstop_notif *vstop_event = (struct vstop_notif *) event; - if (ptid_match (vstop_event->ptid, filter_ptid)) + if (vstop_event->ptid.matches (filter_ptid)) { if (q->free_func != NULL) q->free_func (event); @@ -188,120 +218,80 @@ vstop_notif_reply (struct notif_event *event, char *own_buf) prepare_resume_reply (own_buf, vstop->ptid, &vstop->status); } -struct notif_server notif_stop = -{ - "vStopped", "Stop", NULL, vstop_notif_reply, -}; +/* QUEUE_iterate callback helper for in_queued_stop_replies. */ static int -target_running (void) +in_queued_stop_replies_ptid (QUEUE (notif_event_p) *q, + QUEUE_ITER (notif_event_p) *iter, + struct notif_event *event, + void *data) { - return get_first_thread () != NULL; -} - -static int -start_inferior (char **argv) -{ - char **new_argv = argv; - - 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 (debug_threads) - { - int i; - for (i = 0; new_argv[i]; ++i) - debug_printf ("new_argv[%d] = \"%s\"\n", i, new_argv[i]); - debug_flush (); - } + ptid_t filter_ptid = *(ptid_t *) data; + struct vstop_notif *vstop_event = (struct vstop_notif *) event; -#ifdef SIGTTOU - signal (SIGTTOU, SIG_DFL); - signal (SIGTTIN, SIG_DFL); -#endif + if (vstop_event->ptid.matches (filter_ptid)) + return 0; - signal_pid = create_inferior (new_argv[0], new_argv); + /* 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) + && vstop_event->status.value.related_pid.matches (filter_ptid)) + return 0; - /* 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); + return 1; +} -#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 +/* See server.h. */ - if (wrapper_argv != NULL) - { - struct thread_resume resume_info; +int +in_queued_stop_replies (ptid_t ptid) +{ + return !QUEUE_iterate (notif_event_p, notif_stop.queue, + in_queued_stop_replies_ptid, &ptid); +} - memset (&resume_info, 0, sizeof (resume_info)); - resume_info.thread = pid_to_ptid (signal_pid); - resume_info.kind = resume_continue; - resume_info.sig = 0; +struct notif_server notif_stop = +{ + "vStopped", "Stop", NULL, vstop_notif_reply, +}; - last_ptid = mywait (pid_to_ptid (signal_pid), &last_status, 0, 0); +static int +target_running (void) +{ + return get_first_thread () != NULL; +} - if (last_status.kind == TARGET_WAITKIND_STOPPED) - { - do - { - (*the_target->resume) (&resume_info, 1); +/* See common/common-inferior.h. */ - last_ptid = mywait (pid_to_ptid (signal_pid), &last_status, 0, 0); - if (last_status.kind != TARGET_WAITKIND_STOPPED) - break; +const char * +get_exec_wrapper () +{ + return !wrapper_argv.empty () ? wrapper_argv.c_str () : NULL; +} - current_thread->last_resume_kind = resume_stop; - current_thread->last_status = last_status; - } - while (last_status.value.sig != GDB_SIGNAL_TRAP); - } - target_arch_setup (); - return signal_pid; - } +/* See common/common-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); +char * +get_exec_file (int err) +{ + if (err && program_path.get () == NULL) + error (_("No executable file specified.")); - target_arch_setup (); + return program_path.get (); +} - if (last_status.kind != TARGET_WAITKIND_EXITED - && last_status.kind != TARGET_WAITKIND_SIGNALLED) - { - 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 attach_inferior (int pid) { + client_state &cs = get_client_state (); /* myattach should return -1 if attaching is unsupported, 0 if it succeeded, and call error() otherwise. */ @@ -318,17 +308,17 @@ attach_inferior (int pid) if (!non_stop) { - last_ptid = mywait (pid_to_ptid (pid), &last_status, 0, 0); + cs.last_ptid = mywait (ptid_t (pid), &cs.last_status, 0, 0); /* GDB knows to ignore the first SIGSTOP after attaching to a running process using the "attach" command, but this is different; it's just using "target remote". Pretend it's just starting up. */ - if (last_status.kind == TARGET_WAITKIND_STOPPED - && last_status.value.sig == GDB_SIGNAL_STOP) - last_status.value.sig = GDB_SIGNAL_TRAP; + if (cs.last_status.kind == TARGET_WAITKIND_STOPPED + && cs.last_status.value.sig == GDB_SIGNAL_STOP) + cs.last_status.value.sig = GDB_SIGNAL_TRAP; current_thread->last_resume_kind = resume_stop; - current_thread->last_status = last_status; + current_thread->last_status = cs.last_status; } return 0; @@ -385,7 +375,7 @@ decode_xfer (char *buf, char **object, char **rw, char **annex, char **offset) to as much of DATA/LEN as we could fit. IS_MORE controls the first character of the response. */ static int -write_qxfer_response (char *buf, const void *data, int len, int is_more) +write_qxfer_response (char *buf, const gdb_byte *data, int len, int is_more) { int out_len; @@ -400,52 +390,41 @@ write_qxfer_response (char *buf, const void *data, int len, int is_more) /* Handle btrace enabling in BTS format. */ -static const char * +static void handle_btrace_enable_bts (struct thread_info *thread) { if (thread->btrace != NULL) - return "E.Btrace already enabled."; + error (_("Btrace already enabled.")); current_btrace_conf.format = BTRACE_FORMAT_BTS; - thread->btrace = target_enable_btrace (thread->entry.id, - ¤t_btrace_conf); - if (thread->btrace == NULL) - return "E.Could not enable btrace."; - - return NULL; + thread->btrace = target_enable_btrace (thread->id, ¤t_btrace_conf); } -/* Handle btrace enabling in Intel(R) Processor Trace format. */ +/* Handle btrace enabling in Intel Processor Trace format. */ -static const char * +static void handle_btrace_enable_pt (struct thread_info *thread) { if (thread->btrace != NULL) - return "E.Btrace already enabled."; + error (_("Btrace already enabled.")); current_btrace_conf.format = BTRACE_FORMAT_PT; - thread->btrace = target_enable_btrace (thread->entry.id, - ¤t_btrace_conf); - if (thread->btrace == NULL) - return "E.Could not enable btrace."; - - return NULL; + thread->btrace = target_enable_btrace (thread->id, ¤t_btrace_conf); } /* Handle btrace disabling. */ -static const char * +static void handle_btrace_disable (struct thread_info *thread) { if (thread->btrace == NULL) - return "E.Branch tracing not enabled."; + error (_("Branch tracing not enabled.")); if (target_disable_btrace (thread->btrace) != 0) - return "E.Could not disable branch tracing."; + error (_("Could not disable branch tracing.")); thread->btrace = NULL; - return NULL; } /* Handle the "Qbtrace" packet. */ @@ -453,8 +432,8 @@ handle_btrace_disable (struct thread_info *thread) static int handle_btrace_general_set (char *own_buf) { + client_state &cs = get_client_state (); struct thread_info *thread; - const char *err; char *op; if (!startswith (own_buf, "Qbtrace:")) @@ -462,35 +441,38 @@ handle_btrace_general_set (char *own_buf) op = own_buf + strlen ("Qbtrace:"); - if (ptid_equal (general_thread, null_ptid) - || ptid_equal (general_thread, minus_one_ptid)) + if (cs.general_thread == null_ptid + || cs.general_thread == minus_one_ptid) { strcpy (own_buf, "E.Must select a single thread."); return -1; } - thread = find_thread_ptid (general_thread); + thread = find_thread_ptid (cs.general_thread); if (thread == NULL) { strcpy (own_buf, "E.No such thread."); return -1; } - err = NULL; - - if (strcmp (op, "bts") == 0) - err = handle_btrace_enable_bts (thread); - else if (strcmp (op, "pt") == 0) - err = handle_btrace_enable_pt (thread); - else if (strcmp (op, "off") == 0) - err = handle_btrace_disable (thread); - else - err = "E.Bad Qbtrace operation. Use bts, pt, or off."; + TRY + { + if (strcmp (op, "bts") == 0) + handle_btrace_enable_bts (thread); + else if (strcmp (op, "pt") == 0) + handle_btrace_enable_pt (thread); + else if (strcmp (op, "off") == 0) + handle_btrace_disable (thread); + else + error (_("Bad Qbtrace operation. Use bts, pt, or off.")); - if (err != 0) - strcpy (own_buf, err); - else - write_ok (own_buf); + write_ok (own_buf); + } + CATCH (exception, RETURN_MASK_ERROR) + { + sprintf (own_buf, "E.%s", exception.message); + } + END_CATCH return 1; } @@ -500,6 +482,7 @@ handle_btrace_general_set (char *own_buf) static int handle_btrace_conf_general_set (char *own_buf) { + client_state &cs = get_client_state (); struct thread_info *thread; char *op; @@ -508,14 +491,14 @@ handle_btrace_conf_general_set (char *own_buf) op = own_buf + strlen ("Qbtrace-conf:"); - if (ptid_equal (general_thread, null_ptid) - || ptid_equal (general_thread, minus_one_ptid)) + if (cs.general_thread == null_ptid + || cs.general_thread == minus_one_ptid) { strcpy (own_buf, "E.Must select a single thread."); return -1; } - thread = find_thread_ptid (general_thread); + thread = find_thread_ptid (cs.general_thread); if (thread == NULL) { strcpy (own_buf, "E.No such thread."); @@ -567,6 +550,7 @@ handle_btrace_conf_general_set (char *own_buf) static void handle_general_set (char *own_buf) { + client_state &cs = get_client_state (); if (startswith (own_buf, "QPassSignals:")) { int numsigs = (int) GDB_SIGNAL_LAST, i; @@ -578,7 +562,7 @@ handle_general_set (char *own_buf) { if (i == cursig) { - pass_signals[i] = 1; + cs.pass_signals[i] = 1; if (*p == '\0') /* Keep looping, to clear the remaining signals. */ cursig = -1; @@ -586,7 +570,7 @@ handle_general_set (char *own_buf) p = decode_address_to_semicolon (&cursig, p); } else - pass_signals[i] = 0; + cs.pass_signals[i] = 0; } strcpy (own_buf, "OK"); return; @@ -598,14 +582,14 @@ handle_general_set (char *own_buf) const char *p = own_buf + strlen ("QProgramSignals:"); CORE_ADDR cursig; - program_signals_p = 1; + cs.program_signals_p = 1; p = decode_address_to_semicolon (&cursig, p); for (i = 0; i < numsigs; i++) { if (i == cursig) { - program_signals[i] = 1; + cs.program_signals[i] = 1; if (*p == '\0') /* Keep looping, to clear the remaining signals. */ cursig = -1; @@ -613,21 +597,130 @@ handle_general_set (char *own_buf) p = decode_address_to_semicolon (&cursig, p); } else - program_signals[i] = 0; + cs.program_signals[i] = 0; } strcpy (own_buf, "OK"); return; } + if (startswith (own_buf, "QCatchSyscalls:")) + { + const char *p = own_buf + sizeof ("QCatchSyscalls:") - 1; + int enabled = -1; + CORE_ADDR sysno; + struct process_info *process; + + if (!target_running () || !target_supports_catch_syscall ()) + { + write_enn (own_buf); + return; + } + + if (strcmp (p, "0") == 0) + enabled = 0; + else if (p[0] == '1' && (p[1] == ';' || p[1] == '\0')) + enabled = 1; + else + { + fprintf (stderr, "Unknown catch-syscalls mode requested: %s\n", + own_buf); + write_enn (own_buf); + return; + } + + process = current_process (); + process->syscalls_to_catch.clear (); + + if (enabled) + { + p += 1; + if (*p == ';') + { + p += 1; + while (*p != '\0') + { + p = decode_address_to_semicolon (&sysno, p); + process->syscalls_to_catch.push_back (sysno); + } + } + else + process->syscalls_to_catch.push_back (ANY_SYSCALL); + } + + write_ok (own_buf); + 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; + cs.noack_mode = 1; write_ok (own_buf); return; } @@ -663,7 +756,7 @@ handle_general_set (char *own_buf) 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; @@ -675,14 +768,13 @@ handle_general_set (char *own_buf) ULONGEST setting; unpack_varlen_hex (packet, &setting); - disable_randomization = setting; + cs.disable_randomization = setting; if (remote_debug) { - if (disable_randomization) - fprintf (stderr, "[address space randomization disabled]\n"); - else - fprintf (stderr, "[address space randomization enabled]\n"); + debug_printf (cs.disable_randomization + ? "[address space randomization disabled]\n" + : "[address space randomization enabled]\n"); } write_ok (own_buf); @@ -712,7 +804,7 @@ handle_general_set (char *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; } @@ -723,6 +815,90 @@ handle_general_set (char *own_buf) if (handle_btrace_conf_general_set (own_buf)) return; + if (startswith (own_buf, "QThreadEvents:")) + { + char *mode = own_buf + strlen ("QThreadEvents:"); + enum tribool req = TRIBOOL_UNKNOWN; + + if (strcmp (mode, "0") == 0) + req = TRIBOOL_FALSE; + else if (strcmp (mode, "1") == 0) + req = TRIBOOL_TRUE; + else + { + /* We don't know what this mode is, so complain to GDB. */ + sprintf (own_buf, "E.Unknown thread-events mode requested: %s\n", + mode); + return; + } + + cs.report_thread_events = (req == TRIBOOL_TRUE); + + if (remote_debug) + { + const char *req_str = cs.report_thread_events ? "enabled" : "disabled"; + + 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; + } + + if (startswith (own_buf, "QSetWorkingDir:")) + { + const char *p = own_buf + strlen ("QSetWorkingDir:"); + + if (*p != '\0') + { + std::string path = hex2str (p); + + set_inferior_cwd (path.c_str ()); + + if (remote_debug) + debug_printf (_("[Set the inferior's current directory to %s]\n"), + path.c_str ()); + } + else + { + /* An empty argument means that we should clear out any + previously set cwd for the inferior. */ + set_inferior_cwd (NULL); + + if (remote_debug) + debug_printf (_("\ +[Unset the inferior's current directory; will use gdbserver's cwd]\n")); + } + write_ok (own_buf); + + return; + } + /* Otherwise we didn't know what packet it was. Say we didn't understand it. */ own_buf[0] = 0; @@ -741,12 +917,14 @@ get_features_xml (const char *annex) This variable is set up from the auto-generated init_registers_... routine for the current target. */ - if (desc->xmltarget != NULL && strcmp (annex, "target.xml") == 0) + if (strcmp (annex, "target.xml") == 0) { - if (*desc->xmltarget == '@') - return desc->xmltarget + 1; + const char *ret = tdesc_get_features_xml (desc); + + if (*ret == '@') + return ret + 1; else - annex = desc->xmltarget; + annex = ret; } #ifdef USE_XML @@ -767,7 +945,7 @@ get_features_xml (const char *annex) return NULL; } -void +static void monitor_show_help (void) { monitor_output ("The following monitor commands are supported:\n"); @@ -799,14 +977,15 @@ monitor_show_help (void) static int gdb_read_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len) { + client_state &cs = get_client_state (); int res; - if (current_traceframe >= 0) + if (cs.current_traceframe >= 0) { ULONGEST nbytes; ULONGEST length = len; - if (traceframe_read_mem (current_traceframe, + if (traceframe_read_mem (cs.current_traceframe, memaddr, myaddr, len, &nbytes)) return -1; /* Data read from trace buffer, we're done. */ @@ -821,7 +1000,7 @@ gdb_read_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len) res = prepare_to_access_memory (); if (res == 0) { - if (set_desired_thread (1)) + if (set_desired_thread ()) res = read_inferior_memory (memaddr, myaddr, len); else res = 1; @@ -839,7 +1018,8 @@ gdb_read_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len) static int gdb_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len) { - if (current_traceframe >= 0) + client_state &cs = get_client_state (); + if (cs.current_traceframe >= 0) return EIO; else { @@ -848,7 +1028,7 @@ gdb_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len) ret = prepare_to_access_memory (); if (ret == 0) { - if (set_desired_thread (1)) + if (set_desired_thread ()) ret = write_inferior_memory (memaddr, myaddr, len); else ret = EIO; @@ -891,7 +1071,8 @@ handle_search_memory_1 (CORE_ADDR start_addr, CORE_ADDR search_space_len, ? search_space_len : search_buf_size); - found_ptr = memmem (search_buf, nr_search_bytes, pattern, pattern_len); + found_ptr = (gdb_byte *) memmem (search_buf, nr_search_bytes, pattern, + pattern_len); if (found_ptr != NULL) { @@ -1008,19 +1189,110 @@ handle_search_memory (char *own_buf, int packet_len) free (pattern); } -#define require_running(BUF) \ - if (!target_running ()) \ - { \ - write_enn (BUF); \ - return; \ +/* Handle the "D" packet. */ + +static void +handle_detach (char *own_buf) +{ + client_state &cs = get_client_state (); + + process_info *process; + + if (cs.multi_process) + { + /* skip 'D;' */ + int pid = strtol (&own_buf[2], NULL, 16); + + process = find_process_pid (pid); + } + else + { + process = (current_thread != nullptr + ? get_thread_process (current_thread) + : nullptr); + } + + if (process == NULL) + { + write_enn (own_buf); + return; } + if ((tracing && disconnected_tracing) || any_persistent_commands (process)) + { + if (tracing && disconnected_tracing) + fprintf (stderr, + "Disconnected tracing in effect, " + "leaving gdbserver attached to the process\n"); + + if (any_persistent_commands (process)) + fprintf (stderr, + "Persistent commands are present, " + "leaving gdbserver attached to the process\n"); + + /* Make sure we're in non-stop/async mode, so we we can both + wait for an async socket accept, and handle async target + events simultaneously. There's also no point either in + having the target stop all threads, when we're going to + pass signals down without informing GDB. */ + if (!non_stop) + { + if (debug_threads) + debug_printf ("Forcing non-stop mode\n"); + + non_stop = 1; + start_non_stop (1); + } + + process->gdb_detached = 1; + + /* Detaching implicitly resumes all threads. */ + target_continue_no_signal (minus_one_ptid); + + write_ok (own_buf); + return; + } + + fprintf (stderr, "Detaching from process %d\n", process->pid); + stop_tracing (); + if (detach_inferior (process) != 0) + write_enn (own_buf); + else + { + discard_queued_stop_replies (ptid_t (process->pid)); + write_ok (own_buf); + + if (extended_protocol || target_running ()) + { + /* There is still at least one inferior remaining or + we are in extended mode, so don't terminate gdbserver, + and instead treat this like a normal program exit. */ + cs.last_status.kind = TARGET_WAITKIND_EXITED; + cs.last_status.value.integer = 0; + cs.last_ptid = ptid_t (process->pid); + + current_thread = NULL; + } + else + { + putpkt (own_buf); + remote_close (); + + /* If we are attached, then we can exit. Otherwise, we + need to hang around doing nothing, until the child is + gone. */ + join_inferior (process); + exit (0); + } + } +} + /* Parse options to --debug-format= and "monitor set debug-format". ARG is the text after "--debug-format=" or "monitor set debug-format". IS_MONITOR is non-zero if we're invoked via "monitor set debug-format". This triggers calls to monitor_output. - The result is NULL if all options were parsed ok, otherwise an error - message which the caller must free. + The result is an empty string if all options were parsed ok, otherwise an + error message which the caller must free. N.B. These commands affect all debug format settings, they are not cumulative. If a format is not specified, it is turned off. @@ -1032,13 +1304,9 @@ handle_search_memory (char *own_buf, int packet_len) to gdb's "set debug foo on|off" because we also use this function to parse "--debug-format=foo,bar". */ -static char * +static std::string parse_debug_format_options (const char *arg, int is_monitor) { - VEC (char_ptr) *options; - int ix; - char *option; - /* First turn all debug format options off. */ debug_timestamp = 0; @@ -1046,23 +1314,24 @@ parse_debug_format_options (const char *arg, int is_monitor) while (isspace (*arg)) ++arg; - options = delim_string_to_char_ptr_vec (arg, ','); + std::vector> options + = delim_string_to_char_ptr_vec (arg, ','); - for (ix = 0; VEC_iterate (char_ptr, options, ix, option); ++ix) + for (const gdb::unique_xmalloc_ptr &option : options) { - if (strcmp (option, "all") == 0) + if (strcmp (option.get (), "all") == 0) { debug_timestamp = 1; if (is_monitor) monitor_output ("All extra debug format options enabled.\n"); } - else if (strcmp (option, "none") == 0) + else if (strcmp (option.get (), "none") == 0) { debug_timestamp = 0; if (is_monitor) monitor_output ("All extra debug format options disabled.\n"); } - else if (strcmp (option, "timestamp") == 0) + else if (strcmp (option.get (), "timestamp") == 0) { debug_timestamp = 1; if (is_monitor) @@ -1074,17 +1343,11 @@ parse_debug_format_options (const char *arg, int is_monitor) continue; } else - { - char *msg = xstrprintf ("Unknown debug-format argument: \"%s\"\n", - option); - - free_char_ptr_vec (options); - return msg; - } + return string_printf ("Unknown debug-format argument: \"%s\"\n", + option.get ()); } - free_char_ptr_vec (options); - return NULL; + return std::string (); } /* Handle monitor commands not handled by target-specific handlers. */ @@ -1124,16 +1387,15 @@ handle_monitor_command (char *mon, char *own_buf) } else if (startswith (mon, "set debug-format ")) { - char *error_msg + std::string error_msg = parse_debug_format_options (mon + sizeof ("set debug-format ") - 1, 1); - if (error_msg != NULL) + if (!error_msg.empty ()) { - monitor_output (error_msg); + monitor_output (error_msg.c_str ()); monitor_show_help (); write_enn (own_buf); - xfree (error_msg); } } else if (strcmp (mon, "help") == 0) @@ -1191,7 +1453,7 @@ handle_qxfer_auxv (const char *annex, /* Handle qXfer:exec-file:read. */ static int -handle_qxfer_exec_file (const char *const_annex, +handle_qxfer_exec_file (const char *annex, gdb_byte *readbuf, const gdb_byte *writebuf, ULONGEST offset, LONGEST len) { @@ -1202,7 +1464,7 @@ handle_qxfer_exec_file (const char *const_annex, if (the_target->pid_to_exec_file == NULL || writebuf != NULL) return -2; - if (const_annex[0] == '\0') + if (annex[0] == '\0') { if (current_thread == NULL) return -1; @@ -1211,11 +1473,7 @@ handle_qxfer_exec_file (const char *const_annex, } else { - char *annex = (char *) alloca (strlen (const_annex) + 1); - - strcpy (annex, const_annex); annex = unpack_varlen_hex (annex, &pid); - if (annex[0] != '\0') return -1; } @@ -1272,48 +1530,6 @@ handle_qxfer_features (const char *annex, return len; } -/* Worker routine for handle_qxfer_libraries. - Add to the length pointed to by ARG a conservative estimate of the - length needed to transmit the file name of INF. */ - -static void -accumulate_file_name_length (struct inferior_list_entry *inf, void *arg) -{ - struct dll_info *dll = (struct dll_info *) inf; - unsigned int *total_len = (unsigned int *) arg; - - /* Over-estimate the necessary memory. Assume that every character - in the library name must be escaped. */ - *total_len += 128 + 6 * strlen (dll->name); -} - -/* Worker routine for handle_qxfer_libraries. - Emit the XML to describe the library in INF. */ - -static void -emit_dll_description (struct inferior_list_entry *inf, void *arg) -{ - struct dll_info *dll = (struct dll_info *) inf; - char **p_ptr = (char **) arg; - char *p = *p_ptr; - char *name; - - strcpy (p, " name); - strcpy (p, name); - free (name); - p = p + strlen (p); - strcpy (p, "\">base_addr); - p = p + strlen (p); - strcpy (p, "\"/>\n"); - p = p + strlen (p); - - *p_ptr = p; -} - /* Handle qXfer:libraries:read. */ static int @@ -1321,43 +1537,29 @@ handle_qxfer_libraries (const char *annex, gdb_byte *readbuf, const gdb_byte *writebuf, ULONGEST offset, LONGEST len) { - unsigned int total_len; - char *document, *p; - if (writebuf != NULL) return -2; if (annex[0] != '\0' || current_thread == NULL) return -1; - total_len = 64; - for_each_inferior_with_data (&all_dlls, accumulate_file_name_length, - &total_len); - - document = (char *) malloc (total_len); - if (document == NULL) - return -1; + std::string document = "\n"; - strcpy (document, "\n"); - p = document + strlen (document); + for (const dll_info &dll : all_dlls) + document += string_printf + (" \n", + dll.name.c_str (), (long) dll.base_addr); - for_each_inferior_with_data (&all_dlls, emit_dll_description, &p); + document += "\n"; - strcpy (p, "\n"); + if (offset > document.length ()) + return -1; - total_len = strlen (document); + if (offset + len > document.length ()) + len = document.length () - offset; - if (offset > total_len) - { - free (document); - return -1; - } + memcpy (readbuf, &document[offset], len); - if (offset + len > total_len) - len = total_len - offset; - - memcpy (readbuf, document + offset, len); - free (document); return len; } @@ -1429,15 +1631,17 @@ handle_qxfer_statictrace (const char *annex, gdb_byte *readbuf, const gdb_byte *writebuf, ULONGEST offset, LONGEST len) { + client_state &cs = get_client_state (); ULONGEST nbytes; if (writebuf != NULL) return -2; - if (annex[0] != '\0' || current_thread == NULL || current_traceframe == -1) + if (annex[0] != '\0' || current_thread == NULL + || cs.current_traceframe == -1) return -1; - if (traceframe_read_sdata (current_traceframe, offset, + if (traceframe_read_sdata (cs.current_traceframe, offset, readbuf, len, &nbytes)) return -1; return nbytes; @@ -1447,28 +1651,38 @@ handle_qxfer_statictrace (const char *annex, Emit the XML to describe the thread of INF. */ static void -handle_qxfer_threads_worker (struct inferior_list_entry *inf, void *arg) +handle_qxfer_threads_worker (thread_info *thread, struct buffer *buffer) { - struct thread_info *thread = (struct thread_info *) inf; - struct buffer *buffer = (struct buffer *) arg; - ptid_t ptid = thread_to_gdb_id (thread); + ptid_t ptid = ptid_of (thread); char ptid_s[100]; int core = target_core_of_thread (ptid); char core_s[21]; + const char *name = target_thread_name (ptid); + int handle_len; + gdb_byte *handle; + bool handle_status = target_thread_handle (ptid, &handle, &handle_len); write_ptid (ptid_s, ptid); + buffer_xml_printf (buffer, "\n", - ptid_s, core_s); + buffer_xml_printf (buffer, " core=\"%s\"", core_s); } - else + + if (name != NULL) + buffer_xml_printf (buffer, " name=\"%s\"", name); + + if (handle_status) { - buffer_xml_printf (buffer, "\n", - ptid_s); + char *handle_s = (char *) alloca (handle_len * 2 + 1); + bin2hex (handle, handle_s, handle_len); + buffer_xml_printf (buffer, " handle=\"%s\"", handle_s); } + + buffer_xml_printf (buffer, "/>\n"); } /* Helper for handle_qxfer_threads. */ @@ -1478,8 +1692,10 @@ handle_qxfer_threads_proper (struct buffer *buffer) { buffer_grow_str (buffer, "\n"); - for_each_inferior_with_data (&all_threads, handle_qxfer_threads_worker, - buffer); + for_each_thread ([&] (thread_info *thread) + { + handle_qxfer_threads_worker (thread, buffer); + }); buffer_grow_str0 (buffer, "\n"); } @@ -1541,13 +1757,14 @@ handle_qxfer_traceframe_info (const char *annex, gdb_byte *readbuf, const gdb_byte *writebuf, ULONGEST offset, LONGEST len) { + client_state &cs = get_client_state (); static char *result = 0; static unsigned int result_length = 0; if (writebuf != NULL) return -2; - if (!target_running () || annex[0] != '\0' || current_traceframe == -1) + if (!target_running () || annex[0] != '\0' || cs.current_traceframe == -1) return -1; if (offset == 0) @@ -1561,7 +1778,7 @@ handle_qxfer_traceframe_info (const char *annex, buffer_init (&buffer); - traceframe_read_info (current_traceframe, &buffer); + traceframe_read_info (cs.current_traceframe, &buffer); result = buffer_finish (&buffer); result_length = strlen (result); @@ -1606,30 +1823,32 @@ handle_qxfer_btrace (const char *annex, gdb_byte *readbuf, const gdb_byte *writebuf, ULONGEST offset, LONGEST len) { + client_state &cs = get_client_state (); static struct buffer cache; struct thread_info *thread; - int type, result; + enum btrace_read_type type; + int result; - if (the_target->read_btrace == NULL || writebuf != NULL) + if (writebuf != NULL) return -2; - if (ptid_equal (general_thread, null_ptid) - || ptid_equal (general_thread, minus_one_ptid)) + if (cs.general_thread == null_ptid + || cs.general_thread == minus_one_ptid) { - strcpy (own_buf, "E.Must select a single thread."); + strcpy (cs.own_buf, "E.Must select a single thread."); return -3; } - thread = find_thread_ptid (general_thread); + thread = find_thread_ptid (cs.general_thread); if (thread == NULL) { - strcpy (own_buf, "E.No such thread."); + strcpy (cs.own_buf, "E.No such thread."); return -3; } if (thread->btrace == NULL) { - strcpy (own_buf, "E.Btrace not enabled."); + strcpy (cs.own_buf, "E.Btrace not enabled."); return -3; } @@ -1641,7 +1860,7 @@ handle_qxfer_btrace (const char *annex, type = BTRACE_READ_DELTA; else { - strcpy (own_buf, "E.Bad annex."); + strcpy (cs.own_buf, "E.Bad annex."); return -3; } @@ -1649,12 +1868,21 @@ handle_qxfer_btrace (const char *annex, { buffer_free (&cache); - result = target_read_btrace (thread->btrace, &cache, type); - if (result != 0) + TRY { - memcpy (own_buf, cache.buffer, cache.used_size); - return -3; + result = target_read_btrace (thread->btrace, &cache, type); + if (result != 0) + memcpy (cs.own_buf, cache.buffer, cache.used_size); } + CATCH (exception, RETURN_MASK_ERROR) + { + sprintf (cs.own_buf, "E.%s", exception.message); + result = -1; + } + END_CATCH + + if (result != 0) + return -3; } else if (offset > cache.used_size) { @@ -1677,33 +1905,34 @@ handle_qxfer_btrace_conf (const char *annex, gdb_byte *readbuf, const gdb_byte *writebuf, ULONGEST offset, LONGEST len) { + client_state &cs = get_client_state (); static struct buffer cache; struct thread_info *thread; int result; - if (the_target->read_btrace_conf == NULL || writebuf != NULL) + if (writebuf != NULL) return -2; if (annex[0] != '\0') return -1; - if (ptid_equal (general_thread, null_ptid) - || ptid_equal (general_thread, minus_one_ptid)) + if (cs.general_thread == null_ptid + || cs.general_thread == minus_one_ptid) { - strcpy (own_buf, "E.Must select a single thread."); + strcpy (cs.own_buf, "E.Must select a single thread."); return -3; } - thread = find_thread_ptid (general_thread); + thread = find_thread_ptid (cs.general_thread); if (thread == NULL) { - strcpy (own_buf, "E.No such thread."); + strcpy (cs.own_buf, "E.No such thread."); return -3; } if (thread->btrace == NULL) { - strcpy (own_buf, "E.Btrace not enabled."); + strcpy (cs.own_buf, "E.Btrace not enabled."); return -3; } @@ -1711,12 +1940,21 @@ handle_qxfer_btrace_conf (const char *annex, { buffer_free (&cache); - result = target_read_btrace_conf (thread->btrace, &cache); - if (result != 0) + TRY + { + result = target_read_btrace_conf (thread->btrace, &cache); + if (result != 0) + memcpy (cs.own_buf, cache.buffer, cache.used_size); + } + CATCH (exception, RETURN_MASK_ERROR) { - memcpy (own_buf, cache.buffer, cache.used_size); - return -3; + sprintf (cs.own_buf, "E.%s", exception.message); + result = -1; } + END_CATCH + + if (result != 0) + return -3; } else if (offset > cache.used_size) { @@ -1869,11 +2107,6 @@ handle_qxfer (char *own_buf, int packet_len, int *new_packet_len_p) return 0; } -/* Table used by the crc32 function to calcuate the checksum. */ - -static unsigned int crc32_table[256] = -{0, 0}; - /* Compute 32 bit CRC from inferior memory. On success, return 32 bit CRC. @@ -1882,20 +2115,6 @@ static unsigned int crc32_table[256] = static unsigned long long crc32 (CORE_ADDR base, int len, unsigned int crc) { - if (!crc32_table[1]) - { - /* Initialize the CRC table and the decoding table. */ - int i, j; - unsigned int c; - - for (i = 0; i < 256; i++) - { - for (c = i << 24, j = 8; j > 0; --j) - c = c & 0x80000000 ? (c << 1) ^ 0x04c11db7 : (c << 1); - crc32_table[i] = c; - } - } - while (len--) { unsigned char byte = 0; @@ -1904,7 +2123,7 @@ crc32 (CORE_ADDR base, int len, unsigned int crc) if (read_inferior_memory (base, &byte, 1) != 0) return (unsigned long long) -1; - crc = (crc << 8) ^ crc32_table[((crc >> 24) ^ byte) & 255]; + crc = xcrc32 (&byte, 1, crc); base++; } return (unsigned long long) crc; @@ -1915,27 +2134,10 @@ crc32 (CORE_ADDR base, int len, unsigned int crc) static void supported_btrace_packets (char *buf) { - int btrace_supported = 0; - - if (target_supports_btrace (BTRACE_FORMAT_BTS)) - { - strcat (buf, ";Qbtrace:bts+"); - strcat (buf, ";Qbtrace-conf:bts:size+"); - - btrace_supported = 1; - } - - if (target_supports_btrace (BTRACE_FORMAT_PT)) - { - strcat (buf, ";Qbtrace:pt+"); - strcat (buf, ";Qbtrace-conf:pt:size+"); - - btrace_supported = 1; - } - - if (!btrace_supported) - return; - + strcat (buf, ";Qbtrace:bts+"); + strcat (buf, ";Qbtrace-conf:bts:size+"); + strcat (buf, ";Qbtrace:pt+"); + strcat (buf, ";Qbtrace-conf:pt:size+"); strcat (buf, ";Qbtrace:off+"); strcat (buf, ";qXfer:btrace:read+"); strcat (buf, ";qXfer:btrace-conf:read+"); @@ -1943,34 +2145,56 @@ supported_btrace_packets (char *buf) /* Handle all of the extended 'q' packets. */ -void +static void handle_query (char *own_buf, int packet_len, int *new_packet_len_p) { - static struct inferior_list_entry *thread_ptr; + client_state &cs = get_client_state (); + static std::list::const_iterator thread_iter; /* Reply the current thread id. */ if (strcmp ("qC", own_buf) == 0 && !disable_packet_qC) { - ptid_t gdb_id; - require_running (own_buf); + ptid_t ptid; + require_running_or_return (own_buf); - if (!ptid_equal (general_thread, null_ptid) - && !ptid_equal (general_thread, minus_one_ptid)) - gdb_id = general_thread; + if (cs.general_thread != null_ptid && cs.general_thread != minus_one_ptid) + ptid = cs.general_thread; else { - thread_ptr = get_first_inferior (&all_threads); - gdb_id = thread_to_gdb_id ((struct thread_info *)thread_ptr); + thread_iter = all_threads.begin (); + ptid = (*thread_iter)->id; } sprintf (own_buf, "QC"); own_buf += 2; - write_ptid (own_buf, gdb_id); + write_ptid (own_buf, ptid); return; } if (strcmp ("qSymbol::", own_buf) == 0) { + struct thread_info *save_thread = current_thread; + + /* For qSymbol, GDB only changes the current thread if the + previous current thread was of a different process. So if + the previous thread is gone, we need to pick another one of + the same process. This can happen e.g., if we followed an + exec in a non-leader thread. */ + if (current_thread == NULL) + { + current_thread + = find_any_thread_of_pid (cs.general_thread.pid ()); + + /* Just in case, if we didn't find a thread, then bail out + instead of crashing. */ + if (current_thread == NULL) + { + write_enn (own_buf); + current_thread = save_thread; + return; + } + } + /* GDB is suggesting new symbols have been loaded. This may mean a new shared library has been detected as loaded, so take the opportunity to check if breakpoints we think are @@ -1989,6 +2213,8 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) if (current_thread != NULL && the_target->look_up_symbols != NULL) (*the_target->look_up_symbols) (); + current_thread = save_thread; + strcpy (own_buf, "OK"); return; } @@ -1997,29 +2223,25 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) { if (strcmp ("qfThreadInfo", own_buf) == 0) { - ptid_t gdb_id; - - require_running (own_buf); - thread_ptr = get_first_inferior (&all_threads); + require_running_or_return (own_buf); + thread_iter = all_threads.begin (); *own_buf++ = 'm'; - gdb_id = thread_to_gdb_id ((struct thread_info *)thread_ptr); - write_ptid (own_buf, gdb_id); - thread_ptr = thread_ptr->next; + ptid_t ptid = (*thread_iter)->id; + write_ptid (own_buf, ptid); + thread_iter++; return; } if (strcmp ("qsThreadInfo", own_buf) == 0) { - ptid_t gdb_id; - - require_running (own_buf); - if (thread_ptr != NULL) + require_running_or_return (own_buf); + if (thread_iter != all_threads.end ()) { *own_buf++ = 'm'; - gdb_id = thread_to_gdb_id ((struct thread_info *)thread_ptr); - write_ptid (own_buf, gdb_id); - thread_ptr = thread_ptr->next; + ptid_t ptid = (*thread_iter)->id; + write_ptid (own_buf, ptid); + thread_iter++; return; } else @@ -2035,7 +2257,7 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) { CORE_ADDR text, data; - require_running (own_buf); + require_running_or_return (own_buf); if (the_target->read_offsets (&text, &data)) sprintf (own_buf, "Text=%lX;Data=%lX;Bss=%lX", (long)text, (long)data, (long)data); @@ -2052,9 +2274,6 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) char *p = &own_buf[10]; int gdb_supports_qRelocInsn = 0; - /* Start processing qSupported packet. */ - target_process_qsupported (NULL); - /* Process each feature being provided by GDB. The first feature will follow a ':', and latter features will follow ';'. */ @@ -2062,6 +2281,7 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) { char **qsupported = NULL; int count = 0; + int unknown = 0; int i; /* Two passes, to avoid nested strtok calls in @@ -2083,7 +2303,7 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) /* GDB supports and wants multi-process support if possible. */ if (target_supports_multi_process ()) - multi_process = 1; + cs.multi_process = 1; } else if (strcmp (p, "qRelocInsn+") == 0) { @@ -2096,48 +2316,71 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) by a software breakpoint and for us to handle PC adjustment if necessary on this target. */ if (target_supports_stopped_by_sw_breakpoint ()) - swbreak_feature = 1; + cs.swbreak_feature = 1; } else if (strcmp (p, "hwbreak+") == 0) { /* GDB wants us to report whether a trap is caused by a hardware breakpoint. */ if (target_supports_stopped_by_hw_breakpoint ()) - hwbreak_feature = 1; + cs.hwbreak_feature = 1; } else if (strcmp (p, "fork-events+") == 0) { /* GDB supports and wants fork events if possible. */ if (target_supports_fork_events ()) - report_fork_events = 1; + cs.report_fork_events = 1; } else if (strcmp (p, "vfork-events+") == 0) { /* GDB supports and wants vfork events if possible. */ if (target_supports_vfork_events ()) - report_vfork_events = 1; + cs.report_vfork_events = 1; } else if (strcmp (p, "exec-events+") == 0) { /* GDB supports and wants exec events if possible. */ if (target_supports_exec_events ()) - report_exec_events = 1; + cs.report_exec_events = 1; } else if (strcmp (p, "vContSupported+") == 0) - vCont_supported = 1; + cs.vCont_supported = 1; + else if (strcmp (p, "QThreadEvents+") == 0) + ; + else if (strcmp (p, "no-resumed+") == 0) + { + /* GDB supports and wants TARGET_WAITKIND_NO_RESUMED + events. */ + report_no_resumed = 1; + } else - target_process_qsupported (p); - - free (p); + { + /* Move the unknown features all together. */ + qsupported[i] = NULL; + qsupported[unknown] = p; + unknown++; + } } + /* Give the target backend a chance to process the unknown + features. */ + target_process_qsupported (qsupported, unknown); + + for (i = 0; i < count; i++) + free (qsupported[i]); free (qsupported); } sprintf (own_buf, - "PacketSize=%x;QPassSignals+;QProgramSignals+", + "PacketSize=%x;QPassSignals+;QProgramSignals+;" + "QStartupWithShell+;QEnvironmentHexEncoded+;" + "QEnvironmentReset+;QEnvironmentUnset+;" + "QSetWorkingDir+", PBUFSIZ - 1); + if (target_supports_catch_syscall ()) + strcat (own_buf, ";QCatchSyscalls+"); + if (the_target->qxfer_libraries_svr4 != NULL) strcat (own_buf, ";qXfer:libraries-svr4:read+" ";augmented-libraries-svr4-read+"); @@ -2166,7 +2409,7 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) qXfer:feature:read at all, we will never be re-queried. */ strcat (own_buf, ";qXfer:features:read+"); - if (transport_is_reliable) + if (cs.transport_is_reliable) strcat (own_buf, ";QStartNoAckMode+"); if (the_target->qxfer_osdata != NULL) @@ -2209,13 +2452,9 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) strcat (own_buf, ";tracenz+"); } - if (target_supports_hardware_single_step ()) + if (target_supports_hardware_single_step () + || target_supports_software_single_step () ) { - /* Support target-side breakpoint conditions and commands. - GDBserver needs to step over the breakpoint if the condition - is false. GDBserver software single step is too simple, so - disable conditional breakpoints if the target doesn't have - hardware single step. */ strcat (own_buf, ";ConditionalBreakpoints+"); } strcat (own_buf, ";BreakpointCommands+"); @@ -2236,6 +2475,10 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) strcat (own_buf, ";vContSupported+"); + strcat (own_buf, ";QThreadEvents+"); + + strcat (own_buf, ";no-resumed+"); + /* Reinitialize components as needed for the new connection. */ hostio_handle_new_gdb_connection (); target_handle_new_gdb_connection (); @@ -2252,7 +2495,7 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) int i, err; ptid_t ptid = null_ptid; - require_running (own_buf); + require_running_or_return (own_buf); for (i = 0; i < 3; i++) { @@ -2312,7 +2555,7 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) if (the_target->get_tib_address != NULL && startswith (own_buf, "qGetTIBAddr:")) { - char *annex; + const char *annex; int n; CORE_ADDR tlb; ptid_t ptid = read_ptid (own_buf + 12, &annex); @@ -2365,7 +2608,7 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) if (startswith (own_buf, "qSearch:memory:")) { - require_running (own_buf); + require_running_or_return (own_buf); handle_search_memory (own_buf, packet_len); return; } @@ -2378,12 +2621,11 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) if (own_buf[sizeof ("qAttached") - 1]) { int pid = strtoul (own_buf + sizeof ("qAttached:") - 1, NULL, 16); - process = (struct process_info *) - find_inferior_id (&all_processes, pid_to_ptid (pid)); + process = find_process_pid (pid); } else { - require_running (own_buf); + require_running_or_return (own_buf); process = current_process (); } @@ -2400,12 +2642,12 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) if (startswith (own_buf, "qCRC:")) { /* CRC check (compare-section). */ - char *comma; + const char *comma; ULONGEST base; int len; unsigned long long crc; - require_running (own_buf); + require_running_or_return (own_buf); comma = unpack_varlen_hex (own_buf + 5, &base); if (*comma++ != ',') { @@ -2442,48 +2684,33 @@ static void resume (struct thread_resume *actions, size_t n); typedef int (visit_actioned_threads_callback_ftype) (const struct thread_resume *, struct thread_info *); -/* Struct to pass data to visit_actioned_threads. */ - -struct visit_actioned_threads_data -{ - const struct thread_resume *actions; - size_t num_actions; - visit_actioned_threads_callback_ftype *callback; -}; - /* Call CALLBACK for any thread to which ACTIONS applies to. Returns true if CALLBACK returns true. Returns false if no matching thread is found or CALLBACK results false. - Note: This function is itself a callback for find_inferior. */ + Note: This function is itself a callback for find_thread. */ -static int -visit_actioned_threads (struct inferior_list_entry *entry, void *datap) +static bool +visit_actioned_threads (thread_info *thread, + const struct thread_resume *actions, + size_t num_actions, + visit_actioned_threads_callback_ftype *callback) { - struct visit_actioned_threads_data *data - = (struct visit_actioned_threads_data *) datap; - const struct thread_resume *actions = data->actions; - size_t num_actions = data->num_actions; - visit_actioned_threads_callback_ftype *callback = data->callback; - size_t i; - - for (i = 0; i < num_actions; i++) + for (size_t i = 0; i < num_actions; i++) { const struct thread_resume *action = &actions[i]; - if (ptid_equal (action->thread, minus_one_ptid) - || ptid_equal (action->thread, entry->id) - || ((ptid_get_pid (action->thread) - == ptid_get_pid (entry->id)) - && ptid_get_lwp (action->thread) == -1)) + if (action->thread == minus_one_ptid + || action->thread == thread->id + || ((action->thread.pid () + == thread->id.pid ()) + && action->thread.lwp () == -1)) { - struct thread_info *thread = (struct thread_info *) entry; - if ((*callback) (action, thread)) - return 1; + return true; } } - return 0; + return false; } /* Callback for visit_actioned_threads. If the thread has a pending @@ -2493,26 +2720,27 @@ static int handle_pending_status (const struct thread_resume *resumption, struct thread_info *thread) { + client_state &cs = get_client_state (); if (thread->status_pending_p) { thread->status_pending_p = 0; - last_status = thread->last_status; - last_ptid = thread->entry.id; - prepare_resume_reply (own_buf, last_ptid, &last_status); + cs.last_status = thread->last_status; + cs.last_ptid = thread->id; + prepare_resume_reply (cs.own_buf, cs.last_ptid, &cs.last_status); return 1; } return 0; } /* Parse vCont packets. */ -void +static void handle_v_cont (char *own_buf) { - char *p, *q; + const char *p; 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. */ @@ -2548,15 +2776,15 @@ handle_v_cont (char *own_buf) if (p[0] == 'S' || p[0] == 'C') { - int sig; - sig = strtol (p + 1, &q, 16); + char *q; + int sig = strtol (p + 1, &q, 16); if (p == q) goto err; p = q; - if (!gdb_signal_to_host_p (sig)) + if (!gdb_signal_to_host_p ((enum gdb_signal) sig)) goto err; - resume_info[i].sig = gdb_signal_to_host (sig); + resume_info[i].sig = gdb_signal_to_host ((enum gdb_signal) sig); } else if (p[0] == 'r') { @@ -2586,6 +2814,7 @@ handle_v_cont (char *own_buf) } else if (p[0] == ':') { + const char *q; ptid_t ptid = read_ptid (p + 1, &q); if (p == q) @@ -2618,18 +2847,21 @@ err: static void resume (struct thread_resume *actions, size_t num_actions) { + client_state &cs = get_client_state (); if (!non_stop) { /* Check if among the threads that GDB wants actioned, there's one with a pending status to report. If so, skip actually resuming/stopping and report the pending event immediately. */ - struct visit_actioned_threads_data data; - data.actions = actions; - data.num_actions = num_actions; - data.callback = handle_pending_status; - if (find_inferior (&all_threads, visit_actioned_threads, &data) != NULL) + thread_info *thread_with_status = find_thread ([&] (thread_info *thread) + { + return visit_actioned_threads (thread, actions, num_actions, + handle_pending_status); + }); + + if (thread_with_status != NULL) return; enable_async_io (); @@ -2638,43 +2870,45 @@ resume (struct thread_resume *actions, size_t num_actions) (*the_target->resume) (actions, num_actions); if (non_stop) - write_ok (own_buf); + write_ok (cs.own_buf); else { - last_ptid = mywait (minus_one_ptid, &last_status, 0, 1); + cs.last_ptid = mywait (minus_one_ptid, &cs.last_status, 0, 1); - if (last_status.kind == TARGET_WAITKIND_NO_RESUMED) + if (cs.last_status.kind == TARGET_WAITKIND_NO_RESUMED + && !report_no_resumed) { - /* No proper RSP support for this yet. At least return - error. */ - sprintf (own_buf, "E.No unwaited-for children left."); + /* The client does not support this stop reply. At least + return error. */ + sprintf (cs.own_buf, "E.No unwaited-for children left."); disable_async_io (); return; } - if (last_status.kind != TARGET_WAITKIND_EXITED - && last_status.kind != TARGET_WAITKIND_SIGNALLED - && last_status.kind != TARGET_WAITKIND_NO_RESUMED) - current_thread->last_status = last_status; + if (cs.last_status.kind != TARGET_WAITKIND_EXITED + && cs.last_status.kind != TARGET_WAITKIND_SIGNALLED + && cs.last_status.kind != TARGET_WAITKIND_NO_RESUMED) + current_thread->last_status = cs.last_status; /* From the client's perspective, all-stop mode always stops all threads implicitly (and the target backend has already done so by now). Tag all threads as "want-stopped", so we don't resume them implicitly without the client telling us to. */ gdb_wants_all_threads_stopped (); - prepare_resume_reply (own_buf, last_ptid, &last_status); + prepare_resume_reply (cs.own_buf, cs.last_ptid, &cs.last_status); disable_async_io (); - if (last_status.kind == TARGET_WAITKIND_EXITED - || last_status.kind == TARGET_WAITKIND_SIGNALLED) - mourn_inferior (find_process_pid (ptid_get_pid (last_ptid))); + if (cs.last_status.kind == TARGET_WAITKIND_EXITED + || cs.last_status.kind == TARGET_WAITKIND_SIGNALLED) + target_mourn_inferior (cs.last_ptid); } } /* Attach to a new program. Return 1 if successful, 0 if failure. */ -int +static int handle_v_attach (char *own_buf) { + client_state &cs = get_client_state (); int pid; pid = strtol (own_buf + 8, NULL, 16); @@ -2694,7 +2928,7 @@ handle_v_attach (char *own_buf) write_ok (own_buf); } else - prepare_resume_reply (own_buf, last_ptid, &last_status); + prepare_resume_reply (own_buf, cs.last_ptid, &cs.last_status); return 1; } @@ -2709,7 +2943,10 @@ handle_v_attach (char *own_buf) static int handle_v_run (char *own_buf) { - char *p, *next_p, **new_argv; + client_state &cs = get_client_state (); + char *p, *next_p; + std::vector new_argv; + char *new_program_name = NULL; int i, new_argc; new_argc = 0; @@ -2719,71 +2956,109 @@ handle_v_run (char *own_buf) 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) - { - write_enn (own_buf); - freeargv (new_argv); - return 0; - } - - new_argv[0] = strdup (program_argv[0]); - if (new_argv[0] == NULL) + if (program_path.get () == NULL) { write_enn (own_buf); - freeargv (new_argv); + free_vector_argv (new_argv); return 0; } } + else + program_path.set (gdb::unique_xmalloc_ptr (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_path.get (), program_args); - start_inferior (program_argv); - if (last_status.kind == TARGET_WAITKIND_STOPPED) + if (cs.last_status.kind == TARGET_WAITKIND_STOPPED) { - prepare_resume_reply (own_buf, last_ptid, &last_status); + prepare_resume_reply (own_buf, cs.last_ptid, &cs.last_status); /* In non-stop, sending a resume reply doesn't set the general thread, but GDB assumes a vRun sets it (this is so GDB can query which is the main thread of the new inferior. */ if (non_stop) - general_thread = last_ptid; + cs.general_thread = cs.last_ptid; return 1; } @@ -2795,21 +3070,25 @@ handle_v_run (char *own_buf) } /* Kill process. Return 1 if successful, 0 if failure. */ -int +static int handle_v_kill (char *own_buf) { + client_state &cs = get_client_state (); int pid; char *p = &own_buf[6]; - if (multi_process) + if (cs.multi_process) pid = strtol (p, NULL, 16); else pid = signal_pid; - if (pid != 0 && kill_inferior (pid) == 0) + + process_info *proc = find_process_pid (pid); + + if (proc != nullptr && kill_inferior (proc) == 0) { - last_status.kind = TARGET_WAITKIND_SIGNALLED; - last_status.value.sig = GDB_SIGNAL_KILL; - last_ptid = pid_to_ptid (pid); - discard_queued_stop_replies (last_ptid); + cs.last_status.kind = TARGET_WAITKIND_SIGNALLED; + cs.last_status.value.sig = GDB_SIGNAL_KILL; + cs.last_ptid = ptid_t (pid); + discard_queued_stop_replies (cs.last_ptid); write_ok (own_buf); return 1; } @@ -2824,11 +3103,18 @@ handle_v_kill (char *own_buf) void handle_v_requests (char *own_buf, int packet_len, int *new_packet_len) { + client_state &cs = get_client_state (); if (!disable_packet_vCont) { + if (strcmp (own_buf, "vCtrlC") == 0) + { + (*the_target->request_interrupt) (); + write_ok (own_buf); + return; + } + if (startswith (own_buf, "vCont;")) { - require_running (own_buf); handle_v_cont (own_buf); return; } @@ -2837,12 +3123,15 @@ handle_v_requests (char *own_buf, int packet_len, int *new_packet_len) { strcpy (own_buf, "vCont;c;C;t"); - if (target_supports_hardware_single_step () || !vCont_supported) + if (target_supports_hardware_single_step () + || target_supports_software_single_step () + || !cs.vCont_supported) { - /* If target supports hardware single step, add actions s - and S to the list of supported actions. On the other - hand, if GDB doesn't request the supported vCont actions - in qSupported packet, add s and S to the list too. */ + /* If target supports single step either by hardware or by + software, add actions s and S to the list of supported + actions. On the other hand, if GDB doesn't request the + supported vCont actions in qSupported packet, add s and + S to the list too. */ own_buf = own_buf + strlen (own_buf); strcpy (own_buf, ";s;S"); } @@ -2862,7 +3151,7 @@ handle_v_requests (char *own_buf, int packet_len, int *new_packet_len) if (startswith (own_buf, "vAttach;")) { - if ((!extended_protocol || !multi_process) && target_running ()) + if ((!extended_protocol || !cs.multi_process) && target_running ()) { fprintf (stderr, "Already debugging a process\n"); write_enn (own_buf); @@ -2874,7 +3163,7 @@ handle_v_requests (char *own_buf, int packet_len, int *new_packet_len) if (startswith (own_buf, "vRun;")) { - if ((!extended_protocol || !multi_process) && target_running ()) + if ((!extended_protocol || !cs.multi_process) && target_running ()) { fprintf (stderr, "Already debugging a process\n"); write_enn (own_buf); @@ -2911,12 +3200,13 @@ handle_v_requests (char *own_buf, int packet_len, int *new_packet_len) static void myresume (char *own_buf, int step, int sig) { + client_state &cs = get_client_state (); struct thread_resume resume_info[2]; int n = 0; int valid_cont_thread; - valid_cont_thread = (!ptid_equal (cont_thread, null_ptid) - && !ptid_equal (cont_thread, minus_one_ptid)); + valid_cont_thread = (cs.cont_thread != null_ptid + && cs.cont_thread != minus_one_ptid); if (step || sig || valid_cont_thread) { @@ -2940,21 +3230,19 @@ myresume (char *own_buf, int step, int sig) resume (resume_info, n); } -/* Callback for for_each_inferior. Make a new stop reply for each +/* Callback for for_each_thread. Make a new stop reply for each stopped thread. */ -static int -queue_stop_reply_callback (struct inferior_list_entry *entry, void *arg) +static void +queue_stop_reply_callback (thread_info *thread) { - struct thread_info *thread = (struct thread_info *) entry; - /* For now, assume targets that don't have this callback also don't manage the thread's last_status field. */ if (the_target->thread_stopped == NULL) { struct vstop_notif *new_notif = XNEW (struct vstop_notif); - new_notif->ptid = entry->id; + new_notif->ptid = thread->id; new_notif->status = thread->last_status; /* Pass the last stop reply back to GDB, but don't notify yet. */ @@ -2967,25 +3255,21 @@ queue_stop_reply_callback (struct inferior_list_entry *entry, void *arg) { 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); + target_pid_to_str (thread->id), + status_string.c_str ()); } gdb_assert (thread->last_status.kind != TARGET_WAITKIND_IGNORE); /* Pass the last stop reply back to GDB, but don't notify yet. */ - queue_stop_reply (entry->id, &thread->last_status); + queue_stop_reply (thread->id, &thread->last_status); } } - - return 0; } /* Set this inferior threads's state as "want-stopped". We won't @@ -2993,10 +3277,8 @@ queue_stop_reply_callback (struct inferior_list_entry *entry, void *arg) it. */ static void -gdb_wants_thread_stopped (struct inferior_list_entry *entry) +gdb_wants_thread_stopped (thread_info *thread) { - struct thread_info *thread = (struct thread_info *) entry; - thread->last_resume_kind = resume_stop; if (thread->last_status.kind == TARGET_WAITKIND_IGNORE) @@ -3013,38 +3295,15 @@ gdb_wants_thread_stopped (struct inferior_list_entry *entry) static void gdb_wants_all_threads_stopped (void) { - for_each_inferior (&all_threads, gdb_wants_thread_stopped); -} - -/* Clear the gdb_detached flag of every process. */ - -static void -gdb_reattached_process (struct inferior_list_entry *entry) -{ - struct process_info *process = (struct process_info *) entry; - - process->gdb_detached = 0; -} - -/* Callback for for_each_inferior. Clear the thread's pending status - flag. */ - -static void -clear_pending_status_callback (struct inferior_list_entry *entry) -{ - struct thread_info *thread = (struct thread_info *) entry; - - thread->status_pending_p = 0; + for_each_thread (gdb_wants_thread_stopped); } -/* Callback for for_each_inferior. If the thread is stopped with an +/* Callback for for_each_thread. If the thread is stopped with an interesting event, mark it as having a pending event. */ static void -set_pending_status_callback (struct inferior_list_entry *entry) +set_pending_status_callback (thread_info *thread) { - struct thread_info *thread = (struct thread_info *) entry; - if (thread->last_status.kind != TARGET_WAITKIND_STOPPED || (thread->last_status.value.sig != GDB_SIGNAL_0 /* A breakpoint, watchpoint or finished step from a previous @@ -3057,24 +3316,17 @@ set_pending_status_callback (struct inferior_list_entry *entry) thread->status_pending_p = 1; } -/* Callback for find_inferior. Return true if ENTRY (a thread) has a - pending status to report to GDB. */ - -static int -find_status_pending_thread_callback (struct inferior_list_entry *entry, void *data) -{ - struct thread_info *thread = (struct thread_info *) entry; - - return thread->status_pending_p; -} - /* Status handler for the '?' packet. */ static void handle_status (char *own_buf) { + client_state &cs = get_client_state (); + /* GDB is connected, don't forward events to the target anymore. */ - for_each_inferior (&all_processes, gdb_reattached_process); + for_each_process ([] (process_info *process) { + process->gdb_detached = 0; + }); /* In non-stop mode, we must send a stop reply for each stopped thread. In all-stop mode, just send one for the first stopped @@ -3082,16 +3334,16 @@ handle_status (char *own_buf) if (non_stop) { - find_inferior (&all_threads, queue_stop_reply_callback, NULL); + for_each_thread (queue_stop_reply_callback); /* The first is sent immediatly. OK is sent if there is no stopped thread, which is the same handling of the vStopped packet (by design). */ - notif_write_event (¬if_stop, own_buf); + notif_write_event (¬if_stop, cs.own_buf); } else { - struct inferior_list_entry *thread = NULL; + thread_info *thread = NULL; pause_all (0); stabilize_threads (); @@ -3103,25 +3355,27 @@ handle_status (char *own_buf) reporting now pending. They'll be reported the next time the threads are resumed. Start by marking all interesting events as pending. */ - for_each_inferior (&all_threads, set_pending_status_callback); + for_each_thread (set_pending_status_callback); /* Prefer the last thread that reported an event to GDB (even if that was a GDB_SIGNAL_TRAP). */ - if (last_status.kind != TARGET_WAITKIND_IGNORE - && last_status.kind != TARGET_WAITKIND_EXITED - && last_status.kind != TARGET_WAITKIND_SIGNALLED) - thread = find_inferior_id (&all_threads, last_ptid); + if (cs.last_status.kind != TARGET_WAITKIND_IGNORE + && cs.last_status.kind != TARGET_WAITKIND_EXITED + && cs.last_status.kind != TARGET_WAITKIND_SIGNALLED) + thread = find_thread_ptid (cs.last_ptid); /* If the last event thread is not found for some reason, look for some other thread that might have an event to report. */ if (thread == NULL) - thread = find_inferior (&all_threads, - find_status_pending_thread_callback, NULL); + thread = find_thread ([] (thread_info *thr_arg) + { + return thr_arg->status_pending_p; + }); /* If we're still out of luck, simply pick the first thread in the thread list. */ if (thread == NULL) - thread = get_first_inferior (&all_threads); + thread = get_first_thread (); if (thread != NULL) { @@ -3133,11 +3387,11 @@ handle_status (char *own_buf) /* GDB assumes the current thread is the thread we're reporting the status for. */ - general_thread = thread->id; - set_desired_thread (1); + cs.general_thread = thread->id; + set_desired_thread (); gdb_assert (tp->last_status.kind != TARGET_WAITKIND_IGNORE); - prepare_resume_reply (own_buf, tp->entry.id, &tp->last_status); + prepare_resume_reply (own_buf, tp->id, &tp->last_status); } else strcpy (own_buf, "W00"); @@ -3148,7 +3402,7 @@ static void gdbserver_version (void) { printf ("GNU gdbserver %s%s\n" - "Copyright (C) 2015 Free Software Foundation, Inc.\n" + "Copyright (C) 2018 Free Software Foundation, Inc.\n" "gdbserver is free software, covered by the " "GNU General Public License.\n" "This gdbserver was configured as \"%s\"\n", @@ -3185,6 +3439,13 @@ gdbserver_usage (FILE *stream) " --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" @@ -3220,83 +3481,11 @@ gdbserver_show_disableable (FILE *stream) " threads \tAll of the above\n"); } - -#undef require_running -#define require_running(BUF) \ - if (!target_running ()) \ - { \ - write_enn (BUF); \ - break; \ - } - -static int -first_thread_of (struct inferior_list_entry *entry, void *args) -{ - int pid = * (int *) args; - - if (ptid_get_pid (entry->id) == pid) - return 1; - - return 0; -} - -static void -kill_inferior_callback (struct inferior_list_entry *entry) -{ - struct process_info *process = (struct process_info *) entry; - int pid = ptid_get_pid (process->entry.id); - - kill_inferior (pid); - discard_queued_stop_replies (pid_to_ptid (pid)); -} - -/* Callback for for_each_inferior to detach or kill the inferior, - depending on whether we attached to it or not. - We inform the user whether we're detaching or killing the process - as this is only called when gdbserver is about to exit. */ - -static void -detach_or_kill_inferior_callback (struct inferior_list_entry *entry) -{ - struct process_info *process = (struct process_info *) entry; - int pid = ptid_get_pid (process->entry.id); - - if (process->attached) - detach_inferior (pid); - else - kill_inferior (pid); - - discard_queued_stop_replies (pid_to_ptid (pid)); -} - -/* for_each_inferior callback for detach_or_kill_for_exit to print - the pids of started inferiors. */ - -static void -print_started_pid (struct inferior_list_entry *entry) -{ - struct process_info *process = (struct process_info *) entry; - - if (! process->attached) - { - int pid = ptid_get_pid (process->entry.id); - fprintf (stderr, " %d", pid); - } -} - -/* for_each_inferior callback for detach_or_kill_for_exit to print - the pids of attached inferiors. */ - static void -print_attached_pid (struct inferior_list_entry *entry) +kill_inferior_callback (process_info *process) { - struct process_info *process = (struct process_info *) entry; - - if (process->attached) - { - int pid = ptid_get_pid (process->entry.id); - fprintf (stderr, " %d", pid); - } + kill_inferior (process); + discard_queued_stop_replies (ptid_t (process->pid)); } /* Call this when exiting gdbserver with possible inferiors that need @@ -3313,19 +3502,37 @@ detach_or_kill_for_exit (void) if (have_started_inferiors_p ()) { fprintf (stderr, "Killing process(es):"); - for_each_inferior (&all_processes, print_started_pid); + + for_each_process ([] (process_info *process) { + if (!process->attached) + fprintf (stderr, " %d", process->pid); + }); + fprintf (stderr, "\n"); } if (have_attached_inferiors_p ()) { fprintf (stderr, "Detaching process(es):"); - for_each_inferior (&all_processes, print_attached_pid); + + for_each_process ([] (process_info *process) { + if (process->attached) + fprintf (stderr, " %d", process->pid); + }); + fprintf (stderr, "\n"); } /* Now we can kill or detach the inferiors. */ + for_each_process ([] (process_info *process) { + int pid = process->pid; - for_each_inferior (&all_processes, detach_or_kill_inferior_callback); + if (process->attached) + detach_inferior (process); + else + kill_inferior (process); + + discard_queued_stop_replies (ptid_t (pid)); + }); } /* Value that will be passed to exit(3) when gdbserver exits. */ @@ -3359,11 +3566,25 @@ captured_main (int argc, char *argv[]) { 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; +#if GDB_SELF_TEST + const char *selftest_filter = NULL; +#endif + + current_directory = getcwd (NULL, 0); + client_state &cs = get_client_state (); + + if (current_directory == NULL) + { + error (_("Could not find current working directory: %s"), + safe_strerror (errno)); + } while (*next_arg != NULL && **next_arg == '-') { @@ -3383,13 +3604,25 @@ captured_main (int argc, char *argv[]) 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 (!wrapper_argv.empty ()) + { + /* Erase the last whitespace. */ + wrapper_argv.erase (wrapper_argv.end () - 1); + } - if (next_arg == wrapper_argv || *next_arg == NULL) + if (next_arg == tmp || *next_arg == NULL) { gdbserver_usage (stderr); exit (1); @@ -3402,13 +3635,13 @@ captured_main (int argc, char *argv[]) debug_threads = 1; else if (startswith (*next_arg, "--debug-format=")) { - char *error_msg + std::string error_msg = parse_debug_format_options ((*next_arg) + sizeof ("--debug-format=") - 1, 0); - if (error_msg != NULL) + if (!error_msg.empty ()) { - fprintf (stderr, "%s", error_msg); + fprintf (stderr, "%s", error_msg.c_str ()); exit (1); } } @@ -3456,15 +3689,29 @@ captured_main (int argc, char *argv[]) { /* "-" 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; + cs.disable_randomization = 1; else if (strcmp (*next_arg, "--no-disable-randomization") == 0) - disable_randomization = 0; + cs.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 if (startswith (*next_arg, "--selftest=")) + { + selftest = true; +#if GDB_SELF_TEST + selftest_filter = *next_arg + strlen ("--selftest="); +#endif + } else { fprintf (stderr, "Unknown argument: %s\n", *next_arg); @@ -3475,9 +3722,13 @@ captured_main (int argc, char *argv[]) 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); @@ -3487,11 +3738,14 @@ captured_main (int argc, char *argv[]) opened by remote_prepare. */ notice_open_fds (); + save_original_signals_state (false); + /* We need to know whether the remote connection is stdio before 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; @@ -3518,28 +3772,41 @@ captured_main (int argc, char *argv[]) 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 (); initialize_notif (); - own_buf = (char *) xmalloc (PBUFSIZ + 1); mem_buf = (unsigned char *) xmalloc (PBUFSIZ); + if (selftest) + { +#if GDB_SELF_TEST + selftests::run_tests (selftest_filter); +#else + printf (_("Selftests have been disabled for this build.\n")); +#endif + 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_path.set (gdb::unique_xmalloc_ptr (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_path.get (), program_args); /* We are now (hopefully) stopped at the first instruction of the target process. This assumes that the target process was @@ -3554,9 +3821,9 @@ captured_main (int argc, char *argv[]) } else { - last_status.kind = TARGET_WAITKIND_EXITED; - last_status.value.integer = 0; - last_ptid = minus_one_ptid; + cs.last_status.kind = TARGET_WAITKIND_EXITED; + cs.last_status.value.integer = 0; + cs.last_ptid = minus_one_ptid; } make_cleanup (detach_or_kill_for_exit_cleanup, NULL); @@ -3565,8 +3832,8 @@ captured_main (int argc, char *argv[]) shared library event" notice on gdb side. */ dlls_changed = 0; - if (last_status.kind == TARGET_WAITKIND_EXITED - || last_status.kind == TARGET_WAITKIND_SIGNALLED) + if (cs.last_status.kind == TARGET_WAITKIND_EXITED + || cs.last_status.kind == TARGET_WAITKIND_SIGNALLED) was_running = 0; else was_running = 1; @@ -3576,18 +3843,17 @@ captured_main (int argc, char *argv[]) while (1) { - - noack_mode = 0; - multi_process = 0; - report_fork_events = 0; - report_vfork_events = 0; - report_exec_events = 0; + cs.noack_mode = 0; + cs.multi_process = 0; + cs.report_fork_events = 0; + cs.report_vfork_events = 0; + cs.report_exec_events = 0; /* Be sure we're out of tfind mode. */ - current_traceframe = -1; - cont_thread = null_ptid; - swbreak_feature = 0; - hwbreak_feature = 0; - vCont_supported = 0; + cs.current_traceframe = -1; + cs.cont_thread = null_ptid; + cs.swbreak_feature = 0; + cs.hwbreak_feature = 0; + cs.vCont_supported = 0; remote_open (port); @@ -3598,11 +3864,21 @@ captured_main (int argc, char *argv[]) start_event_loop (); /* If an exit was requested (using the "monitor exit" - command), terminate now. The only other way to get - here is for getpkt to fail; close the connection - and reopen it at the top of the loop. */ + command), terminate now. */ + if (exit_requested) + throw_quit ("Quit"); + + /* The only other way to get here is for getpkt to fail: + + - If --once was specified, we're done. + + - If not in extended-remote mode, and we're no longer + debugging anything, simply exit: GDB has disconnected + after processing the last process exit. - if (exit_requested || run_once) + - Otherwise, close the connection and reopen it at the + top of the loop. */ + if (run_once || (!extended_protocol && !target_running ())) throw_quit ("Quit"); fprintf (stderr, @@ -3613,8 +3889,10 @@ captured_main (int argc, char *argv[]) (by the same GDB instance or another) will refresh all its state from scratch. */ discard_queued_stop_replies (minus_one_ptid); - for_each_inferior (&all_threads, - clear_pending_status_callback); + for_each_thread ([] (thread_info *thread) + { + thread->status_pending_p = 0; + }); if (tracing) { @@ -3651,8 +3929,8 @@ captured_main (int argc, char *argv[]) if (response_needed) { - write_enn (own_buf); - putpkt (own_buf); + write_enn (cs.own_buf); + putpkt (cs.own_buf); } if (run_once) @@ -3689,23 +3967,14 @@ main (int argc, char *argv[]) gdb_assert_not_reached ("captured_main should never return"); } -/* Skip PACKET until the next semi-colon (or end of string). */ - -static void -skip_to_semicolon (char **packet) -{ - while (**packet != '\0' && **packet != ';') - (*packet)++; -} - /* Process options coming from Z packets for a breakpoint. PACKET is the packet buffer. *PACKET is updated to point to the first char after the last processed option. */ static void -process_point_options (struct breakpoint *bp, char **packet) +process_point_options (struct gdb_breakpoint *bp, const char **packet) { - char *dataptr = *packet; + const char *dataptr = *packet; int persist; /* Check if data has the correct format. */ @@ -3725,7 +3994,7 @@ process_point_options (struct breakpoint *bp, char **packet) if (debug_threads) debug_printf ("Found breakpoint condition.\n"); if (!add_breakpoint_condition (bp, &dataptr)) - skip_to_semicolon (&dataptr); + dataptr = strchrnul (dataptr, ';'); } else if (startswith (dataptr, "cmds:")) { @@ -3735,14 +4004,14 @@ process_point_options (struct breakpoint *bp, char **packet) persist = (*dataptr == '1'); dataptr += 2; if (add_breakpoint_commands (bp, &dataptr, persist)) - skip_to_semicolon (&dataptr); + dataptr = strchrnul (dataptr, ';'); } else { fprintf (stderr, "Unknown token %c, ignoring.\n", *dataptr); /* Skip tokens until we find one that we recognize. */ - skip_to_semicolon (&dataptr); + dataptr = strchrnul (dataptr, ';'); } } *packet = dataptr; @@ -3756,28 +4025,18 @@ process_point_options (struct breakpoint *bp, char **packet) static int process_serial_event (void) { - char ch; - int i = 0; + client_state &cs = get_client_state (); int signal; unsigned int len; - int res; CORE_ADDR mem_addr; - int pid; unsigned char sig; int packet_len; int new_packet_len = -1; - /* Used to decide when gdbserver should exit in - multi-mode/remote. */ - static int have_ran = 0; - - if (!have_ran) - have_ran = target_running (); - disable_async_io (); response_needed = 0; - packet_len = getpkt (own_buf); + packet_len = getpkt (cs.own_buf); if (packet_len <= 0) { remote_close (); @@ -3786,282 +4045,186 @@ process_serial_event (void) } response_needed = 1; - i = 0; - ch = own_buf[i++]; + char ch = cs.own_buf[0]; switch (ch) { case 'q': - handle_query (own_buf, packet_len, &new_packet_len); + handle_query (cs.own_buf, packet_len, &new_packet_len); break; case 'Q': - handle_general_set (own_buf); + handle_general_set (cs.own_buf); break; case 'D': - require_running (own_buf); - - if (multi_process) - { - i++; /* skip ';' */ - pid = strtol (&own_buf[i], NULL, 16); - } - else - pid = ptid_get_pid (current_ptid); - - if ((tracing && disconnected_tracing) || any_persistent_commands ()) - { - struct thread_resume resume_info; - struct process_info *process = find_process_pid (pid); - - if (process == NULL) - { - write_enn (own_buf); - break; - } - - if (tracing && disconnected_tracing) - fprintf (stderr, - "Disconnected tracing in effect, " - "leaving gdbserver attached to the process\n"); - - if (any_persistent_commands ()) - fprintf (stderr, - "Persistent commands are present, " - "leaving gdbserver attached to the process\n"); - - /* Make sure we're in non-stop/async mode, so we we can both - wait for an async socket accept, and handle async target - events simultaneously. There's also no point either in - having the target stop all threads, when we're going to - pass signals down without informing GDB. */ - if (!non_stop) - { - if (debug_threads) - debug_printf ("Forcing non-stop mode\n"); - - non_stop = 1; - start_non_stop (1); - } - - process->gdb_detached = 1; - - /* Detaching implicitly resumes all threads. */ - resume_info.thread = minus_one_ptid; - resume_info.kind = resume_continue; - resume_info.sig = 0; - (*the_target->resume) (&resume_info, 1); - - write_ok (own_buf); - break; /* from switch/case */ - } - - fprintf (stderr, "Detaching from process %d\n", pid); - stop_tracing (); - if (detach_inferior (pid) != 0) - write_enn (own_buf); - else - { - discard_queued_stop_replies (pid_to_ptid (pid)); - write_ok (own_buf); - - if (extended_protocol) - { - /* Treat this like a normal program exit. */ - last_status.kind = TARGET_WAITKIND_EXITED; - last_status.value.integer = 0; - last_ptid = pid_to_ptid (pid); - - current_thread = NULL; - } - else - { - putpkt (own_buf); - remote_close (); - - /* If we are attached, then we can exit. Otherwise, we - need to hang around doing nothing, until the child is - gone. */ - join_inferior (pid); - exit (0); - } - } + handle_detach (cs.own_buf); break; case '!': extended_protocol = 1; - write_ok (own_buf); + write_ok (cs.own_buf); break; case '?': - handle_status (own_buf); + handle_status (cs.own_buf); break; case 'H': - if (own_buf[1] == 'c' || own_buf[1] == 'g' || own_buf[1] == 's') + if (cs.own_buf[1] == 'c' || cs.own_buf[1] == 'g' || cs.own_buf[1] == 's') { - ptid_t gdb_id, thread_id; - int pid; - - require_running (own_buf); + require_running_or_break (cs.own_buf); - gdb_id = read_ptid (&own_buf[2], NULL); + ptid_t thread_id = read_ptid (&cs.own_buf[2], NULL); - pid = ptid_get_pid (gdb_id); - - if (ptid_equal (gdb_id, null_ptid) - || ptid_equal (gdb_id, minus_one_ptid)) + if (thread_id == null_ptid || thread_id == minus_one_ptid) thread_id = null_ptid; - else if (pid != 0 - && ptid_equal (pid_to_ptid (pid), - gdb_id)) + else if (thread_id.is_pid ()) { - struct thread_info *thread = - (struct thread_info *) find_inferior (&all_threads, - first_thread_of, - &pid); - if (!thread) + /* The ptid represents a pid. */ + thread_info *thread = find_any_thread_of_pid (thread_id.pid ()); + + if (thread == NULL) { - write_enn (own_buf); + write_enn (cs.own_buf); break; } - thread_id = thread->entry.id; + thread_id = thread->id; } else { - thread_id = gdb_id_to_thread_id (gdb_id); - if (ptid_equal (thread_id, null_ptid)) + /* The ptid represents a lwp/tid. */ + if (find_thread_ptid (thread_id) == NULL) { - write_enn (own_buf); + write_enn (cs.own_buf); break; } } - if (own_buf[1] == 'g') + if (cs.own_buf[1] == 'g') { - if (ptid_equal (thread_id, null_ptid)) + if (thread_id == null_ptid) { /* GDB is telling us to choose any thread. Check if the currently selected thread is still valid. If it is not, select the first available. */ - struct thread_info *thread = - (struct thread_info *) find_inferior_id (&all_threads, - general_thread); + thread_info *thread = find_thread_ptid (cs.general_thread); if (thread == NULL) thread = get_first_thread (); - thread_id = thread->entry.id; + thread_id = thread->id; } - general_thread = thread_id; - set_desired_thread (1); + cs.general_thread = thread_id; + set_desired_thread (); gdb_assert (current_thread != NULL); } - else if (own_buf[1] == 'c') - cont_thread = thread_id; + else if (cs.own_buf[1] == 'c') + cs.cont_thread = thread_id; - write_ok (own_buf); + write_ok (cs.own_buf); } else { /* Silently ignore it so that gdb can extend the protocol without compatibility headaches. */ - own_buf[0] = '\0'; + cs.own_buf[0] = '\0'; } break; case 'g': - require_running (own_buf); - if (current_traceframe >= 0) + require_running_or_break (cs.own_buf); + if (cs.current_traceframe >= 0) { struct regcache *regcache = new_register_cache (current_target_desc ()); - if (fetch_traceframe_registers (current_traceframe, + if (fetch_traceframe_registers (cs.current_traceframe, regcache, -1) == 0) - registers_to_string (regcache, own_buf); + registers_to_string (regcache, cs.own_buf); else - write_enn (own_buf); + write_enn (cs.own_buf); free_register_cache (regcache); } else { struct regcache *regcache; - if (!set_desired_thread (1)) - write_enn (own_buf); + if (!set_desired_thread ()) + write_enn (cs.own_buf); else { regcache = get_thread_regcache (current_thread, 1); - registers_to_string (regcache, own_buf); + registers_to_string (regcache, cs.own_buf); } } break; case 'G': - require_running (own_buf); - if (current_traceframe >= 0) - write_enn (own_buf); + require_running_or_break (cs.own_buf); + if (cs.current_traceframe >= 0) + write_enn (cs.own_buf); else { struct regcache *regcache; - if (!set_desired_thread (1)) - write_enn (own_buf); + if (!set_desired_thread ()) + write_enn (cs.own_buf); else { regcache = get_thread_regcache (current_thread, 1); - registers_from_string (regcache, &own_buf[1]); - write_ok (own_buf); + registers_from_string (regcache, &cs.own_buf[1]); + write_ok (cs.own_buf); } } break; case 'm': - require_running (own_buf); - decode_m_packet (&own_buf[1], &mem_addr, &len); - res = gdb_read_memory (mem_addr, mem_buf, len); - if (res < 0) - write_enn (own_buf); - else - bin2hex (mem_buf, own_buf, res); + { + require_running_or_break (cs.own_buf); + decode_m_packet (&cs.own_buf[1], &mem_addr, &len); + int res = gdb_read_memory (mem_addr, mem_buf, len); + if (res < 0) + write_enn (cs.own_buf); + else + bin2hex (mem_buf, cs.own_buf, res); + } break; case 'M': - require_running (own_buf); - decode_M_packet (&own_buf[1], &mem_addr, &len, &mem_buf); + require_running_or_break (cs.own_buf); + decode_M_packet (&cs.own_buf[1], &mem_addr, &len, &mem_buf); if (gdb_write_memory (mem_addr, mem_buf, len) == 0) - write_ok (own_buf); + write_ok (cs.own_buf); else - write_enn (own_buf); + write_enn (cs.own_buf); break; case 'X': - require_running (own_buf); - if (decode_X_packet (&own_buf[1], packet_len - 1, + require_running_or_break (cs.own_buf); + if (decode_X_packet (&cs.own_buf[1], packet_len - 1, &mem_addr, &len, &mem_buf) < 0 || gdb_write_memory (mem_addr, mem_buf, len) != 0) - write_enn (own_buf); + write_enn (cs.own_buf); else - write_ok (own_buf); + write_ok (cs.own_buf); break; case 'C': - require_running (own_buf); - hex2bin (own_buf + 1, &sig, 1); - if (gdb_signal_to_host_p (sig)) - signal = gdb_signal_to_host (sig); + require_running_or_break (cs.own_buf); + hex2bin (cs.own_buf + 1, &sig, 1); + if (gdb_signal_to_host_p ((enum gdb_signal) sig)) + signal = gdb_signal_to_host ((enum gdb_signal) sig); else signal = 0; - myresume (own_buf, 0, signal); + myresume (cs.own_buf, 0, signal); break; case 'S': - require_running (own_buf); - hex2bin (own_buf + 1, &sig, 1); - if (gdb_signal_to_host_p (sig)) - signal = gdb_signal_to_host (sig); + require_running_or_break (cs.own_buf); + hex2bin (cs.own_buf + 1, &sig, 1); + if (gdb_signal_to_host_p ((enum gdb_signal) sig)) + signal = gdb_signal_to_host ((enum gdb_signal) sig); else signal = 0; - myresume (own_buf, 1, signal); + myresume (cs.own_buf, 1, signal); break; case 'c': - require_running (own_buf); + require_running_or_break (cs.own_buf); signal = 0; - myresume (own_buf, 0, signal); + myresume (cs.own_buf, 0, signal); break; case 's': - require_running (own_buf); + require_running_or_break (cs.own_buf); signal = 0; - myresume (own_buf, 1, signal); + myresume (cs.own_buf, 1, signal); break; case 'Z': /* insert_ ... */ /* Fallthrough. */ @@ -4070,17 +4233,17 @@ process_serial_event (void) char *dataptr; ULONGEST addr; int kind; - char type = own_buf[1]; + char type = cs.own_buf[1]; int res; const int insert = ch == 'Z'; - char *p = &own_buf[3]; + const char *p = &cs.own_buf[3]; p = unpack_varlen_hex (p, &addr); kind = strtol (p + 1, &dataptr, 16); if (insert) { - struct breakpoint *bp; + struct gdb_breakpoint *bp; bp = set_gdb_breakpoint (type, addr, kind, &res); if (bp != NULL) @@ -4093,19 +4256,20 @@ process_serial_event (void) is telling us to drop that list and use this one instead. */ clear_breakpoint_conditions_and_commands (bp); - process_point_options (bp, &dataptr); + const char *options = dataptr; + process_point_options (bp, &options); } } else res = delete_gdb_breakpoint (type, addr, kind); if (res == 0) - write_ok (own_buf); + write_ok (cs.own_buf); else if (res == 1) /* Unsupported. */ - own_buf[0] = '\0'; + cs.own_buf[0] = '\0'; else - write_enn (own_buf); + write_enn (cs.own_buf); break; } case 'k': @@ -4116,14 +4280,15 @@ process_serial_event (void) return 0; fprintf (stderr, "Killing all inferiors\n"); - for_each_inferior (&all_processes, kill_inferior_callback); + + for_each_process (kill_inferior_callback); /* When using the extended protocol, we wait with no program running. The traditional protocol will exit instead. */ if (extended_protocol) { - last_status.kind = TARGET_WAITKIND_EXITED; - last_status.value.sig = GDB_SIGNAL_KILL; + cs.last_status.kind = TARGET_WAITKIND_EXITED; + cs.last_status.value.sig = GDB_SIGNAL_KILL; return 0; } else @@ -4131,22 +4296,19 @@ process_serial_event (void) case 'T': { - ptid_t gdb_id, thread_id; + require_running_or_break (cs.own_buf); - require_running (own_buf); - - gdb_id = read_ptid (&own_buf[1], NULL); - thread_id = gdb_id_to_thread_id (gdb_id); - if (ptid_equal (thread_id, null_ptid)) + ptid_t thread_id = read_ptid (&cs.own_buf[1], NULL); + if (find_thread_ptid (thread_id) == NULL) { - write_enn (own_buf); + write_enn (cs.own_buf); break; } if (mythread_alive (thread_id)) - write_ok (own_buf); + write_ok (cs.own_buf); else - write_enn (own_buf); + write_enn (cs.own_buf); } break; case 'R': @@ -4157,30 +4319,31 @@ process_serial_event (void) if (extended_protocol) { if (target_running ()) - for_each_inferior (&all_processes, - kill_inferior_callback); + for_each_process (kill_inferior_callback); + fprintf (stderr, "GDBserver restarting\n"); /* Wait till we are at 1st instruction in prog. */ - if (program_argv != NULL) + if (program_path.get () != NULL) { - start_inferior (program_argv); - if (last_status.kind == TARGET_WAITKIND_STOPPED) + create_inferior (program_path.get (), program_args); + + if (cs.last_status.kind == TARGET_WAITKIND_STOPPED) { /* Stopped at the first instruction of the target process. */ - general_thread = last_ptid; + cs.general_thread = cs.last_ptid; } else { /* Something went wrong. */ - general_thread = null_ptid; + cs.general_thread = null_ptid; } } else { - last_status.kind = TARGET_WAITKIND_EXITED; - last_status.value.sig = GDB_SIGNAL_KILL; + cs.last_status.kind = TARGET_WAITKIND_EXITED; + cs.last_status.value.sig = GDB_SIGNAL_KILL; } return 0; } @@ -4189,44 +4352,29 @@ process_serial_event (void) /* It is a request we don't understand. Respond with an empty packet so that gdb knows that we don't support this request. */ - own_buf[0] = '\0'; + cs.own_buf[0] = '\0'; break; } case 'v': /* Extended (long) request. */ - handle_v_requests (own_buf, packet_len, &new_packet_len); + handle_v_requests (cs.own_buf, packet_len, &new_packet_len); break; default: /* It is a request we don't understand. Respond with an empty packet so that gdb knows that we don't support this request. */ - own_buf[0] = '\0'; + cs.own_buf[0] = '\0'; break; } if (new_packet_len != -1) - putpkt_binary (own_buf, new_packet_len); + putpkt_binary (cs.own_buf, new_packet_len); else - putpkt (own_buf); + putpkt (cs.own_buf); response_needed = 0; - if (!extended_protocol && have_ran && !target_running ()) - { - /* In non-stop, defer exiting until GDB had a chance to query - the whole vStopped list (until it gets an OK). */ - if (QUEUE_is_empty (notif_event_p, notif_stop.queue)) - { - /* Be transparent when GDB is connected through stdio -- no - need to spam GDB's console. */ - if (!remote_connection_is_stdio ()) - fprintf (stderr, "GDBserver exiting\n"); - remote_close (); - exit (0); - } - } - if (exit_requested) return -1; @@ -4247,45 +4395,62 @@ handle_serial_event (int err, gdb_client_data client_data) /* Be sure to not change the selected thread behind GDB's back. Important in the non-stop mode asynchronous protocol. */ - set_desired_thread (1); + set_desired_thread (); return 0; } +/* Push a stop notification on the notification queue. */ + +static void +push_stop_notification (ptid_t ptid, struct target_waitstatus *status) +{ + struct vstop_notif *vstop_notif = XNEW (struct vstop_notif); + + vstop_notif->status = *status; + vstop_notif->ptid = ptid; + /* Push Stop notification. */ + notif_push (¬if_stop, (struct notif_event *) vstop_notif); +} + /* Event-loop callback for target events. */ int handle_target_event (int err, gdb_client_data client_data) { + client_state &cs = get_client_state (); if (debug_threads) debug_printf ("handling possible target event\n"); - last_ptid = mywait (minus_one_ptid, &last_status, + cs.last_ptid = mywait (minus_one_ptid, &cs.last_status, TARGET_WNOHANG, 1); - if (last_status.kind == TARGET_WAITKIND_NO_RESUMED) + if (cs.last_status.kind == TARGET_WAITKIND_NO_RESUMED) { - /* No RSP support for this yet. */ + if (gdb_connected () && report_no_resumed) + push_stop_notification (null_ptid, &cs.last_status); } - else if (last_status.kind != TARGET_WAITKIND_IGNORE) + else if (cs.last_status.kind != TARGET_WAITKIND_IGNORE) { - int pid = ptid_get_pid (last_ptid); + int pid = cs.last_ptid.pid (); struct process_info *process = find_process_pid (pid); int forward_event = !gdb_connected () || process->gdb_detached; - if (last_status.kind == TARGET_WAITKIND_EXITED - || last_status.kind == TARGET_WAITKIND_SIGNALLED) + if (cs.last_status.kind == TARGET_WAITKIND_EXITED + || cs.last_status.kind == TARGET_WAITKIND_SIGNALLED) { mark_breakpoints_out (process); - mourn_inferior (process); + target_mourn_inferior (cs.last_ptid); } + else if (cs.last_status.kind == TARGET_WAITKIND_THREAD_EXITED) + ; else { /* We're reporting this thread as stopped. Update its "want-stopped" state to what the client wants, until it gets a new resume action. */ current_thread->last_resume_kind = resume_stop; - current_thread->last_status = last_status; + current_thread->last_status = cs.last_status; } if (forward_event) @@ -4296,44 +4461,48 @@ handle_target_event (int err, gdb_client_data client_data) exit (0); } - if (last_status.kind == TARGET_WAITKIND_STOPPED) + if (cs.last_status.kind == TARGET_WAITKIND_EXITED + || cs.last_status.kind == TARGET_WAITKIND_SIGNALLED + || cs.last_status.kind == TARGET_WAITKIND_THREAD_EXITED) + ; + else { /* A thread stopped with a signal, but gdb isn't connected to handle it. Pass it down to the inferior, as if it wasn't being traced. */ - struct thread_resume resume_info; + enum gdb_signal signal; if (debug_threads) debug_printf ("GDB not connected; forwarding event %d for" " [%s]\n", - (int) last_status.kind, - target_pid_to_str (last_ptid)); + (int) cs.last_status.kind, + target_pid_to_str (cs.last_ptid)); - resume_info.thread = last_ptid; - resume_info.kind = resume_continue; - resume_info.sig = gdb_signal_to_host (last_status.value.sig); - (*the_target->resume) (&resume_info, 1); + if (cs.last_status.kind == TARGET_WAITKIND_STOPPED) + signal = cs.last_status.value.sig; + else + signal = GDB_SIGNAL_0; + target_continue (cs.last_ptid, signal); } - else if (debug_threads) - debug_printf ("GDB not connected; ignoring event %d for [%s]\n", - (int) last_status.kind, - target_pid_to_str (last_ptid)); } else - { - struct vstop_notif *vstop_notif = XNEW (struct vstop_notif); - - vstop_notif->status = last_status; - vstop_notif->ptid = last_ptid; - /* Push Stop notification. */ - notif_push (¬if_stop, - (struct notif_event *) vstop_notif); - } + push_stop_notification (cs.last_ptid, &cs.last_status); } /* Be sure to not change the selected thread behind GDB's back. Important in the non-stop mode asynchronous protocol. */ - set_desired_thread (1); + set_desired_thread (); return 0; } + +#if GDB_SELF_TEST +namespace selftests +{ + +void +reset () +{} + +} // namespace selftests +#endif /* GDB_SELF_TEST */