#include <algorithm>
#include <unordered_map>
#include "async-event.h"
+#include "gdbsupport/selftest.h"
/* The remote target. */
void detach (inferior *, int) override;
void disconnect (const char *, int) override;
- void commit_resume () override;
+ void commit_resumed () override;
void resume (ptid_t, int, enum gdb_signal) override;
ptid_t wait (ptid_t, struct target_waitstatus *, target_wait_flags) override;
+ bool has_pending_events () override;
void fetch_registers (struct regcache *, int) override;
void store_registers (struct regcache *, int) override;
const struct btrace_config *btrace_conf (const struct btrace_target_info *) override;
bool augmented_libraries_svr4_read () override;
- bool follow_fork (bool, bool) override;
- void follow_exec (struct inferior *, const char *) override;
+ void follow_fork (bool, bool) override;
+ void follow_exec (inferior *, ptid_t, const char *) override;
int insert_fork_catchpoint (int) override;
int remove_fork_catchpoint (int) override;
int insert_vfork_catchpoint (int) override;
int remove_exec_catchpoint (int) override;
enum exec_direction_kind execution_direction () override;
+ bool supports_memory_tagging () override;
+
+ bool fetch_memtags (CORE_ADDR address, size_t len,
+ gdb::byte_vector &tags, int type) override;
+
+ bool store_memtags (CORE_ADDR address, size_t len,
+ const gdb::byte_vector &tags, int type) override;
+
public: /* Remote specific methods. */
void remote_download_command_source (int num, ULONGEST addr,
int remote_resume_with_vcont (ptid_t ptid, int step,
gdb_signal siggnal);
- void add_current_inferior_and_thread (const char *wait_status);
+ thread_info *add_current_inferior_and_thread (const char *wait_status);
ptid_t wait_ns (ptid_t ptid, struct target_waitstatus *status,
target_wait_flags options);
ptid_t select_thread_for_ambiguous_stop_reply
(const struct target_waitstatus *status);
- void remote_notice_new_inferior (ptid_t currthread, int executing);
+ void remote_notice_new_inferior (ptid_t currthread, bool executing);
void process_initial_stop_replies (int from_tty);
int stop_reply_queue_length ();
void check_pending_events_prevent_wildcard_vcont
- (int *may_global_wildcard_vcont);
+ (bool *may_global_wildcard_vcont);
void discard_pending_stop_replies_in_queue ();
struct stop_reply *remote_notif_remove_queued_reply (ptid_t ptid);
name, title);
/* set/show TITLE-packet {auto,on,off} */
cmd_name = xstrprintf ("%s-packet", title);
- add_setshow_auto_boolean_cmd (cmd_name, class_obscure,
- &config->detect, set_doc,
- show_doc, NULL, /* help_doc */
- NULL,
- show_remote_protocol_packet_cmd,
- &remote_set_cmdlist, &remote_show_cmdlist);
+ set_show_commands cmds
+ = add_setshow_auto_boolean_cmd (cmd_name, class_obscure,
+ &config->detect, set_doc,
+ show_doc, NULL, /* help_doc */
+ NULL,
+ show_remote_protocol_packet_cmd,
+ &remote_set_cmdlist, &remote_show_cmdlist);
+
/* The command code copies the documentation strings. */
xfree (set_doc);
xfree (show_doc);
+
/* set/show remote NAME-packet {auto,on,off} -- legacy. */
if (legacy)
{
char *legacy_name;
legacy_name = xstrprintf ("%s-packet", name);
- add_alias_cmd (legacy_name, cmd_name, class_obscure, 0,
+ add_alias_cmd (legacy_name, cmds.set, class_obscure, 0,
&remote_set_cmdlist);
- add_alias_cmd (legacy_name, cmd_name, class_obscure, 0,
+ add_alias_cmd (legacy_name, cmds.show, class_obscure, 0,
&remote_show_cmdlist);
}
}
/* Support TARGET_WAITKIND_NO_RESUMED. */
PACKET_no_resumed,
+ /* Support for memory tagging, allocation tag fetch/store
+ packets and the tag violation stop replies. */
+ PACKET_memory_tagging_feature,
+
PACKET_MAX
};
return packet_support (PACKET_exec_event_feature) == PACKET_ENABLE;
}
+/* Returns true if memory tagging is supported, false otherwise. */
+
+static bool
+remote_memory_tagging_p ()
+{
+ return packet_support (PACKET_memory_tagging_feature) == PACKET_ENABLE;
+}
+
/* Insert fork catchpoint target routine. If fork events are enabled
then return success, nothing more to do. */
inf = add_inferior_with_spaces ();
}
switch_to_inferior_no_thread (inf);
- push_target (this);
+ inf->push_target (this);
inferior_appeared (inf, pid);
}
thread is (internally) executing or stopped. */
void
-remote_target::remote_notice_new_inferior (ptid_t currthread, int executing)
+remote_target::remote_notice_new_inferior (ptid_t currthread, bool executing)
{
/* In non-stop mode, we assume new found threads are (externally)
running until proven otherwise with a stop reply. In all-stop,
we can only get here if all threads are stopped. */
- int running = target_is_non_stop_p () ? 1 : 0;
+ bool running = target_is_non_stop_p ();
/* If this is a new thread, add it to GDB's thread list.
If we leave it up to WFI to do this, bad things will happen. */
executing until proven otherwise with a stop reply.
In all-stop, we can only get here if all threads are
stopped. */
- int executing = target_is_non_stop_p () ? 1 : 0;
+ bool executing = target_is_non_stop_p ();
remote_notice_new_inferior (item.ptid, executing);
whose response is a stop reply from which we can also try
extracting the thread. If the target doesn't support the explicit
qC query, we infer the current thread from that stop reply, passed
- in in WAIT_STATUS, which may be NULL. */
+ in in WAIT_STATUS, which may be NULL.
-void
+ The function returns pointer to the main thread of the inferior. */
+
+thread_info *
remote_target::add_current_inferior_and_thread (const char *wait_status)
{
struct remote_state *rs = get_remote_state ();
yet. */
thread_info *tp = add_thread_silent (this, curr_ptid);
switch_to_thread_no_regs (tp);
+
+ return tp;
}
/* Print info about a thread that was found already stopped on
Ctrl-C before we're connected and synced up can't interrupt the
target. Instead, it offers to drop the (potentially wedged)
connection. */
- rs->starting_up = 1;
+ rs->starting_up = true;
QUIT;
/* We're connected, but not running. Drop out before we
call start_remote. */
- rs->starting_up = 0;
+ rs->starting_up = false;
return;
}
else
/* Target has no concept of threads at all. GDB treats
non-threaded target as single-threaded; add a main
thread. */
- add_current_inferior_and_thread (wait_status);
+ thread_info *tp = add_current_inferior_and_thread (wait_status);
+ get_remote_thread_info (tp)->set_resumed ();
}
else
{
/* We're connected, but not running. Drop out before we
call start_remote. */
- rs->starting_up = 0;
+ rs->starting_up = false;
return;
}
target, our symbols have been relocated, and we're merged the
target's tracepoints with ours. We're done with basic start
up. */
- rs->starting_up = 0;
+ rs->starting_up = false;
/* Maybe breakpoints are global and need to be inserted now. */
if (breakpoints_should_be_inserted_now ())
/* If this is a function address, return the start of code
instead of any data function descriptor. */
- sym_addr = gdbarch_convert_from_func_ptr_addr (target_gdbarch (),
- sym_addr,
- current_top_target ());
+ sym_addr = gdbarch_convert_from_func_ptr_addr
+ (target_gdbarch (), sym_addr, current_inferior ()->top_target ());
xsnprintf (msg.data (), get_remote_packet_size (), "qSymbol:%s:%s",
phex_nz (sym_addr, addr_size), &reply[8]);
{ "vContSupported", PACKET_DISABLE, remote_supported_packet, PACKET_vContSupported },
{ "QThreadEvents", PACKET_DISABLE, remote_supported_packet, PACKET_QThreadEvents },
{ "no-resumed", PACKET_DISABLE, remote_supported_packet, PACKET_no_resumed },
+ { "memory-tagging", PACKET_DISABLE, remote_supported_packet,
+ PACKET_memory_tagging_feature },
};
static char *remote_support_xml;
if (packet_set_cmd_state (PACKET_no_resumed) != AUTO_BOOLEAN_FALSE)
remote_query_supported_append (&q, "no-resumed+");
+ if (packet_set_cmd_state (PACKET_memory_tagging_feature)
+ != AUTO_BOOLEAN_FALSE)
+ remote_query_supported_append (&q, "memory-tagging+");
+
/* Keep this one last to work around a gdbserver <= 7.10 bug in
the qSupported:xmlRegisters=i386 handling. */
if (remote_support_xml != NULL
}
/* Switch to using the remote target now. */
- push_target (std::move (target_holder));
+ current_inferior ()->push_target (std::move (target_holder));
/* Register extra event sources in the event loop. */
rs->remote_async_inferior_event_token
- = create_async_event_handler (remote_async_inferior_event_handler, remote,
+ = create_async_event_handler (remote_async_inferior_event_handler, nullptr,
"remote");
rs->notif_state = remote_notif_state_allocate (remote);
target_announce_detach (from_tty);
+ if (!gdbarch_has_global_breakpoints (target_gdbarch ()))
+ {
+ /* If we're in breakpoints-always-inserted mode, or the inferior
+ is running, we have to remove breakpoints before detaching.
+ We don't do this in common code instead because not all
+ targets support removing breakpoints while the target is
+ running. The remote target / gdbserver does, though. */
+ remove_breakpoints_inf (current_inferior ());
+ }
+
/* Tell the remote target to detach. */
remote_detach_pid (pid);
it is named remote_follow_fork in anticipation of using it for the
remote target as well. */
-bool
+void
remote_target::follow_fork (bool follow_child, bool detach_fork)
{
struct remote_state *rs = get_remote_state ();
remote_detach_pid (child_pid);
}
}
-
- return false;
}
/* Target follow-exec function for remote targets. Save EXECD_PATHNAME
- in the program space of the new inferior. On entry and at return the
- current inferior is the exec'ing inferior. INF is the new exec'd
- inferior, which may be the same as the exec'ing inferior unless
- follow-exec-mode is "new". */
+ in the program space of the new inferior. */
void
-remote_target::follow_exec (struct inferior *inf, const char *execd_pathname)
+remote_target::follow_exec (inferior *follow_inf, ptid_t ptid,
+ const char *execd_pathname)
{
+ process_stratum_target::follow_exec (follow_inf, ptid, execd_pathname);
+
/* We know that this is a target file name, so if it has the "target:"
prefix we strip it off before saving it in the program space. */
if (is_target_filename (execd_pathname))
execd_pathname += strlen (TARGET_SYSROOT_PREFIX);
- set_pspace_remote_exec_file (inf->pspace, execd_pathname);
+ set_pspace_remote_exec_file (follow_inf->pspace, execd_pathname);
}
/* Same as remote_detach, but don't send the "D" packet; just disconnect. */
return static_cast<remote_inferior *> (inf->priv.get ());
}
+struct stop_reply : public notif_event
+{
+ ~stop_reply ();
+
+ /* The identifier of the thread about this event */
+ ptid_t ptid;
+
+ /* The remote state this event is associated with. When the remote
+ connection, represented by a remote_state object, is closed,
+ all the associated stop_reply events should be released. */
+ struct remote_state *rs;
+
+ struct target_waitstatus ws;
+
+ /* The architecture associated with the expedited registers. */
+ gdbarch *arch;
+
+ /* Expedited registers. This makes remote debugging a bit more
+ efficient for those targets that provide critical registers as
+ part of their normal status mechanism (as another roundtrip to
+ fetch them is avoided). */
+ std::vector<cached_reg_t> regcache;
+
+ enum target_stop_reason stop_reason;
+
+ CORE_ADDR watch_data_address;
+
+ int core;
+};
+
/* Class used to track the construction of a vCont packet in the
outgoing packet buffer. This is used to send multiple vCont
packets if we have more actions than would fit a single packet. */
/* to_commit_resume implementation. */
void
-remote_target::commit_resume ()
+remote_target::commit_resumed ()
{
- int any_process_wildcard;
- int may_global_wildcard_vcont;
-
/* If connected in all-stop mode, we'd send the remote resume
request directly from remote_resume. Likewise if
reverse-debugging, as there are no defined vCont actions for
(vCont;c). We can still send process-wide wildcards though. */
/* Start by assuming a global wildcard (vCont;c) is possible. */
- may_global_wildcard_vcont = 1;
+ bool may_global_wildcard_vcont = true;
/* And assume every process is individually wildcard-able too. */
for (inferior *inf : all_non_exited_inferiors (this))
disable process and global wildcard resumes appropriately. */
check_pending_events_prevent_wildcard_vcont (&may_global_wildcard_vcont);
+ bool any_pending_vcont_resume = false;
+
for (thread_info *tp : all_non_exited_threads (this))
{
remote_thread_info *priv = get_remote_thread_info (tp);
/* And if we can't wildcard a process, we can't wildcard
everything either. */
- may_global_wildcard_vcont = 0;
+ may_global_wildcard_vcont = false;
continue;
}
+ if (priv->get_resume_state () == resume_state::RESUMED_PENDING_VCONT)
+ any_pending_vcont_resume = true;
+
/* If a thread is the parent of an unfollowed fork, then we
can't do a global wildcard, as that would resume the fork
child. */
if (is_pending_fork_parent_thread (tp))
- may_global_wildcard_vcont = 0;
+ may_global_wildcard_vcont = false;
}
+ /* We didn't have any resumed thread pending a vCont resume, so nothing to
+ do. */
+ if (!any_pending_vcont_resume)
+ return;
+
/* Now let's build the vCont packet(s). Actions must be appended
from narrower to wider scopes (thread -> process -> global). If
we end up with too many actions for a single packet vcont_builder
gdb_assert (!thread_is_in_step_over_chain (tp));
+ /* We should never be commit-resuming a thread that has a stop reply.
+ Otherwise, we would end up reporting a stop event for a thread while
+ it is running on the remote target. */
+ remote_state *rs = get_remote_state ();
+ for (const auto &stop_reply : rs->stop_reply_queue)
+ gdb_assert (stop_reply->ptid != tp->ptid);
+
const resumed_pending_vcont_info &info
= remote_thr->resumed_pending_vcont_info ();
/* Now check whether we can send any process-wide wildcard. This is
to avoid sending a global wildcard in the case nothing is
supposed to be resumed. */
- any_process_wildcard = 0;
+ bool any_process_wildcard = false;
for (inferior *inf : all_non_exited_inferiors (this))
{
if (get_remote_inferior (inf)->may_wildcard_vcont)
{
- any_process_wildcard = 1;
+ any_process_wildcard = true;
break;
}
}
vcont_builder.flush ();
}
+/* Implementation of target_has_pending_events. */
+
+bool
+remote_target::has_pending_events ()
+{
+ if (target_can_async_p ())
+ {
+ remote_state *rs = get_remote_state ();
+
+ if (async_event_handler_marked (rs->remote_async_inferior_event_token))
+ return true;
+
+ /* Note that BUFCNT can be negative, indicating sticky
+ error. */
+ if (rs->remote_desc->bufcnt != 0)
+ return true;
+ }
+ return false;
+}
+
\f
/* Non-stop version of target_stop. Uses `vCont;t' to stop a remote
char *p = rs->buf.data ();
char *endp = p + get_remote_packet_size ();
+ /* If any thread that needs to stop was resumed but pending a vCont
+ resume, generate a phony stop_reply. However, first check
+ whether the thread wasn't resumed with a signal. Generating a
+ phony stop in that case would result in losing the signal. */
+ bool needs_commit = false;
+ for (thread_info *tp : all_non_exited_threads (this, ptid))
+ {
+ remote_thread_info *remote_thr = get_remote_thread_info (tp);
+
+ if (remote_thr->get_resume_state ()
+ == resume_state::RESUMED_PENDING_VCONT)
+ {
+ const resumed_pending_vcont_info &info
+ = remote_thr->resumed_pending_vcont_info ();
+ if (info.sig != GDB_SIGNAL_0)
+ {
+ /* This signal must be forwarded to the inferior. We
+ could commit-resume just this thread, but its simpler
+ to just commit-resume everything. */
+ needs_commit = true;
+ break;
+ }
+ }
+ }
+
+ if (needs_commit)
+ commit_resumed ();
+ else
+ for (thread_info *tp : all_non_exited_threads (this, ptid))
+ {
+ remote_thread_info *remote_thr = get_remote_thread_info (tp);
+
+ if (remote_thr->get_resume_state ()
+ == resume_state::RESUMED_PENDING_VCONT)
+ {
+ remote_debug_printf ("Enqueueing phony stop reply for thread pending "
+ "vCont-resume (%d, %ld, %ld)", tp->ptid.pid(),
+ tp->ptid.lwp (), tp->ptid.tid ());
+
+ /* Check that the thread wasn't resumed with a signal.
+ Generating a phony stop would result in losing the
+ signal. */
+ const resumed_pending_vcont_info &info
+ = remote_thr->resumed_pending_vcont_info ();
+ gdb_assert (info.sig == GDB_SIGNAL_0);
+
+ stop_reply *sr = new stop_reply ();
+ sr->ptid = tp->ptid;
+ sr->rs = rs;
+ sr->ws.kind = TARGET_WAITKIND_STOPPED;
+ sr->ws.value.sig = GDB_SIGNAL_0;
+ sr->arch = tp->inf->gdbarch;
+ sr->stop_reason = TARGET_STOPPED_BY_NO_REASON;
+ sr->watch_data_address = 0;
+ sr->core = 0;
+ this->push_stop_reply (sr);
+
+ /* Pretend that this thread was actually resumed on the
+ remote target, then stopped. If we leave it in the
+ RESUMED_PENDING_VCONT state and the commit_resumed
+ method is called while the stop reply is still in the
+ queue, we'll end up reporting a stop event to the core
+ for that thread while it is running on the remote
+ target... that would be bad. */
+ remote_thr->set_resumed ();
+ }
+ }
+
/* FIXME: This supports_vCont_probed check is a workaround until
packet_support is per-connection. */
if (packet_support (PACKET_vCont) == PACKET_SUPPORT_UNKNOWN
gdb_stdtarg->flush ();
}
-struct stop_reply : public notif_event
-{
- ~stop_reply ();
-
- /* The identifier of the thread about this event */
- ptid_t ptid;
-
- /* The remote state this event is associated with. When the remote
- connection, represented by a remote_state object, is closed,
- all the associated stop_reply events should be released. */
- struct remote_state *rs;
-
- struct target_waitstatus ws;
-
- /* The architecture associated with the expedited registers. */
- gdbarch *arch;
-
- /* Expedited registers. This makes remote debugging a bit more
- efficient for those targets that provide critical registers as
- part of their normal status mechanism (as another roundtrip to
- fetch them is avoided). */
- std::vector<cached_reg_t> regcache;
-
- enum target_stop_reason stop_reason;
-
- CORE_ADDR watch_data_address;
-
- int core;
-};
-
/* Return the length of the stop reply queue. */
int
/* acknowledge */
putpkt (remote, self->ack_command);
- if (stop_reply->ws.kind == TARGET_WAITKIND_IGNORE)
- {
- /* We got an unknown stop reply. */
- error (_("Unknown stop reply"));
- }
-
- remote->push_stop_reply (stop_reply);
+ /* Kind can be TARGET_WAITKIND_IGNORE if we have meanwhile discarded
+ the notification. It was left in the queue because we need to
+ acknowledge it and pull the rest of the notifications out. */
+ if (stop_reply->ws.kind != TARGET_WAITKIND_IGNORE)
+ remote->push_stop_reply (stop_reply);
}
static int
context->remove_thread (event->ws.value.related_pid);
}
-/* Check whether any event pending in the vStopped queue would prevent
- a global or process wildcard vCont action. Clear
- *may_global_wildcard if we can't do a global wildcard (vCont;c),
- and clear the event inferior's may_wildcard_vcont flag if we can't
- do a process-wide wildcard resume (vCont;c:pPID.-1). */
+/* Check whether any event pending in the vStopped queue would prevent a
+ global or process wildcard vCont action. Set *may_global_wildcard to
+ false if we can't do a global wildcard (vCont;c), and clear the event
+ inferior's may_wildcard_vcont flag if we can't do a process-wide
+ wildcard resume (vCont;c:pPID.-1). */
void
remote_target::check_pending_events_prevent_wildcard_vcont
- (int *may_global_wildcard)
+ (bool *may_global_wildcard)
{
struct notif_client *notif = ¬if_client_stop;
if (event->ws.kind == TARGET_WAITKIND_FORKED
|| event->ws.kind == TARGET_WAITKIND_VFORKED)
- *may_global_wildcard = 0;
-
- struct inferior *inf = find_inferior_ptid (this, event->ptid);
+ *may_global_wildcard = false;
/* This may be the first time we heard about this process.
Regardless, we must not do a global wildcard resume, otherwise
we'd resume this process too. */
- *may_global_wildcard = 0;
- if (inf != NULL)
- get_remote_inferior (inf)->may_wildcard_vcont = false;
+ *may_global_wildcard = false;
+ if (event->ptid != null_ptid)
+ {
+ inferior *inf = find_inferior_ptid (this, event->ptid);
+ if (inf != NULL)
+ get_remote_inferior (inf)->may_wildcard_vcont = false;
+ }
}
}
/* Discard the in-flight notification. */
if (reply != NULL && reply->ptid.pid () == inf->pid)
{
- delete reply;
- rns->pending_event[notif_client_stop.id] = NULL;
+ /* Leave the notification pending, since the server expects that
+ we acknowledge it with vStopped. But clear its contents, so
+ that later on when we acknowledge it, we also discard it. */
+ reply->ws.kind = TARGET_WAITKIND_IGNORE;
+
+ if (remote_debug)
+ fprintf_unfiltered (gdb_stdlog,
+ "discarded in-flight notification\n");
}
/* Discard the stop replies we have already pulled with
remote_target::select_thread_for_ambiguous_stop_reply
(const struct target_waitstatus *status)
{
+ REMOTE_SCOPED_DEBUG_ENTER_EXIT;
+
/* Some stop events apply to all threads in an inferior, while others
only apply to a single thread. */
bool process_wide_stop
= (status->kind == TARGET_WAITKIND_EXITED
|| status->kind == TARGET_WAITKIND_SIGNALLED);
+ remote_debug_printf ("process_wide_stop = %d", process_wide_stop);
+
thread_info *first_resumed_thread = nullptr;
bool ambiguous = false;
ambiguous = true;
}
+ remote_debug_printf ("first resumed thread is %s",
+ pid_to_str (first_resumed_thread->ptid).c_str ());
+ remote_debug_printf ("is this guess ambiguous? = %d", ambiguous);
+
gdb_assert (first_resumed_thread != nullptr);
/* Warn if the remote target is sending ambiguous stop replies. */
stop_reply->regcache.clear ();
}
- remote_notice_new_inferior (ptid, 0);
+ remote_notice_new_inferior (ptid, false);
remote_thread_info *remote_thr = get_remote_thread_info (this, ptid);
remote_thr->core = stop_reply->core;
remote_thr->stop_reason = stop_reply->stop_reason;
{
REMOTE_SCOPED_DEBUG_ENTER_EXIT;
+ remote_state *rs = get_remote_state ();
+
+ /* Start by clearing the flag that asks for our wait method to be called,
+ we'll mark it again at the end if needed. */
+ if (target_is_async_p ())
+ clear_async_event_handler (rs->remote_async_inferior_event_token);
+
ptid_t event_ptid;
if (target_is_non_stop_p ())
if (target_is_async_p ())
{
- remote_state *rs = get_remote_state ();
-
- /* If there are are events left in the queue tell the event loop
- to return here. */
- if (!rs->stop_reply_queue.empty ())
+ /* If there are events left in the queue, or unacknowledged
+ notifications, then tell the event loop to call us again. */
+ if (!rs->stop_reply_queue.empty ()
+ || rs->notif_state->pending_event[notif_client_stop.id] != nullptr)
mark_async_event_handler (rs->remote_async_inferior_event_token);
}
int unit_size,
ULONGEST *xfered_len)
{
- struct target_section *secp;
+ const struct target_section *secp;
secp = target_section_by_addr (this, memaddr);
if (secp != NULL
{
ULONGEST memend = memaddr + len;
- target_section_table *table = target_get_section_table (this);
- for (target_section &p : *table)
+ const target_section_table *table = target_get_section_table (this);
+ for (const target_section &p : *table)
{
if (memaddr >= p.addr)
{
{
int repeat;
- csum += c;
+ csum += c;
c = readchar (remote_timeout);
csum += c;
repeat = c - ' ' + 3; /* Compute repeat count. */
{
std::vector<mem_region> result;
gdb::optional<gdb::char_vector> text
- = target_read_stralloc (current_top_target (), TARGET_OBJECT_MEMORY_MAP, NULL);
+ = target_read_stralloc (current_inferior ()->top_target (),
+ TARGET_OBJECT_MEMORY_MAP, NULL);
if (text)
result = parse_memory_map (text->data ());
{
struct remote_state *rs = get_remote_state ();
char *reply;
- struct bp_location *loc;
struct tracepoint *tp = (struct tracepoint *) bp;
size_t size = get_remote_packet_size ();
{
tp->hit_count = 0;
tp->traceframe_usage = 0;
- for (loc = tp->loc; loc; loc = loc->next)
+ for (bp_location *loc : tp->locations ())
{
/* If the tracepoint was never downloaded, don't go asking for
any status. */
remote_target::traceframe_info ()
{
gdb::optional<gdb::char_vector> text
- = target_read_stralloc (current_top_target (), TARGET_OBJECT_TRACEFRAME_INFO,
+ = target_read_stralloc (current_inferior ()->top_target (),
+ TARGET_OBJECT_TRACEFRAME_INFO,
NULL);
if (text)
return parse_traceframe_info (text->data ());
btrace_read_config (struct btrace_config *conf)
{
gdb::optional<gdb::char_vector> xml
- = target_read_stralloc (current_top_target (), TARGET_OBJECT_BTRACE_CONF, "");
+ = target_read_stralloc (current_inferior ()->top_target (),
+ TARGET_OBJECT_BTRACE_CONF, "");
if (xml)
parse_xml_btrace_conf (conf, xml->data ());
}
}
gdb::optional<gdb::char_vector> xml
- = target_read_stralloc (current_top_target (), TARGET_OBJECT_BTRACE, annex);
+ = target_read_stralloc (current_inferior ()->top_target (),
+ TARGET_OBJECT_BTRACE, annex);
if (!xml)
return BTRACE_ERR_UNKNOWN;
xsnprintf (annex, annex_size, "%x", pid);
}
- filename = target_read_stralloc (current_top_target (),
+ filename = target_read_stralloc (current_inferior ()->top_target (),
TARGET_OBJECT_EXEC_FILE, annex);
return filename ? filename->data () : nullptr;
remote_async_inferior_event_handler (gdb_client_data data)
{
inferior_event_handler (INF_REG_EVENT);
-
- remote_target *remote = (remote_target *) data;
- remote_state *rs = remote->get_remote_state ();
-
- /* inferior_event_handler may have consumed an event pending on the
- infrun side without calling target_wait on the REMOTE target, or
- may have pulled an event out of a different target. Keep trying
- for this remote target as long it still has either pending events
- or unacknowledged notifications. */
-
- if (rs->notif_state->pending_event[notif_client_stop.id] != NULL
- || !rs->stop_reply_queue.empty ())
- mark_async_event_handler (rs->remote_async_inferior_event_token);
}
int
{
remote_target *remote = get_current_remote_target ();
- if (remote != NULL) /* Have a remote connection. */
- remote->remote_check_symbols ();
+ /* First, check whether the current inferior's process target is a remote
+ target. */
+ if (remote == nullptr)
+ return;
+
+ /* When we are attaching or handling a fork child and the shared library
+ subsystem reads the list of loaded libraries, we receive new objfile
+ events in between each found library. The libraries are read in an
+ undefined order, so if we gave the remote side a chance to look up
+ symbols between each objfile, we might give it an inconsistent picture
+ of the inferior. It could appear that a library A appears loaded but
+ a library B does not, even though library A requires library B. That
+ would present a state that couldn't normally exist in the inferior.
+
+ So, skip these events, we'll give the remote a chance to look up symbols
+ once all the loaded libraries and their symbols are known to GDB. */
+ if (current_inferior ()->in_initial_library_scan)
+ return;
+
+ remote->remote_check_symbols ();
}
/* Pull all the tracepoints defined on the target and create local
value);
}
+/* Implement the "supports_memory_tagging" target_ops method. */
+
+bool
+remote_target::supports_memory_tagging ()
+{
+ return remote_memory_tagging_p ();
+}
+
+/* Create the qMemTags packet given ADDRESS, LEN and TYPE. */
+
+static void
+create_fetch_memtags_request (gdb::char_vector &packet, CORE_ADDR address,
+ size_t len, int type)
+{
+ int addr_size = gdbarch_addr_bit (target_gdbarch ()) / 8;
+
+ std::string request = string_printf ("qMemTags:%s,%s:%s",
+ phex_nz (address, addr_size),
+ phex_nz (len, sizeof (len)),
+ phex_nz (type, sizeof (type)));
+
+ strcpy (packet.data (), request.c_str ());
+}
+
+/* Parse the qMemTags packet reply into TAGS.
+
+ Return true if successful, false otherwise. */
+
+static bool
+parse_fetch_memtags_reply (const gdb::char_vector &reply,
+ gdb::byte_vector &tags)
+{
+ if (reply.empty () || reply[0] == 'E' || reply[0] != 'm')
+ return false;
+
+ /* Copy the tag data. */
+ tags = hex2bin (reply.data () + 1);
+
+ return true;
+}
+
+/* Create the QMemTags packet given ADDRESS, LEN, TYPE and TAGS. */
+
+static void
+create_store_memtags_request (gdb::char_vector &packet, CORE_ADDR address,
+ size_t len, int type,
+ const gdb::byte_vector &tags)
+{
+ int addr_size = gdbarch_addr_bit (target_gdbarch ()) / 8;
+
+ /* Put together the main packet, address and length. */
+ std::string request = string_printf ("QMemTags:%s,%s:%s:",
+ phex_nz (address, addr_size),
+ phex_nz (len, sizeof (len)),
+ phex_nz (type, sizeof (type)));
+ request += bin2hex (tags.data (), tags.size ());
+
+ /* Check if we have exceeded the maximum packet size. */
+ if (packet.size () < request.length ())
+ error (_("Contents too big for packet QMemTags."));
+
+ strcpy (packet.data (), request.c_str ());
+}
+
+/* Implement the "fetch_memtags" target_ops method. */
+
+bool
+remote_target::fetch_memtags (CORE_ADDR address, size_t len,
+ gdb::byte_vector &tags, int type)
+{
+ /* Make sure the qMemTags packet is supported. */
+ if (!remote_memory_tagging_p ())
+ gdb_assert_not_reached ("remote fetch_memtags called with packet disabled");
+
+ struct remote_state *rs = get_remote_state ();
+
+ create_fetch_memtags_request (rs->buf, address, len, type);
+
+ putpkt (rs->buf);
+ getpkt (&rs->buf, 0);
+
+ return parse_fetch_memtags_reply (rs->buf, tags);
+}
+
+/* Implement the "store_memtags" target_ops method. */
+
+bool
+remote_target::store_memtags (CORE_ADDR address, size_t len,
+ const gdb::byte_vector &tags, int type)
+{
+ /* Make sure the QMemTags packet is supported. */
+ if (!remote_memory_tagging_p ())
+ gdb_assert_not_reached ("remote store_memtags called with packet disabled");
+
+ struct remote_state *rs = get_remote_state ();
+
+ create_store_memtags_request (rs->buf, address, len, type, tags);
+
+ putpkt (rs->buf);
+ getpkt (&rs->buf, 0);
+
+ /* Verify if the request was successful. */
+ return packet_check_result (rs->buf.data ()) == PACKET_OK;
+}
+
+/* Return true if remote target T is non-stop. */
+
+bool
+remote_target_is_non_stop_p (remote_target *t)
+{
+ scoped_restore_current_thread restore_thread;
+ switch_to_target_no_thread (t);
+
+ return target_is_non_stop_p ();
+}
+
+#if GDB_SELF_TEST
+
+namespace selftests {
+
+static void
+test_memory_tagging_functions ()
+{
+ remote_target remote;
+
+ struct packet_config *config
+ = &remote_protocol_packets[PACKET_memory_tagging_feature];
+
+ scoped_restore restore_memtag_support_
+ = make_scoped_restore (&config->support);
+
+ /* Test memory tagging packet support. */
+ config->support = PACKET_SUPPORT_UNKNOWN;
+ SELF_CHECK (remote.supports_memory_tagging () == false);
+ config->support = PACKET_DISABLE;
+ SELF_CHECK (remote.supports_memory_tagging () == false);
+ config->support = PACKET_ENABLE;
+ SELF_CHECK (remote.supports_memory_tagging () == true);
+
+ /* Setup testing. */
+ gdb::char_vector packet;
+ gdb::byte_vector tags, bv;
+ std::string expected, reply;
+ packet.resize (32000);
+
+ /* Test creating a qMemTags request. */
+
+ expected = "qMemTags:0,0:0";
+ create_fetch_memtags_request (packet, 0x0, 0x0, 0);
+ SELF_CHECK (strcmp (packet.data (), expected.c_str ()) == 0);
+
+ expected = "qMemTags:deadbeef,10:1";
+ create_fetch_memtags_request (packet, 0xdeadbeef, 16, 1);
+ SELF_CHECK (strcmp (packet.data (), expected.c_str ()) == 0);
+
+ /* Test parsing a qMemTags reply. */
+
+ /* Error reply, tags vector unmodified. */
+ reply = "E00";
+ strcpy (packet.data (), reply.c_str ());
+ tags.resize (0);
+ SELF_CHECK (parse_fetch_memtags_reply (packet, tags) == false);
+ SELF_CHECK (tags.size () == 0);
+
+ /* Valid reply, tags vector updated. */
+ tags.resize (0);
+ bv.resize (0);
+
+ for (int i = 0; i < 5; i++)
+ bv.push_back (i);
+
+ reply = "m" + bin2hex (bv.data (), bv.size ());
+ strcpy (packet.data (), reply.c_str ());
+
+ SELF_CHECK (parse_fetch_memtags_reply (packet, tags) == true);
+ SELF_CHECK (tags.size () == 5);
+
+ for (int i = 0; i < 5; i++)
+ SELF_CHECK (tags[i] == i);
+
+ /* Test creating a QMemTags request. */
+
+ /* Empty tag data. */
+ tags.resize (0);
+ expected = "QMemTags:0,0:0:";
+ create_store_memtags_request (packet, 0x0, 0x0, 0, tags);
+ SELF_CHECK (memcmp (packet.data (), expected.c_str (),
+ expected.length ()) == 0);
+
+ /* Non-empty tag data. */
+ tags.resize (0);
+ for (int i = 0; i < 5; i++)
+ tags.push_back (i);
+ expected = "QMemTags:deadbeef,ff:1:0001020304";
+ create_store_memtags_request (packet, 0xdeadbeef, 255, 1, tags);
+ SELF_CHECK (memcmp (packet.data (), expected.c_str (),
+ expected.length ()) == 0);
+}
+
+} // namespace selftests
+#endif /* GDB_SELF_TEST */
+
void _initialize_remote ();
void
_initialize_remote ()
{
- struct cmd_list_element *cmd;
- const char *cmd_name;
-
/* architecture specific data */
remote_g_packet_data_handle =
gdbarch_data_register_pre_init (remote_g_packet_data_init);
add_target (extended_remote_target_info, extended_remote_target::open);
/* Hook into new objfile notification. */
- gdb::observers::new_objfile.attach (remote_new_objfile);
+ gdb::observers::new_objfile.attach (remote_new_objfile, "remote");
#if 0
init_remote_threadtests ();
Remote protocol specific variables.\n\
Configure various remote-protocol specific variables such as\n\
the packets being used."),
- &remote_set_cmdlist, "set remote ",
+ &remote_set_cmdlist,
0 /* allow-unknown */, &setlist);
add_prefix_cmd ("remote", class_maintenance, show_remote_cmd, _("\
Remote protocol specific variables.\n\
Configure various remote-protocol specific variables such as\n\
the packets being used."),
- &remote_show_cmdlist, "show remote ",
+ &remote_show_cmdlist,
0 /* allow-unknown */, &showlist);
add_cmd ("compare-sections", class_obscure, compare_sections_command, _("\
terminating `#' character and checksum."),
&maintenancelist);
- add_setshow_boolean_cmd ("remotebreak", no_class, &remote_break, _("\
+ set_show_commands remotebreak_cmds
+ = add_setshow_boolean_cmd ("remotebreak", no_class, &remote_break, _("\
Set whether to send break if interrupted."), _("\
Show whether to send break if interrupted."), _("\
If set, a break, instead of a cntrl-c, is sent to the remote target."),
- set_remotebreak, show_remotebreak,
- &setlist, &showlist);
- cmd_name = "remotebreak";
- cmd = lookup_cmd (&cmd_name, setlist, "", NULL, -1, 1);
- deprecate_cmd (cmd, "set remote interrupt-sequence");
- cmd_name = "remotebreak"; /* needed because lookup_cmd updates the pointer */
- cmd = lookup_cmd (&cmd_name, showlist, "", NULL, -1, 1);
- deprecate_cmd (cmd, "show remote interrupt-sequence");
+ set_remotebreak, show_remotebreak,
+ &setlist, &showlist);
+ deprecate_cmd (remotebreak_cmds.set, "set remote interrupt-sequence");
+ deprecate_cmd (remotebreak_cmds.show, "show remote interrupt-sequence");
add_setshow_enum_cmd ("interrupt-sequence", class_support,
interrupt_sequence_modes, &interrupt_sequence_mode,
add_packet_config_cmd (&remote_protocol_packets[PACKET_no_resumed],
"N stop reply", "no-resumed-stop-reply", 0);
+ add_packet_config_cmd (&remote_protocol_packets[PACKET_memory_tagging_feature],
+ "memory-tagging-feature", "memory-tagging-feature", 0);
+
/* Assert that we've registered "set remote foo-packet" commands
for all packet configs. */
{
add_basic_prefix_cmd ("remote", class_files, _("\
Manipulate files on the remote system.\n\
Transfer files to and from the remote target system."),
- &remote_cmdlist, "remote ",
+ &remote_cmdlist,
0 /* allow-unknown */, &cmdlist);
add_cmd ("put", class_files, remote_put_command,
/* Eventually initialize fileio. See fileio.c */
initialize_remote_fileio (&remote_set_cmdlist, &remote_show_cmdlist);
+
+#if GDB_SELF_TEST
+ selftests::register_test ("remote_memory_tagging",
+ selftests::test_memory_tagging_functions);
+#endif
}