[PATCH v2 11/47] gdb, gdbserver, rsp, ze: acknowledge libraries

Tankut Baris Aktemur
2024-12-13T16:19:53Z

From: Markus Metzger <markus.t.metzger@intel.com>

On some accelerator devices, device shared libraries are loaded from a
host thread rather than from a device thread. The reporting entity may
not be the one that actually does the load.

Intel GPU devices, for example, that are based on Level-Zero, will report
shared library events via the device's debug interface. This is triggered
from a host thread calling the run-time interface for loading a device
shared library.

The Level-Zero run-time ensures that this host thread will not return
until the respective debug event has been acknowledged by the debugger.
This allows debuggers to set breakpoints before the new library is used.

Add a mechanism that allows gdbserver to request acknowledgement of newly
reported shared libraries and GDB to acknowledge requested libraries after
placing breakpoints.
---
gdb/NEWS | 7 +++
gdb/doc/gdb.texinfo | 75 +++++++++++++++++++++++++----
gdb/features/library-list.dtd | 10 ++--
gdb/remote.c | 81 ++++++++++++++++++++++++++++++++
gdb/solib-target.c | 59 ++++++++++++++++++++++-
gdb/solib.c | 24 +++++++++-
gdb/solist.h | 7 +++
gdb/target-delegates-gen.c | 50 ++++++++++++++++++++
gdb/target.c | 16 +++++++
gdb/target.h | 21 +++++++++
gdbserver/dll.cc | 100 ++++++++++++++++++++++++++++++++++++---
gdbserver/dll.h | 25 +++++++---
gdbserver/server.cc | 107 +++++++++++++++++++++++++++++++++++++++---
gdbserver/server.h | 4 ++
gdbserver/target.cc | 14 ++++++
gdbserver/target.h | 20 ++++++++
16 files changed, 585 insertions(+), 35 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index b60f66403116e73b4c5d2b2b4d847578da6e701a..698ea9d589870388c56782a3ec535899b768fe72 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -180,6 +180,13 @@ qXfer:libraries:read's response
indicates support by supplying qXfer:libraries:read:in-memory-library+ in the
qSupported packet.

+vAck:library
+vAck:in-memory-library
+
+ Acknowledge libraries to gdbserver when requested. Libraries are acknowledged
+ after the initial processing by GDB such as loading symbols and placing
+ breakpoints.
+
* Changed remote packets

qXfer:features:read:target.xml
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index c84a8372c223e724e3042322a8bee07b12423050..03fcbee94198692b0e0bb1651add94d815e287f0 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -43447,6 +43447,43 @@ for success (@pxref{Stop Reply Packets})
@cindex @samp{vStopped} packet
@xref{Notification Packets}.

+@item vAck:@var{type}:@var{arg}[,@var{arg}@dots{}][;@var{type}:@var{arg}[,@var{arg}@dots{}]]@dots{}
+@cindex @samp{vAck} packet
+@anchor{vAck packet}
+
+Acknowledge a @samp{;}-separated list of remote stub responses, each
+with a @samp{,}-separated list of arguments defined by its @var{type}.
+The following @var{type}s with their respective arguments are
+supported:
+
+@table @samp
+@item library:@var{name}
+Acknowledge the shared library that had been reported as
+@samp{<library name="@var{name}" ack="yes">} in the remote stub's
+@samp{qXfer:libraries:read} response. @value{GDBN} acknowledges
+libraries after initial processing like loading symbols and placing
+breakpoints.
+
+@item in-memory-library:@var{begin},@var{end}
+Acknowledge the shared library that had been reported as
+@samp{<in-memory-library begin="@var{begin}" end="@var{end}" ack="yes">}
+in the remote stub's @samp{qXfer:libraries:read} response.
+@value{GDBN} acknowledges libraries after initial processing like
+loading symbols and placing breakpoints.
+@end table
+
+Reply:
+@table @samp
+@item OK
+for success
+@item E @var{nn}
+for an error
+@end table
+
+@value{GDBN} indicates support for acknowledging individual types of
+responses by supplying an appropriate @samp{qSupported} feature
+(@pxref{qSupported}) for each type that it supports.
+
@item X @var{addr},@var{length}:@var{XX@dots{}}
@anchor{X packet}
@cindex @samp{X} packet
@@ -44785,6 +44822,14 @@ didn't support @samp{E.@var{errtext}}, and older versions of

New packets should be written to support @samp{E.@var{errtext}}
regardless of this feature being true or not.
+
+@item vAck:library
+This feature indicates whether @value{GDBN} supports acknowledging
+libraries reported by name.
+
+@item vAck:in-memory-library
+This feature indicates whether @value{GDBN} supports acknowledging
+in-memory libraries reported by begin and end target address.
@end table

Stubs should ignore any unknown values for
@@ -48031,6 +48076,13 @@ associated name or begin and end addresses and one or more segment or
section base addresses, which report where the library was loaded in
memory.

+It may optionally contain a request for acknowledging that library.
+@value{GDBN} indicates support for acknowledging libraries by
+supplying an appropriate @samp{qSupported} feature
+(@pxref{qSupported}). The remote stub must not request
+acknowledgement of libraries unless @value{GDBN} indicated support for
+it.
+
For the common case of libraries that are fully linked binaries, the
library should have a list of segments. If the target supports
dynamic linking of a relocatable object file, its library XML element
@@ -48056,16 +48108,21 @@ offset, looks like this:
</library-list>
@end smallexample

