[PATCH v4 33/44] gdb, gdbserver, ze: in-memory libraries
Markus Metzger <[email protected]>
| Newsgroups | gmane.comp.gdb.patches |
|---|---|
| Message-ID | <[email protected]> |
For Intel GPU devices, device libraries live in the host memory and are
loaded onto the device from there.
Add support for reporting such in-memory shared libraries via
qXfer:libraries:read
and have GDB read them from target memory.
Reviewed-By: Eli Zaretskii <[email protected]>
---
gdb/NEWS | 7 +++
gdb/doc/gdb.texinfo | 39 +++++++++----
gdb/features/library-list.dtd | 6 +-
gdb/solib-target.c | 66 +++++++++++++++++++++-
gdb/solib.c | 80 ++++++++++++++++++++-------
gdb/solib.h | 23 +++++++-
gdbserver/dll.cc | 101 ++++++++++++++++++++++++++++------
gdbserver/dll.h | 30 +++++++++-
gdbserver/server.cc | 27 ++++++++-
9 files changed, 320 insertions(+), 59 deletions(-)
diff --git a/gdb/NEWS b/gdb/NEWS
index 0b949ae7825..879f8e8865d 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -11,6 +11,13 @@ qXfer:features:read:target.xml
that can be used for passing information when the remote inferior
represents a device, e.g. a GPU.
+* New remote packets
+
+qXfer:libraries:read's response
+
+ The qXfer:libraries:read query supports reporting in-memory libraries.
+ Older GDB will silently ignore them.
+
*** Changes in GDB 18
* Support for the Common Trace Format (CTF) has been removed. GDB now
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index be46ff9f6e2..4cc755a0999 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -48899,9 +48899,10 @@ queries the target's operating system and reports which libraries
are loaded.
The @samp{qXfer:libraries:read} packet returns an XML document which
-lists loaded libraries and their offsets. Each library has an
-associated name and one or more segment or section base addresses,
-which report where the library was loaded in memory.
+lists loaded libraries and their offsets. Each library has either an
+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.
For the common case of libraries that are fully linked binaries, the
library should have a list of segments. If the target supports
@@ -48913,6 +48914,9 @@ depend on the library's link-time base addresses.
@value{GDBN} must be linked with the Expat library to support XML
library lists. @xref{Expat}.
+Versions of @value{GDBN} that do not support in-memory library
+elements will silently ignore them.
+
A simple memory map, with one loaded library relocated by a single
offset, looks like this:
@@ -48924,6 +48928,16 @@ offset, looks like this:
</library-list>
@end smallexample
+A corresponding memory map for an in-memory library looks like this:
+
+@smallexample
+<library-list>
+ <in-memory-library begin="0xa000000" end="0xa001000">
+ <segment address="0x10000000"/>
+ </in-memory-library>
+</library-list>
+@end smallexample
+
Another simple memory map, with one loaded library with three
allocated sections (.text, .data, .bss), looks like this:
@@ -48941,14 +48955,17 @@ The format of a library list is described by this DTD:
@smallexample
<!-- library-list: Root element with versioning -->
-<!ELEMENT library-list (library)*>
-<!ATTLIST library-list version CDATA #FIXED "1.0">
-<!ELEMENT library (segment*, section*)>
-<!ATTLIST library name CDATA #REQUIRED>
-<!ELEMENT segment EMPTY>
-<!ATTLIST segment address CDATA #REQUIRED>
-<!ELEMENT section EMPTY>
-<!ATTLIST section address CDATA #REQUIRED>
+<!ELEMENT library-list (library | in-memory-library)*>
+<!ATTLIST library-list version CDATA #FIXED "1.0">
+<!ELEMENT library (segment*, section*)>
+<!ATTLIST library name CDATA #REQUIRED>
+<!ELEMENT in-memory-library (segment*, section*)>
+<!ATTLIST in-memory-library begin CDATA #REQUIRED
+ end CDATA #REQUIRED>
+<!ELEMENT segment EMPTY>
+<!ATTLIST segment address CDATA #REQUIRED>
+<!ELEMENT section EMPTY>
+<!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 c612d409ad4..890aac4d80c 100644
--- a/gdb/features/library-list.dtd
+++ b/gdb/features/library-list.dtd
@@ -5,12 +5,16 @@
notice and this notice are preserved. -->
<!-- library-list: Root element with versioning -->
-<!ELEMENT library-list (library)*>
+<!ELEMENT library-list (library | in-memory-library)*>
<!ATTLIST library-list version CDATA #FIXED "1.0">
<!ELEMENT library (segment*, section*)>
<!ATTLIST library name CDATA #REQUIRED>
+<!ELEMENT in-memory-library (segment*, section*)>
+<!ATTLIST in-memory-library begin CDATA #REQUIRED
+ end CDATA #REQUIRED>
+
<!ELEMENT segment EMPTY>
<!ATTLIST segment address CDATA #REQUIRED>
diff --git a/gdb/solib-target.c b/gdb/solib-target.c
index ac965df20e9..3508bc792a0 100644
--- a/gdb/solib-target.c
+++ b/gdb/solib-target.c
@@ -26,9 +26,25 @@
#include <vector>
#include "inferior.h"
+/* The location of a loaded library. */
+
+enum lm_location_t
+{
+ lm_on_disk,
+ lm_in_memory
+};
+
/* Private data for each loaded library. */
struct lm_info_target final : public lm_info
{
+ /* The library's location. */
+ lm_location_t location = lm_on_disk;
+
+ /* The library's begin and end memory addresses.
+
+ This is only valid if location == lm_in_memory. */
+ CORE_ADDR begin = 0ull, end = 0ull;
+
/* The target can either specify segment bases or section bases, not
both. */
@@ -133,6 +149,26 @@ library_list_start_library (struct gdb_xml_parser *parser,
std::make_unique<lm_info_target> () });
}
+/* Handle the start of a <in-memory-library> element. */
+
+static void
+in_memory_library_list_start_library (struct gdb_xml_parser *parser,
+ const struct gdb_xml_element *element,
+ void *user_data,
+ std::vector<gdb_xml_value> &attributes)
+{
+ const auto list = static_cast<std::vector<target_library> *> (user_data);
+ list->emplace_back (target_library { "",
+ std::make_unique<lm_info_target> () });
+
+ lm_info_target &info = *list->back ().info;
+ info.location = lm_in_memory;
+ info.begin = (CORE_ADDR) *(ULONGEST *)
+ xml_find_attribute (attributes, "begin")->value.get ();
+ info.end = (CORE_ADDR) *(ULONGEST *)
+ xml_find_attribute (attributes, "end")->value.get ();
+}
+
static void
library_list_end_library (struct gdb_xml_parser *parser,
const struct gdb_xml_element *element,
@@ -196,10 +232,19 @@ static const struct gdb_xml_attribute library_attributes[] = {
{ 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 },
+ { NULL, GDB_XML_AF_NONE, NULL, NULL }
+};
+
static const struct gdb_xml_element library_list_children[] = {
{ "library", library_attributes, library_children,
GDB_XML_EF_REPEATABLE | GDB_XML_EF_OPTIONAL,
library_list_start_library, library_list_end_library },
+ { "in-memory-library", in_memory_library_attributes, library_children,
+ GDB_XML_EF_REPEATABLE | GDB_XML_EF_OPTIONAL,
+ in_memory_library_list_start_library, library_list_end_library },
{ NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
};
@@ -249,8 +294,25 @@ target_solib_ops::current_sos () const
/* Build a struct solib for each entry on the list. */
for (auto &library : library_list)
- sos.emplace_back (std::move (library.info), library.name, library.name,
- *this);
+ {
+ switch (library.info->location)
+ {
+ case lm_on_disk:
+ sos.emplace_back (std::move (library.info), library.name,
+ library.name, *this);
+ break;
+
+ case lm_in_memory:
+ if (library.info->end <= library.info->begin)
+ warning (_("bad in-memory-library location: begin=%s, end=%s"),
+ core_addr_to_string_nz (library.info->begin),
+ core_addr_to_string_nz (library.info->end));
+ else
+ sos.emplace_back (std::move (library.info), library.info->begin,
+ library.info->end, *this);
+ break;
+ }
+ }
return sos;
}
diff --git a/gdb/solib.c b/gdb/solib.c
index 869b769d043..11cd4432d33 100644
--- a/gdb/solib.c
+++ b/gdb/solib.c
@@ -395,13 +395,36 @@ solib_bfd_fopen (const char *pathname, int fd)
return abfd;
}
+/* Initialize ABFD. */
+
+static void
+solib_bfd_init (bfd *abfd)
+{
+ const struct bfd_arch_info *b;
+
+ /* Check bfd format. */
+ if (!gdb_bfd_check_format (abfd, bfd_object))
+ error (_("`%ps': not in executable format: %s"),
+ styled_string (file_name_style.style (),
+ bfd_get_filename (abfd)),
+ bfd_errmsg (bfd_get_error ()));
+
+ /* Check bfd arch. */
+ b = gdbarch_bfd_arch_info (current_inferior ()->arch ());
+ if (!b->compatible (b, bfd_get_arch_info (abfd)))
+ error (_("`%ps': Shared library architecture %s is not compatible "
+ "with target architecture %s."),
+ styled_string (file_name_style.style (),
+ bfd_get_filename (abfd)),
+ bfd_get_arch_info (abfd)->printable_name, b->printable_name);
+}
+
/* Find shared library PATHNAME and open a BFD for it. */
gdb_bfd_ref_ptr
solib_bfd_open (const char *pathname)
{
int found_file;
- const struct bfd_arch_info *b;
/* Search for shared library file. */
gdb::unique_xmalloc_ptr<char> found_pathname
@@ -418,22 +441,7 @@ solib_bfd_open (const char *pathname)
/* Open bfd for shared library. */
gdb_bfd_ref_ptr abfd (solib_bfd_fopen (found_pathname.get (), found_file));
-
- /* Check bfd format. */
- if (!gdb_bfd_check_format (abfd.get (), bfd_object))
- error (_("`%ps': not in executable format: %s"),
- styled_string (file_name_style.style (),
- bfd_get_filename (abfd.get ())),
- bfd_errmsg (bfd_get_error ()));
-
- /* Check bfd arch. */
- b = gdbarch_bfd_arch_info (current_inferior ()->arch ());
- if (!b->compatible (b, bfd_get_arch_info (abfd.get ())))
- error (_("`%ps': Shared library architecture %s is not compatible "
- "with target architecture %s."),
- styled_string (file_name_style.style (),
- bfd_get_filename (abfd.get ())),
- bfd_get_arch_info (abfd.get ())->printable_name, b->printable_name);
+ solib_bfd_init (abfd.get ());
return abfd;
}
@@ -456,6 +464,23 @@ solib_ops::iterate_over_objfiles_in_search_order
return;
}
+gdb_bfd_ref_ptr
+solib_ops::bfd_open_from_target_memory (CORE_ADDR addr,
+ CORE_ADDR size,
+ const char *target) const
+{
+ /* Open bfd for shared library. */
+ gdb_bfd_ref_ptr abfd
+ = gdb_bfd_open_from_target_memory (addr, size, target);
+ if (abfd == nullptr)
+ error (_("Could not open file from target '%s' "
+ "at address %s with size %s."), target,
+ core_addr_to_string_nz (addr), core_addr_to_string_nz (size));
+ solib_bfd_init (abfd.get ());
+
+ return abfd;
+}
+
/* Given a pointer to one of the shared objects in our list of mapped
objects, use the recorded name to open a bfd descriptor for the
object, build a section table, relocate all the section addresses
@@ -471,9 +496,22 @@ solib_ops::iterate_over_objfiles_in_search_order
static int
solib_map_sections (solib &so)
{
- gdb::unique_xmalloc_ptr<char> filename
- = gdb_rl_tilde_expand (so.name.c_str ());
- gdb_bfd_ref_ptr abfd (so.ops ().bfd_open (filename.get ()));
+ gdb_bfd_ref_ptr abfd;
+ if (!so.name.empty ())
+ {
+ gdb::unique_xmalloc_ptr<char> filename
+ = gdb_rl_tilde_expand (so.name.c_str ());
+ abfd = so.ops ().bfd_open (filename.get ());
+ }
+ else if (so.begin != 0 && so.end != 0)
+ {
+ gdb_assert (so.begin < so.end);
+ abfd = so.ops ().bfd_open_from_target_memory (so.begin,
+ so.end - so.begin,
+ gnutarget);
+ }
+ else
+ internal_error (_("solib has neither a name nor a memory range"));
/* If we have a core target then the core target might have some helpful
information (i.e. build-ids) about the shared libraries we are trying
@@ -520,7 +558,7 @@ solib_map_sections (solib &so)
{
warning (_("Build-id of %ps does not match core file."),
styled_string (file_name_style.style (),
- filename.get ()));
+ so.name.c_str ()));
abfd = nullptr;
}
}
diff --git a/gdb/solib.h b/gdb/solib.h
index 662d467b0cc..6914fc79a96 100644
--- a/gdb/solib.h
+++ b/gdb/solib.h
@@ -58,12 +58,21 @@ struct solib_ops;
struct solib : intrusive_list_node<solib>
{
- /* Constructor
+ /* On-disk solib constructor.
OPS is the solib_ops implementation providing this solib. */
explicit solib (lm_info_up lm_info, std::string original_name,
std::string name, const solib_ops &ops);
+ /* In-memory solib constructor. */
+ explicit solib (lm_info_up lm_info, CORE_ADDR begin, CORE_ADDR end,
+ const solib_ops &ops)
+ : lm_info (std::move (lm_info)),
+ begin (begin),
+ end (end),
+ m_ops (&ops)
+ {}
+
/* Return the solib_ops implementation providing this solib. */
const solib_ops &ops () const
{ return *m_ops; }
@@ -95,9 +104,14 @@ struct solib : intrusive_list_node<solib>
map we've already loaded. */
std::string original_name;
- /* Shared object file name, expanded to something GDB can open. */
+ /* Shared object file name, expanded to something GDB can open.
+ This is an empty string for in-memory shared objects. */
std::string name;
+ /* The address range of an in-memory shared object. Both BEGIN and END
+ are zero for on-disk shared objects. */
+ CORE_ADDR begin = 0, end = 0;
+
/* The following fields of the structure are built from
information gathered from the shared object file itself, and
are set when we actually add it to our symbol tables.
@@ -283,6 +297,11 @@ struct solib_ops
(iterate_over_objfiles_in_search_order_cb_ftype cb,
objfile *current_objfile) const;
+ /* Open an in-memory shared library at ADDR of at most SIZE bytes.
+ The TARGET string is used to identify the target. */
+ virtual gdb_bfd_ref_ptr bfd_open_from_target_memory
+ (CORE_ADDR addr, CORE_ADDR size, const char *target) const;
+
protected:
/* The program space for which this solib_ops was created. */
program_space *m_pspace;
diff --git a/gdbserver/dll.cc b/gdbserver/dll.cc
index 9ed2f5c80fc..32d6dfb21a7 100644
--- a/gdbserver/dll.cc
+++ b/gdbserver/dll.cc
@@ -40,34 +40,52 @@ loaded_dll (process_info *proc, const char *name, CORE_ADDR base_addr)
proc->dlls_changed = true;
}
-/* Record that the DLL with NAME and BASE_ADDR has been unloaded
- from the current process. */
+/* Record a newly loaded in-memory DLL at BASE_ADDR for PROC. */
void
-unloaded_dll (const char *name, CORE_ADDR base_addr)
+loaded_dll (process_info *proc, CORE_ADDR begin, CORE_ADDR end,
+ CORE_ADDR base_addr)
{
- unloaded_dll (current_process (), name, base_addr);
+ gdb_assert (proc != nullptr);
+
+ /* We do not support overlapping in-memory libraries. */
+ 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)
+ {
+ /* DLL precedes the new library; note that end is exclusive. */
+ if (dll.end <= begin)
+ return false;
+ /* DLL succeeds the new library; note that end is exclusive. */
+ if (end <= dll.begin)
+ return false;
+ /* DLL overlaps with the new library. */
+ return true;
+ });
+
+ if (it != dlls.end ())
+ error (_("In-memory library [%s;%s) overlaps with [%s;%s)."),
+ paddress (begin), paddress (end), paddress (it->begin),
+ paddress (it->end));
+
+ proc->all_dlls.emplace_back (begin, end, base_addr);
+ proc->dlls_changed = true;
}
/* Record that the DLL with NAME and BASE_ADDR has been unloaded
- from PROC. */
+ from the current process. */
void
-unloaded_dll (process_info *proc, const char *name, CORE_ADDR base_addr)
+unloaded_dll (const char *name, CORE_ADDR base_addr)
{
- gdb_assert (proc != nullptr);
- auto pred = [&] (const dll_info &dll)
- {
- if (base_addr != UNSPECIFIED_CORE_ADDR
- && base_addr == dll.base_addr)
- return true;
-
- if (name != NULL && dll.name == name)
- return true;
-
- return false;
- };
+ unloaded_dll (current_process (), name, base_addr);
+}
+static void
+unload_dll_if (process_info *proc,
+ std::function<bool (const dll_info &)> pred)
+{
auto iter = std::find_if (proc->all_dlls.begin (), proc->all_dlls.end (),
pred);
@@ -89,3 +107,50 @@ unloaded_dll (process_info *proc, const char *name, CORE_ADDR base_addr)
proc->dlls_changed = true;
}
}
+
+/* Record that the DLL with NAME and BASE_ADDR has been unloaded
+ from PROC. */
+
+void
+unloaded_dll (process_info *proc, const char *name, CORE_ADDR base_addr)
+{
+ unload_dll_if (proc, [&] (const dll_info &dll)
+ {
+ if (dll.location != dll_info::on_disk)
+ return false;
+
+ if (base_addr != UNSPECIFIED_CORE_ADDR
+ && base_addr == dll.base_addr)
+ return true;
+
+ if (name != NULL && dll.name == name)
+ return true;
+
+ return false;
+ });
+}
+
+/* Record that the in-memory DLL from BEGIN to END loaded at BASE_ADDR has been
+ unloaded. */
+
+void
+unloaded_dll (process_info *proc, CORE_ADDR begin, CORE_ADDR end,
+ CORE_ADDR base_addr)
+{
+ unload_dll_if (proc, [&] (const dll_info &dll)
+ {
+ if (dll.location != dll_info::in_memory)
+ return false;
+
+ if (base_addr != UNSPECIFIED_CORE_ADDR && base_addr == dll.base_addr)
+ return true;
+
+ /* We do not require the end address to be specified - we don't
+ support partially unloaded libraries, anyway. */
+ if ((begin == dll.begin)
+ && (end == UNSPECIFIED_CORE_ADDR || end == dll.end))
+ return true;
+
+ return false;
+ });
+}
diff --git a/gdbserver/dll.h b/gdbserver/dll.h
index 624e142f557..ab8ffdc1d8c 100644
--- a/gdbserver/dll.h
+++ b/gdbserver/dll.h
@@ -24,19 +24,47 @@ struct process_info;
struct dll_info
{
+ enum location_t
+ {
+ on_disk,
+ in_memory
+ };
+
dll_info (const std::string &name_, CORE_ADDR base_addr_)
- : name (name_), base_addr (base_addr_)
+ : location (on_disk), name (name_), base_addr (base_addr_)
+ {}
+
+ dll_info (CORE_ADDR begin_, CORE_ADDR end_, CORE_ADDR base_addr_)
+ : location (in_memory), begin (begin_), end (end_), base_addr (base_addr_)
{}
+ /* Where the library bits are stored. */
+ location_t location;
+
+ /* The name of a file on disk containing the library.
+
+ This is only valid if LOCATION == ON_DISK. */
std::string name;
+
+ /* The address range in memory containing the library.
+
+ This is only valid if LOCATION == IN_MEMORY. */
+ CORE_ADDR begin;
+ CORE_ADDR end;
+
+ /* The base address at which the library is loaded. */
CORE_ADDR base_addr;
};
extern void loaded_dll (const char *name, CORE_ADDR base_addr);
extern void loaded_dll (process_info *proc, const char *name,
CORE_ADDR base_addr);
+extern void loaded_dll (process_info *proc, CORE_ADDR begin, CORE_ADDR end,
+ CORE_ADDR base_addr);
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);
#endif /* GDBSERVER_DLL_H */
diff --git a/gdbserver/server.cc b/gdbserver/server.cc
index 7ad93e5e303..67ef448780f 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -1901,6 +1901,29 @@ handle_qxfer_features (const char *annex,
return len;
}
+/* Print a qXfer:libraries:read entry for DLL. */
+
+static std::string
+print_qxfer_libraries_entry (const dll_info &dll)
+{
+ switch (dll.location)
+ {
+ case dll_info::in_memory:
+ return string_printf
+ (" <in-memory-library begin=\"0x%s\" end=\"0x%s\">"
+ "<segment address=\"0x%s\"/></in-memory-library>\n",
+ paddress (dll.begin), paddress (dll.end),
+ paddress (dll.base_addr));
+
+ case dll_info::on_disk:
+ return string_printf
+ (" <library name=\"%s\"><segment address=\"0x%s\"/></library>\n",
+ dll.name.c_str (), paddress (dll.base_addr));
+ }
+
+ gdb_assert_not_reached ("unknown dll location: %x", dll.location);
+}
+
/* Handle qXfer:libraries:read. */
static int
@@ -1919,9 +1942,7 @@ handle_qxfer_libraries (const char *annex,
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));
+ document += print_qxfer_libraries_entry (dll);
document += "</library-list>\n";
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.