/* Main code for remote server for GDB.
- Copyright (C) 1989-2020 Free Software Foundation, Inc.
+ Copyright (C) 1989-2021 Free Software Foundation, Inc.
This file is part of GDB.
#include "gdbsupport/selftest.h"
#include "gdbsupport/scope-exit.h"
+#include "gdbsupport/gdb_select.h"
+#include "gdbsupport/scoped_restore.h"
+#include "gdbsupport/search.h"
#define require_running_or_return(BUF) \
if (!target_running ()) \
/* Whether to report TARGET_WAITKIND_NO_RESUMED events. */
static bool report_no_resumed;
+/* The event loop checks this to decide whether to continue accepting
+ events. */
+static bool keep_processing_events = true;
+
bool non_stop;
static struct {
bool disable_packet_Tthread;
bool disable_packet_qC;
bool disable_packet_qfThreadInfo;
+bool disable_packet_T;
static unsigned char *mem_buf;
next = iter;
++next;
+ if (iter == notif_stop.queue.begin ())
+ {
+ /* The head of the list contains the notification that was
+ already sent to GDB. So we can't remove it, otherwise
+ when GDB sends the vStopped, it would ack the _next_
+ notification, which hadn't been sent yet! */
+ continue;
+ }
+
if (remove_all_on_match_ptid (*iter, ptid))
{
delete *iter;
return 1;
}
+/* Create the qMemTags packet reply given TAGS.
+
+ Returns true if parsing succeeded and false otherwise. */
+
+static bool
+create_fetch_memtags_reply (char *reply, const gdb::byte_vector &tags)
+{
+ /* It is an error to pass a zero-sized tag vector. */
+ gdb_assert (tags.size () != 0);
+
+ std::string packet ("m");
+
+ /* Write the tag data. */
+ packet += bin2hex (tags.data (), tags.size ());
+
+ /* Check if the reply is too big for the packet to handle. */
+ if (PBUFSIZ < packet.size ())
+ return false;
+
+ strcpy (reply, packet.c_str ());
+ return true;
+}
+
+/* Parse the QMemTags request into ADDR, LEN and TAGS.
+
+ Returns true if parsing succeeded and false otherwise. */
+
+static bool
+parse_store_memtags_request (char *request, CORE_ADDR *addr, size_t *len,
+ gdb::byte_vector &tags, int *type)
+{
+ gdb_assert (startswith (request, "QMemTags:"));
+
+ const char *p = request + strlen ("QMemTags:");
+
+ /* Read address and length. */
+ unsigned int length = 0;
+ p = decode_m_packet_params (p, addr, &length, ':');
+ *len = length;
+
+ /* Read the tag type. */
+ ULONGEST tag_type = 0;
+ p = unpack_varlen_hex (p, &tag_type);
+ *type = (int) tag_type;
+
+ /* Make sure there is a colon after the type. */
+ if (*p != ':')
+ return false;
+
+ /* Skip the colon. */
+ p++;
+
+ /* Read the tag data. */
+ tags = hex2bin (p);
+
+ return true;
+}
+
/* Handle all of the extended 'Q' packets. */
static void
}
req_str = req ? "non-stop" : "all-stop";
- if (start_non_stop (req) != 0)
+ if (the_target->start_non_stop (req == 1) != 0)
{
fprintf (stderr, "Setting %s mode failed\n", req_str);
write_enn (own_buf);
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);
+ std::string err
+ = string_printf ("E.Unknown thread-events mode requested: %s\n",
+ mode);
+ strcpy (own_buf, err.c_str ());
return;
}
return;
}
+
+ /* Handle store memory tags packets. */
+ if (startswith (own_buf, "QMemTags:")
+ && target_supports_memory_tagging ())
+ {
+ gdb::byte_vector tags;
+ CORE_ADDR addr = 0;
+ size_t len = 0;
+ int type = 0;
+
+ require_running_or_return (own_buf);
+
+ bool ret = parse_store_memtags_request (own_buf, &addr, &len, tags,
+ &type);
+
+ if (ret)
+ ret = the_target->store_memtags (addr, len, tags, type);
+
+ if (!ret)
+ write_enn (own_buf);
+ else
+ write_ok (own_buf);
+
+ return;
+ }
+
/* Otherwise we didn't know what packet it was. Say we didn't
understand it. */
own_buf[0] = 0;
monitor_output (" Enable h/w breakpoint/watchpoint debugging messages\n");
monitor_output (" set remote-debug <0|1>\n");
monitor_output (" Enable remote protocol debugging messages\n");
+ monitor_output (" set event-loop-debug <0|1>\n");
+ monitor_output (" Enable event loop debugging messages\n");
monitor_output (" set debug-format option1[,option2,...]\n");
monitor_output (" Add additional information to debugging messages\n");
monitor_output (" Options: all, none");
}
}
-/* Subroutine of handle_search_memory to simplify it. */
-
-static int
-handle_search_memory_1 (CORE_ADDR start_addr, CORE_ADDR search_space_len,
- gdb_byte *pattern, unsigned pattern_len,
- gdb_byte *search_buf,
- unsigned chunk_size, unsigned search_buf_size,
- CORE_ADDR *found_addrp)
-{
- /* Prime the search buffer. */
-
- if (gdb_read_memory (start_addr, search_buf, search_buf_size)
- != search_buf_size)
- {
- warning ("Unable to access %ld bytes of target "
- "memory at 0x%lx, halting search.",
- (long) search_buf_size, (long) start_addr);
- return -1;
- }
-
- /* Perform the search.
-
- The loop is kept simple by allocating [N + pattern-length - 1] bytes.
- When we've scanned N bytes we copy the trailing bytes to the start and
- read in another N bytes. */
-
- while (search_space_len >= pattern_len)
- {
- gdb_byte *found_ptr;
- unsigned nr_search_bytes = (search_space_len < search_buf_size
- ? search_space_len
- : search_buf_size);
-
- found_ptr = (gdb_byte *) memmem (search_buf, nr_search_bytes, pattern,
- pattern_len);
-
- if (found_ptr != NULL)
- {
- CORE_ADDR found_addr = start_addr + (found_ptr - search_buf);
- *found_addrp = found_addr;
- return 1;
- }
-
- /* Not found in this chunk, skip to next chunk. */
-
- /* Don't let search_space_len wrap here, it's unsigned. */
- if (search_space_len >= chunk_size)
- search_space_len -= chunk_size;
- else
- search_space_len = 0;
-
- if (search_space_len >= pattern_len)
- {
- unsigned keep_len = search_buf_size - chunk_size;
- CORE_ADDR read_addr = start_addr + chunk_size + keep_len;
- int nr_to_read;
-
- /* Copy the trailing part of the previous iteration to the front
- of the buffer for the next iteration. */
- memcpy (search_buf, search_buf + chunk_size, keep_len);
-
- nr_to_read = (search_space_len - keep_len < chunk_size
- ? search_space_len - keep_len
- : chunk_size);
-
- if (gdb_read_memory (read_addr, search_buf + keep_len,
- nr_to_read) != search_buf_size)
- {
- warning ("Unable to access %ld bytes of target memory "
- "at 0x%lx, halting search.",
- (long) nr_to_read, (long) read_addr);
- return -1;
- }
-
- start_addr += chunk_size;
- }
- }
-
- /* Not found. */
-
- return 0;
-}
-
/* Handle qSearch:memory packets. */
static void
CORE_ADDR search_space_len;
gdb_byte *pattern;
unsigned int pattern_len;
- /* NOTE: also defined in find.c testcase. */
-#define SEARCH_CHUNK_SIZE 16000
- const unsigned chunk_size = SEARCH_CHUNK_SIZE;
- /* Buffer to hold memory contents for searching. */
- gdb_byte *search_buf;
- unsigned search_buf_size;
int found;
CORE_ADDR found_addr;
int cmd_name_len = sizeof ("qSearch:memory:") - 1;
pattern = (gdb_byte *) malloc (packet_len);
if (pattern == NULL)
- {
- error ("Unable to allocate memory to perform the search");
- strcpy (own_buf, "E00");
- return;
- }
+ error ("Unable to allocate memory to perform the search");
+
if (decode_search_memory_packet (own_buf + cmd_name_len,
packet_len - cmd_name_len,
&start_addr, &search_space_len,
{
free (pattern);
error ("Error in parsing qSearch:memory packet");
- strcpy (own_buf, "E00");
- return;
}
- search_buf_size = chunk_size + pattern_len - 1;
-
- /* No point in trying to allocate a buffer larger than the search space. */
- if (search_space_len < search_buf_size)
- search_buf_size = search_space_len;
-
- search_buf = (gdb_byte *) malloc (search_buf_size);
- if (search_buf == NULL)
+ auto read_memory = [] (CORE_ADDR addr, gdb_byte *result, size_t len)
{
- free (pattern);
- error ("Unable to allocate memory to perform the search");
- strcpy (own_buf, "E00");
- return;
- }
+ return gdb_read_memory (addr, result, len) == len;
+ };
- found = handle_search_memory_1 (start_addr, search_space_len,
- pattern, pattern_len,
- search_buf, chunk_size, search_buf_size,
- &found_addr);
+ found = simple_search_memory (read_memory, start_addr, search_space_len,
+ pattern, pattern_len, &found_addr);
if (found > 0)
sprintf (own_buf, "1,%lx", (long) found_addr);
else
strcpy (own_buf, "E00");
- free (search_buf);
free (pattern);
}
debug_printf ("Forcing non-stop mode\n");
non_stop = true;
- start_non_stop (1);
+ the_target->start_non_stop (true);
}
process->gdb_detached = 1;
remote_debug = 0;
monitor_output ("Protocol debug output disabled.\n");
}
+ else if (strcmp (mon, "set event-loop-debug 1") == 0)
+ {
+ debug_event_loop = debug_event_loop_kind::ALL;
+ monitor_output ("Event loop debug output enabled.\n");
+ }
+ else if (strcmp (mon, "set event-loop-debug 0") == 0)
+ {
+ debug_event_loop = debug_event_loop_kind::OFF;
+ monitor_output ("Event loop debug output disabled.\n");
+ }
else if (startswith (mon, "set debug-format "))
{
std::string error_msg
gdb_byte *readbuf, const gdb_byte *writebuf,
ULONGEST offset, LONGEST len)
{
- if (!the_target->pt->supports_read_auxv () || writebuf != NULL)
+ if (!the_target->supports_read_auxv () || writebuf != NULL)
return -2;
if (annex[0] != '\0' || current_thread == NULL)
return -1;
- return the_target->pt->read_auxv (offset, readbuf, len);
+ return the_target->read_auxv (offset, readbuf, len);
}
/* Handle qXfer:exec-file:read. */
gdb_byte *readbuf, const gdb_byte *writebuf,
ULONGEST offset, LONGEST len)
{
- char *file;
ULONGEST pid;
int total_len;
- if (the_target->pid_to_exec_file == NULL || writebuf != NULL)
+ if (!the_target->supports_pid_to_exec_file () || writebuf != NULL)
return -2;
if (annex[0] == '\0')
if (pid <= 0)
return -1;
- file = (*the_target->pid_to_exec_file) (pid);
+ const char *file = the_target->pid_to_exec_file (pid);
if (file == NULL)
return -1;
std::string document = "<library-list version=\"1.0\">\n";
- for (const dll_info &dll : all_dlls)
+ process_info *proc = current_process ();
+ for (const dll_info &dll : proc->all_dlls)
document += string_printf
(" <library name=\"%s\"><segment address=\"0x%s\"/></library>\n",
dll.name.c_str (), paddress (dll.base_addr));
if (writebuf != NULL)
return -2;
- if (current_thread == NULL || the_target->qxfer_libraries_svr4 == NULL)
+ if (current_thread == NULL
+ || !the_target->supports_qxfer_libraries_svr4 ())
return -1;
- return the_target->qxfer_libraries_svr4 (annex, readbuf, writebuf, offset, len);
+ return the_target->qxfer_libraries_svr4 (annex, readbuf, writebuf,
+ offset, len);
}
/* Handle qXfer:osadata:read. */
gdb_byte *readbuf, const gdb_byte *writebuf,
ULONGEST offset, LONGEST len)
{
- if (!the_target->pt->supports_qxfer_osdata () || writebuf != NULL)
+ if (!the_target->supports_qxfer_osdata () || writebuf != NULL)
return -2;
- return the_target->pt->qxfer_osdata (annex, readbuf, NULL, offset, len);
+ return the_target->qxfer_osdata (annex, readbuf, NULL, offset, len);
}
/* Handle qXfer:siginfo:read and qXfer:siginfo:write. */
gdb_byte *readbuf, const gdb_byte *writebuf,
ULONGEST offset, LONGEST len)
{
- if (the_target->qxfer_siginfo == NULL)
+ if (!the_target->supports_qxfer_siginfo ())
return -2;
if (annex[0] != '\0' || current_thread == NULL)
return -1;
- return (*the_target->qxfer_siginfo) (annex, readbuf, writebuf, offset, len);
+ return the_target->qxfer_siginfo (annex, readbuf, writebuf, offset, len);
}
/* Handle qXfer:statictrace:read. */
buffer_xml_printf (buffer, "/>\n");
}
-/* Helper for handle_qxfer_threads. */
+/* Helper for handle_qxfer_threads. Return true on success, false
+ otherwise. */
-static void
+static bool
handle_qxfer_threads_proper (struct buffer *buffer)
{
+ client_state &cs = get_client_state ();
+
+ scoped_restore save_current_thread
+ = make_scoped_restore (¤t_thread);
+ scoped_restore save_current_general_thread
+ = make_scoped_restore (&cs.general_thread);
+
buffer_grow_str (buffer, "<threads>\n");
- for_each_thread ([&] (thread_info *thread)
+ process_info *error_proc = find_process ([&] (process_info *process)
{
- handle_qxfer_threads_worker (thread, buffer);
+ /* The target may need to access memory and registers (e.g. via
+ libthread_db) to fetch thread properties. Prepare for memory
+ access here, so that we potentially pause threads just once
+ for all accesses. Note that even if someday we stop needing
+ to pause threads to access memory, we will need to be able to
+ access registers, or other ptrace accesses like
+ PTRACE_GET_THREAD_AREA. */
+
+ /* Need to switch to each process in turn, because
+ prepare_to_access_memory prepares for an access in the
+ current process pointed to by general_thread. */
+ switch_to_process (process);
+ cs.general_thread = current_thread->id;
+
+ int res = prepare_to_access_memory ();
+ if (res == 0)
+ {
+ for_each_thread (process->pid, [&] (thread_info *thread)
+ {
+ handle_qxfer_threads_worker (thread, buffer);
+ });
+
+ done_accessing_memory ();
+ return false;
+ }
+ else
+ return true;
});
buffer_grow_str0 (buffer, "</threads>\n");
+ return error_proc == nullptr;
}
/* Handle qXfer:threads:read. */
buffer_init (&buffer);
- handle_qxfer_threads_proper (&buffer);
+ bool res = handle_qxfer_threads_proper (&buffer);
result = buffer_finish (&buffer);
result_length = strlen (result);
buffer_free (&buffer);
+
+ if (!res)
+ return -1;
}
if (offset >= result_length)
handle_qxfer_fdpic (const char *annex, gdb_byte *readbuf,
const gdb_byte *writebuf, ULONGEST offset, LONGEST len)
{
- if (the_target->read_loadmap == NULL)
+ if (!the_target->supports_read_loadmap ())
return -2;
if (current_thread == NULL)
return -1;
- return (*the_target->read_loadmap) (annex, offset, readbuf, len);
+ return the_target->read_loadmap (annex, offset, readbuf, len);
}
/* Handle qXfer:btrace:read. */
return (unsigned long long) crc;
}
+/* Parse the qMemTags packet request into ADDR and LEN. */
+
+static void
+parse_fetch_memtags_request (char *request, CORE_ADDR *addr, size_t *len,
+ int *type)
+{
+ gdb_assert (startswith (request, "qMemTags:"));
+
+ const char *p = request + strlen ("qMemTags:");
+
+ /* Read address and length. */
+ unsigned int length = 0;
+ p = decode_m_packet_params (p, addr, &length, ':');
+ *len = length;
+
+ /* Read the tag type. */
+ ULONGEST tag_type = 0;
+ p = unpack_varlen_hex (p, &tag_type);
+ *type = (int) tag_type;
+}
+
/* Add supported btrace packets to BUF. */
static void
tracepoint_look_up_symbols ();
if (current_thread != NULL)
- the_target->pt->look_up_symbols ();
+ the_target->look_up_symbols ();
current_thread = save_thread;
}
}
- if (the_target->pt->supports_read_offsets ()
+ if (the_target->supports_read_offsets ()
&& strcmp ("qOffsets", own_buf) == 0)
{
CORE_ADDR text, data;
require_running_or_return (own_buf);
- if (the_target->pt->read_offsets (&text, &data))
+ if (the_target->read_offsets (&text, &data))
sprintf (own_buf, "Text=%lX;Data=%lX;Bss=%lX",
(long)text, (long)data, (long)data);
else
';'. */
if (*p == ':')
{
- char **qsupported = NULL;
- int count = 0;
- int unknown = 0;
- int i;
+ std::vector<std::string> qsupported;
+ std::vector<const char *> unknowns;
/* Two passes, to avoid nested strtok calls in
target_process_qsupported. */
for (p = strtok_r (p + 1, ";", &saveptr);
p != NULL;
p = strtok_r (NULL, ";", &saveptr))
- {
- count++;
- qsupported = XRESIZEVEC (char *, qsupported, count);
- qsupported[count - 1] = xstrdup (p);
- }
+ qsupported.emplace_back (p);
- for (i = 0; i < count; i++)
+ for (const std::string &feature : qsupported)
{
- p = qsupported[i];
- if (strcmp (p, "multiprocess+") == 0)
+ if (feature == "multiprocess+")
{
/* GDB supports and wants multi-process support if
possible. */
if (target_supports_multi_process ())
cs.multi_process = 1;
}
- else if (strcmp (p, "qRelocInsn+") == 0)
+ else if (feature == "qRelocInsn+")
{
/* GDB supports relocate instruction requests. */
gdb_supports_qRelocInsn = 1;
}
- else if (strcmp (p, "swbreak+") == 0)
+ else if (feature == "swbreak+")
{
/* GDB wants us to report whether a trap is caused
by a software breakpoint and for us to handle PC
if (target_supports_stopped_by_sw_breakpoint ())
cs.swbreak_feature = 1;
}
- else if (strcmp (p, "hwbreak+") == 0)
+ else if (feature == "hwbreak+")
{
/* GDB wants us to report whether a trap is caused
by a hardware breakpoint. */
if (target_supports_stopped_by_hw_breakpoint ())
cs.hwbreak_feature = 1;
}
- else if (strcmp (p, "fork-events+") == 0)
+ else if (feature == "fork-events+")
{
/* GDB supports and wants fork events if possible. */
if (target_supports_fork_events ())
cs.report_fork_events = 1;
}
- else if (strcmp (p, "vfork-events+") == 0)
+ else if (feature == "vfork-events+")
{
/* GDB supports and wants vfork events if possible. */
if (target_supports_vfork_events ())
cs.report_vfork_events = 1;
}
- else if (strcmp (p, "exec-events+") == 0)
+ else if (feature == "exec-events+")
{
/* GDB supports and wants exec events if possible. */
if (target_supports_exec_events ())
cs.report_exec_events = 1;
}
- else if (strcmp (p, "vContSupported+") == 0)
+ else if (feature == "vContSupported+")
cs.vCont_supported = 1;
- else if (strcmp (p, "QThreadEvents+") == 0)
+ else if (feature == "QThreadEvents+")
;
- else if (strcmp (p, "no-resumed+") == 0)
+ else if (feature == "no-resumed+")
{
/* GDB supports and wants TARGET_WAITKIND_NO_RESUMED
events. */
report_no_resumed = true;
}
+ else if (feature == "memory-tagging+")
+ {
+ /* GDB supports memory tagging features. */
+ if (target_supports_memory_tagging ())
+ cs.memory_tagging_feature = true;
+ }
else
{
/* Move the unknown features all together. */
- qsupported[i] = NULL;
- qsupported[unknown] = p;
- unknown++;
+ unknowns.push_back (feature.c_str ());
}
}
/* 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);
+ target_process_qsupported (unknowns);
}
sprintf (own_buf,
if (target_supports_catch_syscall ())
strcat (own_buf, ";QCatchSyscalls+");
- if (the_target->qxfer_libraries_svr4 != NULL)
+ if (the_target->supports_qxfer_libraries_svr4 ())
strcat (own_buf, ";qXfer:libraries-svr4:read+"
";augmented-libraries-svr4-read+");
else
strcat (own_buf, ";qXfer:libraries:read+");
}
- if (the_target->pt->supports_read_auxv ())
+ if (the_target->supports_read_auxv ())
strcat (own_buf, ";qXfer:auxv:read+");
- if (the_target->qxfer_siginfo != NULL)
+ if (the_target->supports_qxfer_siginfo ())
strcat (own_buf, ";qXfer:siginfo:read+;qXfer:siginfo:write+");
- if (the_target->read_loadmap != NULL)
+ if (the_target->supports_read_loadmap ())
strcat (own_buf, ";qXfer:fdpic:read+");
/* We always report qXfer:features:read, as targets may
if (cs.transport_is_reliable)
strcat (own_buf, ";QStartNoAckMode+");
- if (the_target->pt->supports_qxfer_osdata ())
+ if (the_target->supports_qxfer_osdata ())
strcat (own_buf, ";qXfer:osdata:read+");
if (target_supports_multi_process ())
if (target_supports_stopped_by_hw_breakpoint ())
strcat (own_buf, ";hwbreak+");
- if (the_target->pid_to_exec_file != NULL)
+ if (the_target->supports_pid_to_exec_file ())
strcat (own_buf, ";qXfer:exec-file:read+");
strcat (own_buf, ";vContSupported+");
strcat (own_buf, ";no-resumed+");
+ if (target_supports_memory_tagging ())
+ strcat (own_buf, ";memory-tagging+");
+
/* Reinitialize components as needed for the new connection. */
hostio_handle_new_gdb_connection ();
target_handle_new_gdb_connection ();
}
/* Thread-local storage support. */
- if (the_target->pt->supports_get_tls_address ()
+ if (the_target->supports_get_tls_address ()
&& startswith (own_buf, "qGetTLSAddr:"))
{
char *p = own_buf + 12;
if (thread == NULL)
err = 2;
else
- err = the_target->pt->get_tls_address (thread, parts[0], parts[1],
- &address);
+ err = the_target->get_tls_address (thread, parts[0], parts[1],
+ &address);
}
if (err == 0)
}
/* Windows OS Thread Information Block address support. */
- if (the_target->get_tib_address != NULL
+ if (the_target->supports_get_tib_address ()
&& startswith (own_buf, "qGetTIBAddr:"))
{
const char *annex;
CORE_ADDR tlb;
ptid_t ptid = read_ptid (own_buf + 12, &annex);
- n = (*the_target->get_tib_address) (ptid, &tlb);
+ n = the_target->get_tib_address (ptid, &tlb);
if (n == 1)
{
strcpy (own_buf, paddress(tlb));
write_ok (own_buf);
- if (the_target->handle_monitor_command == NULL
- || (*the_target->handle_monitor_command) (mon) == 0)
+ if (the_target->handle_monitor_command (mon) == 0)
/* Default processing. */
handle_monitor_command (mon, own_buf);
if (target_supports_tracepoints () && handle_tracepoint_query (own_buf))
return;
+ /* Handle fetch memory tags packets. */
+ if (startswith (own_buf, "qMemTags:")
+ && target_supports_memory_tagging ())
+ {
+ gdb::byte_vector tags;
+ CORE_ADDR addr = 0;
+ size_t len = 0;
+ int type = 0;
+
+ require_running_or_return (own_buf);
+
+ parse_fetch_memtags_request (own_buf, &addr, &len, &type);
+
+ bool ret = the_target->fetch_memtags (addr, len, tags, type);
+
+ if (ret)
+ ret = create_fetch_memtags_reply (own_buf, tags);
+
+ if (!ret)
+ write_enn (own_buf);
+
+ *new_packet_len_p = strlen (own_buf);
+ return;
+ }
+
/* Otherwise we didn't know what packet it was. Say we didn't
understand it. */
own_buf[0] = 0;
enable_async_io ();
}
- the_target->pt->resume (actions, num_actions);
+ the_target->resume (actions, num_actions);
if (non_stop)
write_ok (cs.own_buf);
}
if (cs.last_status.kind != TARGET_WAITKIND_EXITED
- && cs.last_status.kind != TARGET_WAITKIND_SIGNALLED
+ && cs.last_status.kind != TARGET_WAITKIND_SIGNALLED
&& cs.last_status.kind != TARGET_WAITKIND_NO_RESUMED)
current_thread->last_status = cs.last_status;
disable_async_io ();
if (cs.last_status.kind == TARGET_WAITKIND_EXITED
- || cs.last_status.kind == TARGET_WAITKIND_SIGNALLED)
- target_mourn_inferior (cs.last_ptid);
+ || 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. */
-static int
+/* Attach to a new program. */
+static void
handle_v_attach (char *own_buf)
{
client_state &cs = get_client_state ();
some libraries are preloaded. GDB will always poll the
library list. Avoids the "stopped by shared library event"
notice on the GDB side. */
- dlls_changed = 0;
+ current_process ()->dlls_changed = false;
if (non_stop)
{
}
else
prepare_resume_reply (own_buf, cs.last_ptid, &cs.last_status);
-
- return 1;
}
else
- {
- write_enn (own_buf);
- return 0;
- }
+ write_enn (own_buf);
}
-/* Run a new program. Return 1 if successful, 0 if failure. */
-static int
+/* Run a new program. */
+static void
handle_v_run (char *own_buf)
{
client_state &cs = get_client_state ();
else if (p == next_p)
{
/* Empty argument. */
- new_argv.push_back (xstrdup ("''"));
+ new_argv.push_back (xstrdup (""));
}
else
{
if (*next_p)
next_p++;
}
- new_argv.push_back (NULL);
if (new_program_name == NULL)
{
{
write_enn (own_buf);
free_vector_argv (new_argv);
- return 0;
+ return;
}
}
else
query which is the main thread of the new inferior. */
if (non_stop)
cs.general_thread = cs.last_ptid;
-
- return 1;
}
else
- {
- write_enn (own_buf);
- return 0;
- }
+ write_enn (own_buf);
}
-/* Kill process. Return 1 if successful, 0 if failure. */
-static int
+/* Kill process. */
+static void
handle_v_kill (char *own_buf)
{
client_state &cs = get_client_state ();
cs.last_ptid = ptid_t (pid);
discard_queued_stop_replies (cs.last_ptid);
write_ok (own_buf);
- return 1;
}
else
- {
- write_enn (own_buf);
- return 0;
- }
+ write_enn (own_buf);
}
/* Handle all of the extended 'v' packets. */
{
if (strcmp (own_buf, "vCtrlC") == 0)
{
- the_target->pt->request_interrupt ();
+ the_target->request_interrupt ();
write_ok (own_buf);
return;
}
{
/* 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)
+ if (!the_target->supports_thread_stopped ())
{
struct vstop_notif *new_notif = new struct vstop_notif;
}
else
{
- if (thread_stopped (thread))
+ if (target_thread_stopped (thread))
{
if (debug_threads)
{
{
thread_info *thread = NULL;
- pause_all (0);
- stabilize_threads ();
+ target_pause_all (false);
+ target_stabilize_threads ();
gdb_wants_all_threads_stopped ();
/* We can only report one status, but we might be coming out of
gdbserver_version (void)
{
printf ("GNU gdbserver %s%s\n"
- "Copyright (C) 2020 Free Software Foundation, Inc.\n"
+ "Copyright (C) 2021 Free Software Foundation, Inc.\n"
"gdbserver is free software, covered by the "
"GNU General Public License.\n"
"This gdbserver was configured as \"%s\"\n",
" none\n"
" timestamp\n"
" --remote-debug Enable remote protocol debugging output.\n"
+ " --event-loop-debug Enable event loop debugging output.\n"
" --disable-packet=OPT1[,OPT2,...]\n"
" Disable support for RSP packets or features.\n"
" Options:\n"
- " vCont, Tthread, qC, qfThreadInfo and \n"
+ " vCont, T, Tthread, qC, qfThreadInfo and \n"
" threads (disable all threading packets).\n"
"\n"
"For more information, consult the GDB manual (available as on-line \n"
" qfThreadInfo\tThread listing\n"
" Tthread \tPassing the thread specifier in the "
"T stop reply packet\n"
- " threads \tAll of the above\n");
+ " threads \tAll of the above\n"
+ " T \tAll 'T' packets\n");
+}
+
+/* Start up the event loop. This is the entry point to the event
+ loop. */
+
+static void
+start_event_loop ()
+{
+ /* Loop until there is nothing to do. This is the entry point to
+ the event loop engine. If nothing is ready at this time, wait
+ for something to happen (via wait_for_event), then process it.
+ Return when there are no longer event sources to wait for. */
+
+ keep_processing_events = true;
+ while (keep_processing_events)
+ {
+ /* Any events already waiting in the queue? */
+ int res = gdb_do_one_event ();
+
+ /* Was there an error? */
+ if (res == -1)
+ break;
+ }
+
+ /* We are done with the event loop. There are no more event sources
+ to listen to. So we exit gdbserver. */
}
static void
}
}
+#if GDB_SELF_TEST
+
+namespace selftests {
+
+static void
+test_memory_tagging_functions (void)
+{
+ /* Setup testing. */
+ gdb::char_vector packet;
+ gdb::byte_vector tags, bv;
+ std::string expected;
+ packet.resize (32000);
+ CORE_ADDR addr;
+ size_t len;
+ int type;
+
+ /* Test parsing a qMemTags request. */
+
+ /* Valid request, addr, len and type updated. */
+ addr = 0xff;
+ len = 255;
+ type = 255;
+ strcpy (packet.data (), "qMemTags:0,0:0");
+ parse_fetch_memtags_request (packet.data (), &addr, &len, &type);
+ SELF_CHECK (addr == 0 && len == 0 && type == 0);
+
+ /* Valid request, addr, len and type updated. */
+ addr = 0;
+ len = 0;
+ type = 0;
+ strcpy (packet.data (), "qMemTags:deadbeef,ff:5");
+ parse_fetch_memtags_request (packet.data (), &addr, &len, &type);
+ SELF_CHECK (addr == 0xdeadbeef && len == 255 && type == 5);
+
+ /* Test creating a qMemTags reply. */
+
+ /* Non-empty tag data. */
+ bv.resize (0);
+
+ for (int i = 0; i < 5; i++)
+ bv.push_back (i);
+
+ expected = "m0001020304";
+ SELF_CHECK (create_fetch_memtags_reply (packet.data (), bv) == true);
+ SELF_CHECK (strcmp (packet.data (), expected.c_str ()) == 0);
+
+ /* Test parsing a QMemTags request. */
+
+ /* Valid request and empty tag data: addr, len, type and tags updated. */
+ addr = 0xff;
+ len = 255;
+ type = 255;
+ tags.resize (5);
+ strcpy (packet.data (), "QMemTags:0,0:0:");
+ SELF_CHECK (parse_store_memtags_request (packet.data (),
+ &addr, &len, tags, &type) == true);
+ SELF_CHECK (addr == 0 && len == 0 && type == 0 && tags.size () == 0);
+
+ /* Valid request and non-empty tag data: addr, len, type
+ and tags updated. */
+ addr = 0;
+ len = 0;
+ type = 0;
+ tags.resize (0);
+ strcpy (packet.data (),
+ "QMemTags:deadbeef,ff:5:0001020304");
+ SELF_CHECK (parse_store_memtags_request (packet.data (), &addr, &len, tags,
+ &type) == true);
+ SELF_CHECK (addr == 0xdeadbeef && len == 255 && type == 5
+ && tags.size () == 5);
+}
+
+} // namespace selftests
+#endif /* GDB_SELF_TEST */
+
/* Main function. This is called by the real "main" function,
wrapped in a TRY_CATCH that handles any uncaught exceptions. */
int was_running;
bool selftest = false;
#if GDB_SELF_TEST
- const char *selftest_filter = NULL;
+ std::vector<const char *> selftest_filters;
+
+ selftests::register_test ("remote_memory_tagging",
+ selftests::test_memory_tagging_functions);
#endif
current_directory = getcwd (NULL, 0);
}
else if (strcmp (*next_arg, "--remote-debug") == 0)
remote_debug = 1;
+ else if (strcmp (*next_arg, "--event-loop-debug") == 0)
+ debug_event_loop = debug_event_loop_kind::ALL;
else if (startswith (*next_arg, "--debug-file="))
debug_set_output ((*next_arg) + sizeof ("--debug-file=") -1);
else if (strcmp (*next_arg, "--disable-packet") == 0)
disable_packet_qC = true;
else if (strcmp ("qfThreadInfo", tok) == 0)
disable_packet_qfThreadInfo = true;
+ else if (strcmp ("T", tok) == 0)
+ disable_packet_T = true;
else if (strcmp ("threads", tok) == 0)
{
disable_packet_vCont = true;
else if (startswith (*next_arg, "--selftest="))
{
selftest = true;
+
#if GDB_SELF_TEST
- selftest_filter = *next_arg + strlen ("--selftest=");
+ const char *filter = *next_arg + strlen ("--selftest=");
+ if (*filter == '\0')
+ {
+ fprintf (stderr, _("Error: selftest filter is empty.\n"));
+ exit (1);
+ }
+
+ selftest_filters.push_back (filter);
#endif
}
else
initialize_async_io ();
initialize_low ();
have_job_control ();
- initialize_event_loop ();
if (target_supports_tracepoints ())
initialize_tracepoint ();
if (selftest)
{
#if GDB_SELF_TEST
- selftests::run_tests (selftest_filter);
+ selftests::run_tests (selftest_filters);
#else
printf (_("Selftests have been disabled for this build.\n"));
#endif
program_path.set (make_unique_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. */
target_create_inferior (program_path.get (), program_args);
/* Don't report shared library events on the initial connection,
even if some libraries are preloaded. Avoids the "stopped by
shared library event" notice on gdb side. */
- dlls_changed = 0;
+ if (current_thread != nullptr)
+ current_process ()->dlls_changed = false;
if (cs.last_status.kind == TARGET_WAITKIND_EXITED
|| cs.last_status.kind == TARGET_WAITKIND_SIGNALLED)
cs.swbreak_feature = 0;
cs.hwbreak_feature = 0;
cs.vCont_supported = 0;
+ cs.memory_tagging_feature = false;
remote_open (port);
- 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.
+ debugging anything, simply exit: GDB has disconnected
+ after processing the last process exit.
- Otherwise, close the connection and reopen it at the
- top of the loop. */
+ top of the loop. */
if (run_once || (!extended_protocol && !target_running ()))
throw_quit ("Quit");
down without informing GDB. */
if (!non_stop)
{
- if (start_non_stop (1))
+ if (the_target->start_non_stop (true))
non_stop = 1;
/* Detaching implicitly resumes all threads;
/* Event-loop callback for serial events. */
-int
+void
handle_serial_event (int err, gdb_client_data client_data)
{
if (debug_threads)
/* Really handle it. */
if (process_serial_event () < 0)
- return -1;
+ {
+ keep_processing_events = false;
+ return;
+ }
/* Be sure to not change the selected thread behind GDB's back.
Important in the non-stop mode asynchronous protocol. */
set_desired_thread ();
-
- return 0;
}
/* Push a stop notification on the notification queue. */
/* Event-loop callback for target events. */
-int
+void
handle_target_event (int err, gdb_client_data client_data)
{
client_state &cs = get_client_state ();
/* Be sure to not change the selected thread behind GDB's back.
Important in the non-stop mode asynchronous protocol. */
set_desired_thread ();
+}
+/* See gdbsupport/event-loop.h. */
+
+int
+invoke_async_signal_handlers ()
+{
return 0;
}
+/* See gdbsupport/event-loop.h. */
+
+int
+check_async_event_handlers ()
+{
+ return 0;
+}
+
+/* See gdbsupport/errors.h */
+
+void
+flush_streams ()
+{
+ fflush (stdout);
+ fflush (stderr);
+}
+
+/* See gdbsupport/gdb_select.h. */
+
+int
+gdb_select (int n, fd_set *readfds, fd_set *writefds,
+ fd_set *exceptfds, struct timeval *timeout)
+{
+ return select (n, readfds, writefds, exceptfds, timeout);
+}
+
#if GDB_SELF_TEST
namespace selftests
{