-A corresponding memory map for an in-memory library looks like this:
+A corresponding memory map for an in-memory library with a request for
+acknowledgement looks like this:

@smallexample
<library-list>
- <in-memory-library begin="0xa000000" end="0xa001000">
+ <in-memory-library begin="0xa000000" end="0xa001000" ack="yes">
<segment address="0x10000000"/>
</library>
</library-list>
@end smallexample

+@value{GDBN} will acknowledge the library with a @samp{vAck;library}
+or, as in this case, a @samp{vAck;in-memory-library} packet.
+@xref{vAck packet}.
+
Another simple memory map, with one loaded library with three
allocated sections (.text, .data, .bss), looks like this:

@@ -48084,16 +48141,18 @@ The format of a library list is described by this DTD:
@smallexample

<!ELEMENT library-list (library | in-memory-library)*>
-<!ATTLIST library-list version CDATA #FIXED "1.1">
+<!ATTLIST library-list version CDATA #FIXED "1.2">
<!ELEMENT library (segment*, section*)>
-<!ATTLIST library name CDATA #REQUIRED>
+<!ATTLIST library name CDATA #REQUIRED
+ ack (yes | no) 'no'>
<!ELEMENT in-memory-library (segment*, section*)>
-<!ATTLIST in-memory-library begin CDATA #REQUIRED
- end CDATA #REQUIRED>
+<!ATTLIST in-memory-library begin CDATA #REQUIRED
+ end CDATA #REQUIRED
+ ack (yes | no) 'no'>
<!ELEMENT segment EMPTY>
-<!ATTLIST segment address CDATA #REQUIRED>
+<!ATTLIST segment address CDATA #REQUIRED>
<!ELEMENT section EMPTY>
-<!ATTLIST section address CDATA #REQUIRED>
+<!ATTLIST section address CDATA #REQUIRED>
@end smallexample

In addition, segments and section descriptors cannot be mixed within a
diff --git a/gdb/features/library-list.dtd b/gdb/features/library-list.dtd
index f55071c8e906f091752e8ca78ec29bcd76028433..473b8eaa719b1d310f9eb4fe8f531df85008845d 100644
--- a/gdb/features/library-list.dtd
+++ b/gdb/features/library-list.dtd
@@ -6,14 +6,16 @@


<!ELEMENT library-list (library | in-memory-library)*>
-<!ATTLIST library-list version CDATA #FIXED "1.1">
+<!ATTLIST library-list version CDATA #FIXED "1.2">

<!ELEMENT library (segment*, section*)>
-<!ATTLIST library name CDATA #REQUIRED>
+<!ATTLIST library name CDATA #REQUIRED
+ ack (yes | no) 'no'>

<!ELEMENT in-memory-library (segment*, section*)>
-<!ATTLIST in-memory-library begin CDATA #REQUIRED
- end CDATA #REQUIRED>
+<!ATTLIST in-memory-library begin CDATA #REQUIRED
+ end CDATA #REQUIRED
+ ack (yes | no) 'no'>

<!ELEMENT segment EMPTY>
<!ATTLIST segment address CDATA #REQUIRED>
diff --git a/gdb/remote.c b/gdb/remote.c
index 7d074a5df322d68ded8f96c4832bc8c247435a4f..281e616eb83008e75da8a18efaee7001ddbf6d0d 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -376,6 +376,12 @@ enum {
/* Support remote CTRL-C. */
PACKET_vCtrlC,

+ /* Support acknowledging libraries. */
+ PACKET_vAck_library,
+
+ /* Support acknowledging in-memory-libraries. */
+ PACKET_vAck_in_memory_library,
+
/* Support TARGET_WAITKIND_NO_RESUMED. */
PACKET_no_resumed,

@@ -1154,6 +1160,9 @@ class remote_target : public process_stratum_target

bool is_address_tagged (gdbarch *gdbarch, CORE_ADDR address) override;

+ void ack_library (const char *name) override;
+ void ack_in_memory_library (CORE_ADDR begin, CORE_ADDR end) override;
+
public: /* Remote specific methods. */

void remote_download_command_source (int num, ULONGEST addr,
@@ -5845,6 +5854,10 @@ static const struct protocol_feature remote_protocol_features[] = {
PACKET_memory_tagging_feature },
{ "error-message", PACKET_ENABLE, remote_supported_packet,
PACKET_accept_error_message },
+ { "vAck:library", PACKET_DISABLE, remote_supported_packet,
+ PACKET_vAck_library },
+ { "vAck:in-memory-library", PACKET_DISABLE, remote_supported_packet,
+ PACKET_vAck_in_memory_library },
};

static char *remote_support_xml;
@@ -5959,6 +5972,14 @@ remote_target::remote_query_supported ()
remote_query_supported_append
(&q, "qXfer:libraries:read:in-memory-library+");

+ if (m_features.packet_set_cmd_state (PACKET_vAck_library)
+ != AUTO_BOOLEAN_FALSE)
+ remote_query_supported_append (&q, "vAck:library+");
+
+ if (m_features.packet_set_cmd_state (PACKET_vAck_in_memory_library)
+ != AUTO_BOOLEAN_FALSE)
+ remote_query_supported_append (&q, "vAck:in-memory-library+");
+
/* Keep this one last to work around a gdbserver <= 7.10 bug in
the qSupported:xmlRegisters=i386 handling. */
if (remote_support_xml != NULL
@@ -15679,6 +15700,60 @@ remote_target::vcont_r_supported ()
&& get_remote_state ()->supports_vCont.r);
}

+void
+remote_target::ack_library (const char *name)
+{
+ struct remote_state *rs = get_remote_state ();
+ char *p = rs->buf.data ();
+ char *endp = p + get_remote_packet_size ();
+
+ xsnprintf (p, endp - p, "vAck:library:%s", name);
+
+ putpkt (rs->buf);
+ getpkt (&rs->buf, 0);
+
+ packet_result result
+ = m_features.packet_ok (rs->buf, PACKET_vAck_library);
+ switch (result.status ())
+ {
+ case PACKET_OK:
+ break;
+ case PACKET_UNKNOWN:
+ error (_("No support for acknowledging libraries."));
+ case PACKET_ERROR:
+ error (_("Acknowledging library '%s' failed: '%s'"), name,
+ rs->buf.data ());
+ }
+}
+
+void
+remote_target::ack_in_memory_library (CORE_ADDR begin, CORE_ADDR end)
+{
+ struct remote_state *rs = get_remote_state ();
+ char *p = rs->buf.data ();
+ char *endp = p + get_remote_packet_size ();
+
+ xsnprintf (p, endp - p, "vAck:in-memory-library:%s,%s",
+ core_addr_to_string_nz (begin), core_addr_to_string_nz (end));
+
+ putpkt (rs->buf);
+ getpkt (&rs->buf, 0);
+
+ packet_result result
+ = m_features.packet_ok (rs->buf, PACKET_vAck_in_memory_library);
+ switch (result.status ())
+ {
+ case PACKET_OK:
+ break;
+ case PACKET_UNKNOWN:
+ error (_("No support for acknowledging in-memory libraries."));
+ case PACKET_ERROR:
+ error (_("Failed to acknowledge in-memory library %s-%s: %s"),
+ core_addr_to_string_nz (begin), core_addr_to_string_nz (end),
+ rs->buf.data ());
+ }
+}
+
/* The "set/show range-stepping" set hook. */

static void
@@ -16442,6 +16517,12 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,

add_packet_config_cmd (PACKET_vCtrlC, "vCtrlC", "ctrl-c", 0);

+ add_packet_config_cmd (PACKET_vAck_library,
+ "vAck:library", "ack-library", 0);
+
+ add_packet_config_cmd (PACKET_vAck_in_memory_library,
+ "vAck:in-memory-library", "ack-in-memory-library", 0);
+
add_packet_config_cmd (PACKET_QThreadEvents, "QThreadEvents", "thread-events",
0);

diff --git a/gdb/solib-target.c b/gdb/solib-target.c
index 7165aed7e22f87c96a3716397e9380aff03a2a61..bf74d79ecdb2309b7645aaf04ce9fed1bff51cff 100644
--- a/gdb/solib-target.c
+++ b/gdb/solib-target.c
@@ -53,6 +53,11 @@ struct lm_info_target final : public lm_info
This is only valid if location == lm_in_memory. */
CORE_ADDR begin = 0ull, end = 0ull;

+ /* A flag saying whether library load and unload need to be acknowledged
+ to the target after processing the library and placing/removing
+ breakpoints. */
+ bool need_ack = false;
+
/* The target can either specify segment bases or section bases, not
both. */

@@ -132,6 +137,24 @@ library_list_start_section (struct gdb_xml_parser *parser,
last->section_bases.push_back (address);
}

+/* Handle the 'ack' attribute of <library> and <in-memory-library>. */
+
+static void
+library_ack (lm_info_target &item, std::vector<gdb_xml_value> &attributes)
+{
+ gdb_xml_value *ack = xml_find_attribute (attributes, "ack");
+ if (ack != nullptr)
+ {
+ const char *value = (const char *) ack->value.get ();
+ if (strcmp (value, "yes") == 0)
+ item.need_ack = true;
+ else if (strcmp (value, "no") == 0)
+ item.need_ack = false;
+ else
+ warning (_("bad attribute value for library:ack"));
+ }
+}
+
/* Handle the start of a <library> element. */

static void
@@ -146,6 +169,8 @@ library_list_start_library (struct gdb_xml_parser *parser,
item->name
= (const char *) xml_find_attribute (attributes, "name")->value.get ();

+ library_ack (*item, attributes);
+
list->emplace_back (item);
}

@@ -165,6 +190,8 @@ in_memory_library_list_start_library (struct gdb_xml_parser *parser,
item->end = (CORE_ADDR) *(ULONGEST *)
xml_find_attribute (attributes, "end")->value.get ();

+ library_ack (*item, attributes);
+
list->emplace_back (item);
}

@@ -196,7 +223,8 @@ library_list_start_list (struct gdb_xml_parser *parser,
{
const char *string = (const char *) version->value.get ();

- if ((strcmp (string, "1.0") != 0) && (strcmp (string, "1.1") != 0))
+ if ((strcmp (string, "1.0") != 0) && (strcmp (string, "1.1") != 0)
+ && (strcmp (string, "1.2") != 0))
gdb_xml_error (parser,
_("Library list has unsupported version \"%s\""),
string);
@@ -228,12 +256,14 @@ static const struct gdb_xml_element library_children[] = {

static const struct gdb_xml_attribute library_attributes[] = {
{ "name", GDB_XML_AF_NONE, NULL, NULL },
+ { "ack", GDB_XML_AF_OPTIONAL, NULL, NULL },
{ NULL, GDB_XML_AF_NONE, NULL, NULL }
};

static const struct gdb_xml_attribute in_memory_library_attributes[] = {
{ "begin", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
{ "end", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+ { "ack", GDB_XML_AF_OPTIONAL, NULL, NULL },
{ NULL, GDB_XML_AF_NONE, NULL, NULL }
};

@@ -473,6 +503,32 @@ solib_target_in_dynsym_resolve_code (CORE_ADDR pc)
return in_plt_section (pc);
}

+static void
+solib_target_ack_library (solib &so)
+{
+ lm_info_target *lm
+ = gdb::checked_static_cast<lm_info_target *> (so.lm_info.get ());
+
+ if (!lm->need_ack)
+ return;
+
+ /* Try only once, whether we succeed or not. */
+ lm->need_ack = false;
+ switch (lm->location)
+ {
+ case lm_on_disk:
+ target_ack_library (so.so_original_name.c_str ());
+ return;
+
+ case lm_in_memory:
+ target_ack_in_memory_library (lm->begin, lm->end);
+ return;
+ }
+
+ warning (_("bad solib location '%d' for %s."), lm->location,
+ so.so_original_name.c_str ());
+}
+
const solib_ops solib_target_so_ops =
{
solib_target_relocate_section_addresses,
@@ -489,4 +545,5 @@ const solib_ops solib_target_so_ops =
nullptr,
default_find_solib_addr,
gdb_bfd_open_from_target_memory,
+ solib_target_ack_library,
};
diff --git a/gdb/solib.c b/gdb/solib.c
index cb302f9215257ddff18c94f5be39e189b02d84e6..92fc5137a3d469f55043235a65dcadcc1f25fe96 100644
--- a/gdb/solib.c
+++ b/gdb/solib.c
@@ -967,6 +967,7 @@ solib_add (const char *pattern, int from_tty, int readsyms)
if (from_tty)
add_flags |= SYMFILE_VERBOSE;

+ std::list<solib *> added_solibs;
for (solib &gdb : current_program_space->solibs ())
if (!pattern || re_exec (gdb.so_name.c_str ()))
{
@@ -989,14 +990,23 @@ solib_add (const char *pattern, int from_tty, int readsyms)
styled_string (file_name_style.style (),
gdb.so_name.c_str ()));
}
- else if (solib_read_symbols (gdb, add_flags))
- loaded_any_symbols = true;
+ else
+ added_solibs.emplace_back (&gdb);
}
}

+ for (solib *gdb : added_solibs)
+ if (solib_read_symbols (*gdb, add_flags))
+ loaded_any_symbols = true;
+
if (loaded_any_symbols)
breakpoint_re_set ();

+ /* Acknowledge loading of new solibs. This must be called after
+ breakpoints have been set in this newly loaded solib. */
+ for (solib *gdb : added_solibs)
+ solib_ack_library (*gdb);
+
if (from_tty && pattern && !any_matches)
gdb_printf ("No loaded shared libraries match the pattern `%s'.\n",
pattern);
@@ -1700,6 +1710,16 @@ default_find_solib_addr (solib &so)
return {};
}

+/* See solist.h. */
+
+void solib_ack_library (solib &so)
+{
+ const solib_ops *ops = gdbarch_so_ops (current_inferior ()->arch ());
+
+ if (ops->ack_library != nullptr)
+ (*ops->ack_library) (so);
+}
+
void _initialize_solib ();

void
diff --git a/gdb/solist.h b/gdb/solist.h
index c4d053a5c6c0261f1176121b2bc1f904ac2424a4..f64e45d874f68c644d1c5b6b182c7667319b6fad 100644
--- a/gdb/solist.h
+++ b/gdb/solist.h
@@ -191,6 +191,10 @@ struct solib_ops
gdb_bfd_ref_ptr (*bfd_open_from_target_memory) (CORE_ADDR addr,
CORE_ADDR size,
const char *target);
+
+ /* Acknowledge a library. This is called from add_solib after loading
+ symbols and placing breakpoints. */
+ void (*ack_library) (solib &so);
};

/* A unique pointer to a so_list. */
@@ -215,4 +219,7 @@ extern gdb_bfd_ref_ptr solib_bfd_open (const char *in_pathname);
unable to find an address within the library SO. */
extern std::optional<CORE_ADDR> default_find_solib_addr (solib &so);

+/* Acknowledge a library. */
+extern void solib_ack_library (solib &so);
+
#endif
diff --git a/gdb/target-delegates-gen.c b/gdb/target-delegates-gen.c
index dd20e1404c32f45c536756e6e09cdadfab29d79b..d3f1a7cc313832fd376c2c93c505f334ab9f3e33 100644
--- a/gdb/target-delegates-gen.c
+++ b/gdb/target-delegates-gen.c
@@ -190,6 +190,8 @@ struct dummy_target : public target_ops
void call_history_from (ULONGEST arg0, int arg1, record_print_flags arg2) override;
void call_history_range (ULONGEST arg0, ULONGEST arg1, record_print_flags arg2) override;
bool augmented_libraries_svr4_read () override;
+ void ack_library (const char *arg0) override;
+ void ack_in_memory_library (CORE_ADDR arg0, CORE_ADDR arg1) override;
const struct frame_unwind *get_unwinder () override;
const struct frame_unwind *get_tailcall_unwinder () override;
void prepare_to_generate_core () override;
@@ -367,6 +369,8 @@ struct debug_target : public target_ops
void call_history_from (ULONGEST arg0, int arg1, record_print_flags arg2) override;
void call_history_range (ULONGEST arg0, ULONGEST arg1, record_print_flags arg2) override;
bool augmented_libraries_svr4_read () override;
+ void ack_library (const char *arg0) override;
+ void ack_in_memory_library (CORE_ADDR arg0, CORE_ADDR arg1) override;
const struct frame_unwind *get_unwinder () override;
const struct frame_unwind *get_tailcall_unwinder () override;
void prepare_to_generate_core () override;
@@ -4194,6 +4198,52 @@ debug_target::augmented_libraries_svr4_read ()
return result;
}

+void
+target_ops::ack_library (const char *arg0)
+{
+ this->beneath ()->ack_library (arg0);
+}
+
+void
+dummy_target::ack_library (const char *arg0)
+{
+ tcomplain ();
+}
+
+void
+debug_target::ack_library (const char *arg0)
+{
+ gdb_printf (gdb_stdlog, "-> %s->ack_library (...)\n", this->beneath ()->shortname ());
+ this->beneath ()->ack_library (arg0);
+ gdb_printf (gdb_stdlog, "<- %s->ack_library (", this->beneath ()->shortname ());
+ target_debug_print_const_char_p (arg0);
+ gdb_puts (")\n", gdb_stdlog);
+}
+
+void
+target_ops::ack_in_memory_library (CORE_ADDR arg0, CORE_ADDR arg1)
+{
+ this->beneath ()->ack_in_memory_library (arg0, arg1);
+}
+
+void
+dummy_target::ack_in_memory_library (CORE_ADDR arg0, CORE_ADDR arg1)
+{
+ tcomplain ();
+}
+
+void
+debug_target::ack_in_memory_library (CORE_ADDR arg0, CORE_ADDR arg1)
+{
+ gdb_printf (gdb_stdlog, "-> %s->ack_in_memory_library (...)\n", this->beneath ()->shortname ());
+ this->beneath ()->ack_in_memory_library (arg0, arg1);
+ gdb_printf (gdb_stdlog, "<- %s->ack_in_memory_library (", this->beneath ()->shortname ());
+ target_debug_print_CORE_ADDR (arg0);
+ gdb_puts (", ", gdb_stdlog);
+ target_debug_print_CORE_ADDR (arg1);
+ gdb_puts (")\n", gdb_stdlog);
+}
+
const struct frame_unwind *
target_ops::get_unwinder ()
{
diff --git a/gdb/target.c b/gdb/target.c
index b6d1abe82dbfd93df601c599fc03d744d9094d00..fc7bda11eefea6a8d6c24380d98554e6ea385269 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -4226,6 +4226,22 @@ target_done_generating_core (void)
current_inferior ()->top_target ()->done_generating_core ();
}

+/* See target.h. */
+
+void
+target_ack_library (const char *name)
+{
+ current_inferior ()->top_target ()->ack_library (name);
+}
+
+/* See target.h. */
+
+void
+target_ack_in_memory_library (CORE_ADDR begin, CORE_ADDR end)
+{
+ current_inferior ()->top_target ()->ack_in_memory_library (begin, end);
+}
+
\f

static char targ_desc[] =
diff --git a/gdb/target.h b/gdb/target.h
index be84b27fd43206cf63bc6161321171dfb01b00aa..630944cde3a78a2fa8f2ed32282f995b66dc274a 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -1320,6 +1320,21 @@ struct target_ops
virtual bool augmented_libraries_svr4_read ()
TARGET_DEFAULT_RETURN (false);

+ /* Acknowledge a library reported by name.
+
+ Libraries are acknowledged after initial processing like loading
+ symbols and placing breakpoints on request from the target. */
+ virtual void ack_library (const char *name)
+ TARGET_DEFAULT_NORETURN (tcomplain ());
+
+ /* Acknowledge an in-memory library reported by begin and end target
+ addresses.
+
+ Libraries are acknowledged after initial processing like loading
+ symbols and placing breakpoints on request from the target. */
+ virtual void ack_in_memory_library (CORE_ADDR begin, CORE_ADDR end)
+ TARGET_DEFAULT_NORETURN (tcomplain ());
+
/* Those unwinders are tried before any other arch unwinders. If
SELF doesn't have unwinders, it should delegate to the
"beneath" target. */
@@ -2629,4 +2644,10 @@ extern void target_prepare_to_generate_core (void);
/* See to_done_generating_core. */
extern void target_done_generating_core (void);

+/* See target_ops::ack_library. */
+extern void target_ack_library (const char *name);
+
+/* See target_ops::ack_in_memory_library. */
+extern void target_ack_in_memory_library (CORE_ADDR begin, CORE_ADDR end);
+
#endif /* !defined (TARGET_H) */
diff --git a/gdbserver/dll.cc b/gdbserver/dll.cc
index c2c63e9a69a54ddede77e40ea33b4e19cbdc71af..bbc8169920cf5b099e12b3eaec1c272d9ed2bf0a 100644
--- a/gdbserver/dll.cc
+++ b/gdbserver/dll.cc
@@ -25,18 +25,24 @@
/* Record a newly loaded DLL at BASE_ADDR for the current process. */

void
-loaded_dll (const char *name, CORE_ADDR base_addr)
+loaded_dll (const char *name, CORE_ADDR base_addr, bool need_ack)
{
- loaded_dll (current_process (), name, base_addr);
+ loaded_dll (current_process (), name, base_addr, need_ack);
}

/* Record a newly loaded DLL at BASE_ADDR for PROC. */

void
-loaded_dll (process_info *proc, const char *name, CORE_ADDR base_addr)
+loaded_dll (process_info *proc, const char *name, CORE_ADDR base_addr,
+ bool need_ack)
{
+ if (need_ack && !get_client_state ().vack_library_supported)
+ throw_error (NOT_SUPPORTED_ERROR,
+ _("library acknowledgement not supported."));
+
gdb_assert (proc != nullptr);
- proc->all_dlls.emplace_back (name != nullptr ? name : "", base_addr);
+ proc->all_dlls.emplace_back (name != nullptr ? name : "", base_addr,
+ need_ack);
proc->dlls_changed = true;
}

@@ -44,10 +50,16 @@ loaded_dll (process_info *proc, const char *name, CORE_ADDR base_addr)

void
loaded_dll (process_info *proc, CORE_ADDR begin, CORE_ADDR end,
- CORE_ADDR base_addr)
+ CORE_ADDR base_addr, bool need_ack)
{
+ /* It suffices to assert support for on-disk library acknowledgement since we
+ can fall back to that. */
+ if (need_ack && !get_client_state ().vack_library_supported)
+ throw_error (NOT_SUPPORTED_ERROR,
+ _("library acknowledgement not supported."));
+
gdb_assert (proc != nullptr);
- proc->all_dlls.emplace_back (begin, end, base_addr);
+ proc->all_dlls.emplace_back (begin, end, base_addr, need_ack);
proc->dlls_changed = true;
}

@@ -60,6 +72,78 @@ unloaded_dll (const char *name, CORE_ADDR base_addr)
unloaded_dll (current_process (), name, base_addr);
}

+static void
+ack_dll (process_info *process, dll_info &dll)
+{
+ gdb_assert (dll.need_ack);
+
+ switch (dll.location)
+ {
+ case dll_info::on_disk:
+ /* Check if this is a temporary file for an in-memory library. */
+ if (dll.begin == UNSPECIFIED_CORE_ADDR)
+ {
+ target_ack_library (process, dll.name.c_str ());
+ dll.need_ack = false;
+ return;
+ }
+
+ [[fallthrough]];
+ case dll_info::in_memory:
+ target_ack_in_memory_library (process, dll.begin, dll.end);
+ dll.need_ack = false;
+ return;
+ }
+
+ internal_error (_("bad library location: %d."), dll.location);
+}
+
+void
+ack_dll (process_info *proc, const char *name)
+{
+ std::list<dll_info> &dlls = proc->all_dlls;
+ std::list<dll_info>::iterator it
+ = std::find_if (dlls.begin (), dlls.end (),
+ [name] (const dll_info &dll)
+ {
+ return (dll.name == std::string (name));
+ });
+
+ if (it != dlls.end ())
+ ack_dll (proc, *it);
+}
+
+void
+ack_dll (const char *name)
+{
+ ack_dll (current_process (), name);
+}
+
+void
+ack_dll (process_info *proc, CORE_ADDR begin, CORE_ADDR end)
+{
+ std::list<dll_info> &dlls = proc->all_dlls;
+ std::list<dll_info>::iterator it
+ = std::find_if (dlls.begin (), dlls.end (),
+ [begin, end] (const dll_info &dll)
+ {
+ /* For root devices with multiple sub-devices, modules with
+ identical start/end addresses may be received for different
+ sub-devices. Therefore we check for the 'NEED_ACK' flag in
+ the search, too. */
+ return ((dll.begin == begin) && (dll.end == end) && dll.need_ack);
+ });
+
+ if (it != dlls.end ())
+ ack_dll (proc, *it);
+}
+
+void
+ack_dll (CORE_ADDR begin, CORE_ADDR end)
+{
+ ack_dll (current_process (), begin, end);
+}
+
/* Record that the DLL with NAME and BASE_ADDR has been unloaded
from PROC. */

@@ -99,6 +183,8 @@ unloaded_dll (process_info *proc, const char *name, CORE_ADDR base_addr)
{
/* DLL has been found so remove the entry and free associated
resources. */
+ if (iter->need_ack)
+ ack_dll (proc, *iter);
proc->all_dlls.erase (iter);
proc->dlls_changed = true;
}
@@ -144,6 +230,8 @@ unloaded_dll (process_info *proc, CORE_ADDR begin, CORE_ADDR end,
{
/* DLL has been found so remove the entry and free associated
resources. */
+ if (iter->need_ack)
+ ack_dll (proc, *iter);
proc->all_dlls.erase (iter);
proc->dlls_changed = 1;
}
diff --git a/gdbserver/dll.h b/gdbserver/dll.h
index 9bce28e70cf3939b5b062e4b9c0f0d2d10a1d1d7..fa65456db6da49d2700cc468ccb95a63a0c2059a 100644
--- a/gdbserver/dll.h
+++ b/gdbserver/dll.h
@@ -30,12 +30,15 @@ struct dll_info
in_memory
};

- dll_info (const std::string &name_, CORE_ADDR base_addr_)
- : location (on_disk), name (name_), base_addr (base_addr_)
+ dll_info (const std::string &name_, CORE_ADDR base_addr_, bool need_ack_)
+ : location (on_disk), name (name_), begin (0), end (0),
+ base_addr (base_addr_), need_ack (need_ack_)
{}

- dll_info (CORE_ADDR begin_, CORE_ADDR end_, CORE_ADDR base_addr_)
- : location (in_memory), begin (begin_), end (end_), base_addr (base_addr_)
+ dll_info (CORE_ADDR begin_, CORE_ADDR end_, CORE_ADDR base_addr_,
+ bool need_ack_)
+ : location (in_memory), begin (begin_), end (end_), base_addr (base_addr_),
+ need_ack (need_ack_)
{}

location_t location;
@@ -43,17 +46,25 @@ struct dll_info
CORE_ADDR begin;
CORE_ADDR end;
CORE_ADDR base_addr;
+ bool need_ack;
};

-extern void loaded_dll (const char *name, CORE_ADDR base_addr);
+/* Throws NOT_SUPPORTED_ERROR if library acknowledgement is requested
+ (NEED_ACK = TRUE) and not supported. */
+extern void loaded_dll (const char *name, CORE_ADDR base_addr,
+ bool need_ack = false);
extern void loaded_dll (process_info *proc, const char *name,
- CORE_ADDR base_addr);
+ CORE_ADDR base_addr, bool need_ack = false);
extern void loaded_dll (process_info *proc, CORE_ADDR begin, CORE_ADDR end,
- CORE_ADDR base_addr);
+ CORE_ADDR base_addr, bool need_ack = false);
extern void unloaded_dll (const char *name, CORE_ADDR base_addr);
extern void unloaded_dll (process_info *proc, const char *name,
CORE_ADDR base_addr);
extern void unloaded_dll (process_info *proc, CORE_ADDR begin, CORE_ADDR end,
CORE_ADDR base_addr);
+extern void ack_dll (const char *name);
+extern void ack_dll (process_info *proc, const char *name);
+extern void ack_dll (CORE_ADDR begin, CORE_ADDR end);
+extern void ack_dll (process_info *proc, CORE_ADDR begin, CORE_ADDR end);

#endif /* GDBSERVER_DLL_H */
diff --git a/gdbserver/server.cc b/gdbserver/server.cc
index 90902099a1b5a84fe86cb8391bf983a5943e78c5..68ee52a7615b51c24e3c609bd78b525416b71420 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -1919,25 +1919,33 @@ dll_to_tmpfile (dll_info &dll)
static std::string
print_qxfer_libraries_entry (dll_info &dll)
{
+ const client_state &cs = get_client_state ();
+
switch (dll.location)
{
case dll_info::in_memory:
- if (get_client_state ().in_memory_library_supported)
+ if (cs.in_memory_library_supported
+ && (!dll.need_ack || cs.vack_in_memory_library_supported))
return string_printf
- (" <in-memory-library begin=\"0x%s\" end=\"0x%s\">"
+ (" <in-memory-library begin=\"0x%s\" end=\"0x%s\"%s>"
"<segment address=\"0x%s\"/></in-memory-library>\n",
paddress (dll.begin), paddress (dll.end),
- paddress (dll.base_addr));
+ dll.need_ack ? " ack=\"yes\"" : "", paddress (dll.base_addr));

- /* GDB does not support in-memory-library. Fall back to storing it in a
- temporary file and report that file to GDB. */
+ /* GDB does not support in-memory-library or acknowledging in-memory
+ libraries. Fall back to storing it in a temporary file and report that
+ file to GDB. */
dll.name = dll_to_tmpfile (dll);
[[fallthrough]];

case dll_info::on_disk:
+ /* We checked this when requesting acknowledgement for DLL. */
+ gdb_assert (!dll.need_ack || cs.vack_library_supported);
+
return string_printf
- (" <library name=\"%s\"><segment address=\"0x%s\"/></library>\n",
- dll.name.c_str (), paddress (dll.base_addr));
+ (" <library name=\"%s\"%s><segment address=\"0x%s\"/></library>\n",
+ dll.name.c_str (), dll.need_ack ? " ack=\"yes\"" : "",
+ paddress (dll.base_addr));
}

warning (_("unknown dll location: %x"), dll.location);
@@ -1970,6 +1978,18 @@ library_list_version_needed (const std::list<dll_info> &dlls)
}
break;
}
+
+ if (dll.need_ack)
+ {
+ /* We checked support for acknowledgement when we inserted DLL.
+
+ It suffices to assert support for on-disk library acknowledgement
+ since we can fall back to that. */
+ gdb_assert (!dll.need_ack || cs.vack_library_supported);
+
+ major = std::max (major, 1);
+ minor = std::max (minor, 2);
+ }
}

return std::to_string (major) + std::string (".") + std::to_string (minor);
@@ -2854,6 +2874,10 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
cs.error_message_supported = true;
else if (feature == "qXfer:libraries:read:in-memory-library+")
cs.in_memory_library_supported = true;
+ else if (feature == "vAck:library+")
+ cs.vack_library_supported = true;
+ else if (feature == "vAck:in-memory-library+")
+ cs.vack_in_memory_library_supported = true;
else
{
/* Move the unknown features all together. */
@@ -2984,6 +3008,9 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
if (target_supports_memory_tagging ())
strcat (own_buf, ";memory-tagging+");

+ strcat (own_buf, ";vAck:library+");
+ strcat (own_buf, ";vAck:in-memory-library+");
+
/* Reinitialize components as needed for the new connection. */
hostio_handle_new_gdb_connection ();
target_handle_new_gdb_connection ();
@@ -3371,6 +3398,66 @@ err:
return;
}

+/* Parse vAck packets. */
+
+static void
+handle_v_ack (char *own_buf)
+{
+ client_state &cs = get_client_state ();
+ char *p;
+
+ /* Move past vAck: to the first type string. */
+ p = &own_buf[5];
+ do
+ {
+ if (cs.vack_library_supported
+ && (strncmp (p, "library:", strlen ("library:")) == 0))
+ {
+ p += strlen ("library:");
+
+ /* We expect a single argument: the filename. */
+ const char *name = p;
+ p = strchr (p, ';');
+ if (p != nullptr)
+ *p++ = '\0';
+
+ ack_dll (name);
+ }
+ else if (cs.vack_in_memory_library_supported
+ && (strncmp (p, "in-memory-library:",
+ strlen ("in-memory-library:")) == 0))
+ {
+ p += strlen ("in-memory-library:");
+
+ /* We expect two arguments: begin and end address. */
+ CORE_ADDR begin, end;
+
+ begin = (CORE_ADDR) strtoull (p, &p, 16);
+ if (*p != ',')
+ break;
+
+ end = (CORE_ADDR) strtoull (p+1, &p, 16);
+ if (*p == ';')
+ p += 1;
+ else if (*p != 0)
+ break;
+
+ ack_dll (begin, end);
+ }
+ else
+ break;
+ }
+ while (p != nullptr && *p != 0);
+
+ if (p == nullptr || *p == 0)
+ write_ok (own_buf);
+ else
+ {
+ std::string junk { p };
+ sprintf (own_buf, "E.junk in vAck: '%s'.", junk.c_str ());
+ }
+}
+
/* Resume target with ACTIONS, an array of NUM_ACTIONS elements. */

static void
@@ -3717,6 +3804,12 @@ handle_v_requests (char *own_buf, int packet_len, int *new_packet_len)
return;
}

+ if (startswith (own_buf, "vAck:"))
+ {
+ handle_v_ack (own_buf);
+ return;
+ }
+
if (handle_notif_ack (own_buf, packet_len))
return;

diff --git a/gdbserver/server.h b/gdbserver/server.h
index 3bacd1062f8bf47720189d2d02b36ddb6089e6bc..ffbedc43cfc22bda65349751b6f23574ef7fd05b 100644
--- a/gdbserver/server.h
+++ b/gdbserver/server.h
@@ -199,6 +199,10 @@ struct client_state
are not supported with qRcmd and m packets, but are still supported
everywhere else. This is for backward compatibility reasons. */
bool error_message_supported = false;
+
+ /* Track supported packets. */
+ bool vack_library_supported = false;
+ bool vack_in_memory_library_supported = false;
};

client_state &get_client_state ();
diff --git a/gdbserver/target.cc b/gdbserver/target.cc
index 6db32da2e959284fea72f37ec2ddacf8071022e4..b07a4405c01ee7132bd26f83b603130f1d30e9fe 100644
--- a/gdbserver/target.cc
+++ b/gdbserver/target.cc
@@ -441,6 +441,20 @@ process_stratum_target::store_memtags (CORE_ADDR address, size_t len,
gdb_assert_not_reached ("target op store_memtags not supported");
}

+void
+process_stratum_target::ack_library (process_info *process, const char *name)
+{
+ gdb_assert_not_reached ("target op ack_library not supported");
+}
+
+void
+process_stratum_target::ack_in_memory_library (process_info *process,
+ CORE_ADDR begin,
+ CORE_ADDR end)
+{
+ gdb_assert_not_reached ("target op ack_in_memory_library not supported");
+}
+
int
process_stratum_target::read_offsets (CORE_ADDR *text, CORE_ADDR *data)
{
diff --git a/gdbserver/target.h b/gdbserver/target.h
index 3643b9110dac760f3aadabe481b6f360d7210f5e..2fa88e09ffbc65c37fe468bbc5ea3580971ccfdb 100644
--- a/gdbserver/target.h
+++ b/gdbserver/target.h
@@ -516,6 +516,13 @@ class process_stratum_target
Returns true if successful and false otherwise. */
virtual bool store_memtags (CORE_ADDR address, size_t len,
const gdb::byte_vector &tags, int type);
+
+ /* Acknowledge a library reported by name. */
+ virtual void ack_library (process_info *process, const char *name);
+
+ /* Acknowledge an in-memory library reported by address. */
+ virtual void ack_in_memory_library (process_info *process, CORE_ADDR begin,
+ CORE_ADDR end);
};

extern process_stratum_target *the_target;
@@ -713,6 +720,19 @@ target_thread_pending_child (thread_info *thread, target_waitkind *kind)
return the_target->thread_pending_child (thread, kind);
}

+static inline void
+target_ack_library (process_info *process, const char *name)
+{
+ the_target->ack_library (process, name);
+}
+
+static inline void
+target_ack_in_memory_library (process_info *process, CORE_ADDR begin,
+ CORE_ADDR end)
+{
+ the_target->ack_in_memory_library (process, begin, end);
+}
+
/* Read LEN bytes from MEMADDR in the buffer MYADDR. Return 0 if the read
is successful, otherwise, return a non-zero error code. */