[PATCH v5 03/15] gdb, gdbserver: support dlmopen()

Markus Metzger via Gdb-patches
2022-06-02T14:31:18Z

In glibc, the r_debug structure contains (amongst others) the following
fields:

int r_version:
Version number for this protocol. It should be greater than 0.

If r_version is 2, struct r_debug is extended to struct r_debug_extended
with one additional field:

struct r_debug_extended *r_next;
Link to the next r_debug_extended structure. Each r_debug_extended
structure represents a different namespace. The first r_debug_extended
structure is for the default namespace.

1. Change solib_svr4_r_map argument to take the debug base.
2. Add solib_svr4_r_next to find the link map in the next namespace from
the r_next field.
3. Update svr4_current_sos_direct to get the link map in the next namespace
from the r_next field.
4. Don't check shared libraries in other namespaces when updating shared
libraries in a new namespace.
5. Update svr4_same to check the load offset in addition to the name
6. Update svr4_default_sos to also set l_addr_inferior
7. Change the flat solib_list into a per-namespace list using the
namespace's r_debug address to identify the namespace.

Add gdb.base/dlmopen.exp to test this.

To remain backwards compatible with older gdbserver, we reserve the
namespace zero for a flat list of solibs from all namespaces. Subsequent
patches will extend RSP to allow listing libraries grouped by namespace.

This fixes PR 11839.

Co-authored-by: Lu, Hongjiu <hongjiu.lu@intel.com>
---
gdb/linux-tdep.c | 2 +
gdb/mips-fbsd-tdep.c | 2 +
gdb/mips-netbsd-tdep.c | 2 +
gdb/solib-svr4.c | 377 ++++++++++++++++++++-------
gdb/solib-svr4.h | 3 +
gdb/testsuite/gdb.base/dlmopen-lib.c | 25 ++
gdb/testsuite/gdb.base/dlmopen.c | 65 +++++
gdb/testsuite/gdb.base/dlmopen.exp | 150 +++++++++++
gdb/testsuite/lib/gdb.exp | 96 +++++++
gdbserver/linux-low.cc | 247 +++++++++++-------
10 files changed, 784 insertions(+), 185 deletions(-)
create mode 100644 gdb/testsuite/gdb.base/dlmopen-lib.c
create mode 100644 gdb/testsuite/gdb.base/dlmopen.c
create mode 100644 gdb/testsuite/gdb.base/dlmopen.exp

diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c
index 4e728a06e7e..a0e319f9395 100644
--- a/gdb/linux-tdep.c
+++ b/gdb/linux-tdep.c
@@ -2775,6 +2775,7 @@ linux_ilp32_fetch_link_map_offsets ()
lmo.r_map_offset = 4;
lmo.r_brk_offset = 8;
lmo.r_ldsomap_offset = -1;
+ lmo.r_next_offset = 20;

/* Everything we need is in the first 20 bytes. */
lmo.link_map_size = 20;
@@ -2803,6 +2804,7 @@ linux_lp64_fetch_link_map_offsets ()
lmo.r_map_offset = 8;
lmo.r_brk_offset = 16;
lmo.r_ldsomap_offset = -1;
+ lmo.r_next_offset = 40;

/* Everything we need is in the first 40 bytes. */
lmo.link_map_size = 40;
diff --git a/gdb/mips-fbsd-tdep.c b/gdb/mips-fbsd-tdep.c
index 05043d93e74..ecee4cdde50 100644
--- a/gdb/mips-fbsd-tdep.c
+++ b/gdb/mips-fbsd-tdep.c
@@ -495,6 +495,7 @@ mips_fbsd_ilp32_fetch_link_map_offsets (void)
lmo.r_map_offset = 4;
lmo.r_brk_offset = 8;
lmo.r_ldsomap_offset = -1;
+ lmo.r_next_offset = -1;

lmo.link_map_size = 24;
lmo.l_addr_offset = 0;
@@ -522,6 +523,7 @@ mips_fbsd_lp64_fetch_link_map_offsets (void)
lmo.r_map_offset = 8;
lmo.r_brk_offset = 16;
lmo.r_ldsomap_offset = -1;
+ lmo.r_next_offset = -1;

lmo.link_map_size = 48;
lmo.l_addr_offset = 0;
diff --git a/gdb/mips-netbsd-tdep.c b/gdb/mips-netbsd-tdep.c
index 2179ca0bf5a..b7a68ffe81b 100644
--- a/gdb/mips-netbsd-tdep.c
+++ b/gdb/mips-netbsd-tdep.c
@@ -308,6 +308,7 @@ mipsnbsd_ilp32_fetch_link_map_offsets (void)
lmo.r_map_offset = 4;
lmo.r_brk_offset = 8;
lmo.r_ldsomap_offset = -1;
+ lmo.r_next_offset = -1;

/* Everything we need is in the first 24 bytes. */
lmo.link_map_size = 24;
@@ -336,6 +337,7 @@ mipsnbsd_lp64_fetch_link_map_offsets (void)
lmo.r_map_offset = 8;
lmo.r_brk_offset = 16;
lmo.r_ldsomap_offset = -1;
+ lmo.r_next_offset = -1;

/* Everything we need is in the first 40 bytes. */
lmo.link_map_size = 48;
diff --git a/gdb/solib-svr4.c b/gdb/solib-svr4.c
index 6222f79a7a0..1f2abcf16ef 100644
--- a/gdb/solib-svr4.c
+++ b/gdb/solib-svr4.c
@@ -46,10 +46,12 @@
#include "gdb_bfd.h"
#include "probe.h"

+#include
+
static struct link_map_offsets *svr4_fetch_link_map_offsets (void);
static int svr4_have_link_map_offsets (void);
static void svr4_relocate_main_executable (void);
-static void svr4_free_library_list (void *p_list);
+static void svr4_free_library_list (so_list *solist);
static void probes_table_remove_objfile_probes (struct objfile *objfile);
static void svr4_iterate_over_objfiles_in_search_order
(gdbarch *gdbarch, iterate_over_objfiles_in_search_order_cb_ftype cb,
@@ -174,7 +176,16 @@ svr4_same_1 (const char *gdb_so_name, const char *inferior_so_name)
static int
svr4_same (struct so_list *gdb, struct so_list *inferior)
{
- return (svr4_same_1 (gdb->so_original_name, inferior->so_original_name));
+ if (!svr4_same_1 (gdb->so_original_name, inferior->so_original_name))
+ return false;
+
+ /* There may be different instances of the same library, in different
+ namespaces. Each instance, however, must have been loaded at a
+ different address so its relocation offset would be different. */
+ const lm_info_svr4 *lmg = (const lm_info_svr4 *) gdb->lm_info;
+ const lm_info_svr4 *lmi = (const lm_info_svr4 *) inferior->lm_info;
+
+ return (lmg->l_addr_inferior == lmi->l_addr_inferior);
}

static std::unique_ptr<lm_info_svr4>
@@ -329,7 +340,7 @@ struct svr4_info
svr4_info () = default;
~svr4_info ();

- /* Base of dynamic linker structures. */
+ /* Base of dynamic linker structures in default namespace. */
CORE_ADDR debug_base = 0;

/* Validity flag for debug_loader_offset. */
@@ -341,7 +352,7 @@ struct svr4_info
/* Name of the dynamic linker, valid if debug_loader_offset_p. */
char *debug_loader_name = nullptr;

- /* Load map address for the main executable. */
+ /* Load map address for the main executable in default namespace. */
CORE_ADDR main_lm_addr = 0;

CORE_ADDR interp_text_sect_low = 0;
@@ -349,9 +360,9 @@ struct svr4_info
CORE_ADDR interp_plt_sect_low = 0;
CORE_ADDR interp_plt_sect_high = 0;

- /* Nonzero if the list of objects was last obtained from the target
+ /* True if the list of objects was last obtained from the target
via qXfer:libraries-svr4:read. */
- int using_xfer = 0;
+ bool using_xfer = false;

/* Table of struct probe_and_action instances, used by the
probes-based interface to map breakpoint addresses to probes
@@ -359,14 +370,35 @@ struct svr4_info
probe_and_action->prob->address. */
htab_up probes_table;

- /* List of objects loaded into the inferior, used by the probes-
- based interface. */
- struct so_list *solib_list = nullptr;
+ /* List of objects loaded into the inferior per namespace, used by the
+ probes-based interface.
+
+ The namespace is represented by the address of its corresponding
+ r_debug[_ext] object. We get the namespace id as agrument to the
+ 'reloc_complete' probe but we don't get it when scanning the load map
+ on attach.
+
+ The r_debug[_ext] objects may move when ld.so itself moves. In that
+ case, we expect also the global _r_debug to move so we can detect
+ this and reload everything. The r_debug[_ext] objects are not
+ expected to move individually.
+
+ The special entry zero is reserved for a linear list to support
+ gdbstubs that do not support namespaces. */
+ std::map<CORE_ADDR, so_list *> solib_lists;
};

/* Per-program-space data key. */
static const struct program_space_key<svr4_info> solib_svr4_pspace_data;

+/* Return whether DEBUG_BASE is the default namespace of INFO. */
+
+static bool
+svr4_is_default_namespace (const svr4_info *info, CORE_ADDR debug_base)
+{
+ return (debug_base == info->debug_base);
+}
+
/* Free the probes table. */

static void
@@ -375,18 +407,21 @@ free_probes_table (struct svr4_info *info)
info->probes_table.reset (nullptr);
}

-/* Free the solib list. */
+/* Free the solib lists for all namespaces. */

static void
-free_solib_list (struct svr4_info *info)
+free_solib_lists (svr4_info *info)
{
- svr4_free_library_list (&info->solib_list);
- info->solib_list = NULL;
+ for (const std::pair<CORE_ADDR, so_list *> tuple
+ : info->solib_lists)
+ svr4_free_library_list (tuple.second);
+
+ info->solib_lists.clear ();
}

svr4_info::~svr4_info ()
{
- free_solib_list (this);
+ free_solib_lists (this);
}

/* Get the svr4 data for program space PSPACE. If none is found yet, add it now.
@@ -731,7 +766,7 @@ elf_locate_base (void)
RT_CONSISTENT. */

static CORE_ADDR
-solib_svr4_r_map (struct svr4_info *info)
+solib_svr4_r_map (CORE_ADDR debug_base)
{
struct link_map_offsets *lmo = svr4_fetch_link_map_offsets ();
struct type *ptr_type = builtin_type (target_gdbarch ())->builtin_data_ptr;
@@ -739,7 +774,7 @@ solib_svr4_r_map (struct svr4_info *info)

try
{
- addr = read_memory_typed_address (info->debug_base + lmo->r_map_offset,
+ addr = read_memory_typed_address (debug_base + lmo->r_map_offset,
ptr_type);
}
catch (const gdb_exception_error &ex)
@@ -793,6 +828,35 @@ solib_svr4_r_ldsomap (struct svr4_info *info)
ptr_type);
}

+/* Find the next namespace from the r_next field. */
+
+static CORE_ADDR
+solib_svr4_r_next (CORE_ADDR debug_base)
+{
+ link_map_offsets *lmo = svr4_fetch_link_map_offsets ();
+ type *ptr_type = builtin_type (target_gdbarch ())->builtin_data_ptr;
+ bfd_endian byte_order = type_byte_order (ptr_type);
+ ULONGEST version = 0;
+
+ try
+ {
+ version
+ = read_memory_unsigned_integer (debug_base + lmo->r_version_offset,
+ lmo->r_version_size, byte_order);
+ }
+ catch (const gdb_exception_error &ex)
+ {
+ exception_print (gdb_stderr, ex);
+ }
+
+ /* The r_next field is added with r_version == 2. */
+ if (version < 2 || lmo->r_next_offset == -1)
+ return 0;
+
+ return read_memory_typed_address (debug_base + lmo->r_next_offset,
+ ptr_type);
+}
+
/* On Solaris systems with some versions of the dynamic linker,
ld.so's l_name pointer points to the SONAME in the string table
rather than into writable memory. So that GDB can find shared
@@ -849,7 +913,7 @@ open_symbol_file_object (int from_tty)
return 0; /* failed somehow... */

/* First link map member should be the executable. */
- lm = solib_svr4_r_map (info);
+ lm = solib_svr4_r_map (info->debug_base);
if (lm == 0)
return 0; /* failed somehow... */

@@ -883,11 +947,19 @@ open_symbol_file_object (int from_tty)

struct svr4_library_list
{
- struct so_list *head, **tailp;
+ /* The tail pointer of the current namespace. This is internal to XML
+ parsing. */
+ so_list **tailp;

/* Inferior address of struct link_map used for the main executable. It is
NULL if not known. */
CORE_ADDR main_lm;
+
+ /* List of objects loaded into the inferior per namespace. This does
+ not include any default sos.
+
+ See comment on struct svr4_info.solib_lists. */
+ std::map<CORE_ADDR, so_list *> solib_lists;
};

/* This module's 'free_objfile' observer. */
@@ -919,13 +991,11 @@ svr4_clear_so (struct so_list *so)
li->l_addr_p = 0;
}

-/* Free so_list built so far (called via cleanup). */
+/* Free so_list built so far. */

static void
-svr4_free_library_list (void *p_list)
+svr4_free_library_list (so_list *list)
{
- struct so_list *list = *(struct so_list **) p_list;
-
while (list != NULL)
{
struct so_list *next = list->next;
@@ -1022,6 +1092,12 @@ svr4_library_list_start_list (struct gdb_xml_parser *parser,

if (main_lm)
list->main_lm = *(ULONGEST *) main_lm->value.get ();
+
+ /* Older gdbserver do not support namespaces. We use the special
+ namespace zero for a linear list of libraries. */
+ so_list **solist = &list->solib_lists[0];
+ *solist = nullptr;
+ list->tailp = solist;
}

/* The allowed elements and attributes for an XML library list.
@@ -1069,13 +1145,16 @@ static const struct gdb_xml_element svr4_library_list_elements[] =
static int
svr4_parse_libraries (const char *document, struct svr4_library_list *list)
{
- auto cleanup = make_scope_exit ([&] ()
+ auto cleanup = make_scope_exit ([list] ()
{
- svr4_free_library_list (&list->head);
+ for (const std::pair<CORE_ADDR, so_list *> tuple
+ : list->solib_lists)
+ svr4_free_library_list (tuple.second);
});

- memset (list, 0, sizeof (*list));
- list->tailp = &list->head;
+ list->tailp = nullptr;
+ list->main_lm = 0;
+ list->solib_lists.clear ();
if (gdb_xml_parse_quick (_("target library list"), "library-list-svr4.dtd",
svr4_library_list_elements, document, list) == 0)
{
@@ -1141,7 +1220,7 @@ svr4_default_sos (svr4_info *info)
newobj->lm_info = li;

/* Nothing will ever check the other fields if we set l_addr_p. */
- li->l_addr = info->debug_loader_offset;
+ li->l_addr = li->l_addr_inferior = info->debug_loader_offset;
li->l_addr_p = 1;

strncpy (newobj->so_name, info->debug_loader_name, SO_NAME_MAX_PATH_SIZE - 1);
@@ -1232,17 +1311,18 @@ svr4_read_so_list (svr4_info *info, CORE_ADDR lm, CORE_ADDR prev_lm,
/* Read the full list of currently loaded shared objects directly
from the inferior, without referring to any libraries read and
stored by the probes interface. Handle special cases relating
- to the first elements of the list. */
+ to the first elements of the list in default namespace. */

-static struct so_list *
+static void
svr4_current_sos_direct (struct svr4_info *info)
{
CORE_ADDR lm;
- struct so_list *head = NULL;
- struct so_list **link_ptr = &head;
- int ignore_first;
+ bool ignore_first;
struct svr4_library_list library_list;

+ /* Remove any old libraries. We're going to read them back in again. */
+ free_solib_lists (info);
+
/* Fall back to manual examination of the target if the packet is not
supported or gdbserver failed to find DT_DEBUG. gdb.server/solib-list.exp
tests a case where gdbserver cannot find the shared libraries list while
@@ -1258,49 +1338,110 @@ svr4_current_sos_direct (struct svr4_info *info)
if (library_list.main_lm)
info->main_lm_addr = library_list.main_lm;

- return library_list.head ? library_list.head : svr4_default_sos (info);
+ /* Remove an empty special zero namespace so we know that when there
+ is one, it is actually used, and we have a flat list without
+ namespace information. */
+ if ((library_list.solib_lists.find (0)
+ != library_list.solib_lists.end ())
+ && (library_list.solib_lists[0] == nullptr))
+ library_list.solib_lists.erase (0);
+
+ std::swap (info->solib_lists, library_list.solib_lists);
+ return;
}

/* If we can't find the dynamic linker's base structure, this
must not be a dynamically linked executable. Hmm. */
info->debug_base = elf_locate_base ();
if (info->debug_base == 0)
- return svr4_default_sos (info);
+ return;

/* Assume that everything is a library if the dynamic loader was loaded
late by a static executable. */
if (current_program_space->exec_bfd ()
&& bfd_get_section_by_name (current_program_space->exec_bfd (),
".dynamic") == NULL)
- ignore_first = 0;
+ ignore_first = false;
else
- ignore_first = 1;
+ ignore_first = true;

- auto cleanup = make_scope_exit ([&] ()
+ auto cleanup = make_scope_exit ([info] ()
{
- svr4_free_library_list (&head);
+ free_solib_lists (info);
});

- /* Walk the inferior's link map list, and build our list of
- `struct so_list' nodes. */
- lm = solib_svr4_r_map (info);
- if (lm)
- svr4_read_so_list (info, lm, 0, &link_ptr, ignore_first);
+ /* Collect the sos in each namespace. */
+ CORE_ADDR debug_base = info->debug_base;
+ for (; debug_base != 0;
+ ignore_first = false, debug_base = solib_svr4_r_next (debug_base))
+ {
+ /* Walk the inferior's link map list, and build our so_list list. */
+ lm = solib_svr4_r_map (debug_base);
+ if (lm != 0)
+ {
+ so_list **sos = &info->solib_lists[debug_base];
+ *sos = nullptr;
+
+ svr4_read_so_list (info, lm, 0, &sos, ignore_first);
+ }
+ }

/* On Solaris, the dynamic linker is not in the normal list of
shared objects, so make sure we pick it up too. Having
symbol information for the dynamic linker is quite crucial
- for skipping dynamic linker resolver code. */
- lm = solib_svr4_r_ldsomap (info);
- if (lm)
- svr4_read_so_list (info, lm, 0, &link_ptr, 0);
+ for skipping dynamic linker resolver code.
+
+ Note that we interpret the ldsomap load map address as 'virtual'
+ r_debug object. If we added it to the default namespace (as it was),
+ we would probably run into inconsistencies with the load map's
+ prev/next links (I wonder if we did). */
+ debug_base = solib_svr4_r_ldsomap (info);
+ if (debug_base != 0)
+ {
+ /* Add the dynamic linker's namespace unless we already did. */
+ if (info->solib_lists.find (debug_base) == info->solib_lists.end ())
+ {
+ so_list **sos = &info->solib_lists[debug_base];
+ *sos = nullptr;
+ svr4_read_so_list (info, debug_base, 0, &sos, 0);
+ }
+ }

cleanup.release ();
+}

- if (head == NULL)
- return svr4_default_sos (info);
+/* Collect sos read and stored by the probes interface. */

- return head;
+static so_list *
+svr4_collect_probes_sos (svr4_info *info)
+{
+ so_list *sos = nullptr;
+ so_list **pnext = &sos;
+
+ for (const std::pair<CORE_ADDR, so_list *> tuple
+ : info->solib_lists)
+ {
+ so_list *solist = tuple.second;
+
+ /* Allow the linker to report empty namespaces. */
+ if (solist == nullptr)
+ continue;
+
+ *pnext = svr4_copy_library_list (solist);
+
+ /* Update PNEXT to point to the next member of the last element. */
+ gdb_assert (*pnext != nullptr);
+ for (;;)
+ {
+ so_list *next = *pnext;
+ if (next == nullptr)
+ break;
+
+ pnext = &next->next;
+ }
+ }
+
+ return sos;
}

/* Implement the main part of the "current_sos" target_so_ops
@@ -1309,13 +1450,26 @@ svr4_current_sos_direct (struct svr4_info *info)
static struct so_list *
svr4_current_sos_1 (svr4_info *info)
{
- /* If the solib list has been read and stored by the probes
- interface then we return a copy of the stored list. */
- if (info->solib_list != NULL)
- return svr4_copy_library_list (info->solib_list);
+ so_list *sos = nullptr;
+
+ /* If we're using the probes interface, we can use the cache as it will
+ be maintained by probe update/reload actions. */
+ if (info->probes_table != nullptr)
+ sos = svr4_collect_probes_sos (info);

- /* Otherwise obtain the solib list directly from the inferior. */
- return svr4_current_sos_direct (info);
+ /* If we're not using the probes interface or if we didn't cache
+ anything, read the sos to fill the cache, then collect them from the
+ cache. */
+ if (sos == nullptr)
+ {
+ svr4_current_sos_direct (info);
+
+ sos = svr4_collect_probes_sos (info);
+ if (sos == nullptr)
+ sos = svr4_default_sos (info);
+ }
+
+ return sos;
}

/* Implement the "current_sos" target_so_ops method. */
@@ -1649,8 +1803,7 @@ solib_event_probe_action (struct probe_and_action *pa)
static int
solist_update_full (struct svr4_info *info)
{
- free_solib_list (info);
- info->solib_list = svr4_current_sos_direct (info);
+ svr4_current_sos_direct (info);

return 1;
}
@@ -1661,29 +1814,51 @@ solist_update_full (struct svr4_info *info)
failure. */

static int
-solist_update_incremental (struct svr4_info *info, CORE_ADDR lm)
+solist_update_incremental (svr4_info *info, CORE_ADDR debug_base,
+ CORE_ADDR lm)
{
- struct so_list *tail;
- CORE_ADDR prev_lm;
-
- /* svr4_current_sos_direct contains logic to handle a number of
- special cases relating to the first elements of the list. To
- avoid duplicating this logic we defer to solist_update_full
- if the list is empty. */
- if (info->solib_list == NULL)
- return 0;
-
/* Fall back to a full update if we are using a remote target
that does not support incremental transfers. */
if (info->using_xfer && !target_augmented_libraries_svr4_read ())
return 0;

- /* Walk to the end of the list. */
- for (tail = info->solib_list; tail->next != NULL; tail = tail->next)
- /* Nothing. */;
+ /* Fall back to a full update if we used the special namespace zero. We
+ wouldn't be able to find the last item in the DEBUG_BASE namespace
+ and hence get the prev link wrong. */
+ if (info->solib_lists.find (0) != info->solib_lists.end ())
+ return 0;
+
+ /* Ensure that the element is actually initialized. */
+ if (info->solib_lists.find (debug_base) == info->solib_lists.end ())
+ info->solib_lists[debug_base] = nullptr;
+
+ so_list **psolist = &info->solib_lists[debug_base];
+ so_list **pnext = nullptr;
+ so_list *solist = *psolist;
+ CORE_ADDR prev_lm;
+
+ if (solist == nullptr)
+ {
+ /* svr4_current_sos_direct contains logic to handle a number of
+ special cases relating to the first elements of the list in
+ default namespace. To avoid duplicating this logic we defer to
+ solist_update_full in this case. */
+ if (svr4_is_default_namespace (info, debug_base))
+ return 0;
+
+ prev_lm = 0;
+ pnext = psolist;
+ }
+ else
+ {
+ /* Walk to the end of the list. */
+ for (; solist->next != nullptr; solist = solist->next)
+ /* Nothing. */;

- lm_info_svr4 *li = (lm_info_svr4 *) tail->lm_info;
- prev_lm = li->lm_addr;
+ lm_info_svr4 *li = (lm_info_svr4 *) solist->lm_info;
+ prev_lm = li->lm_addr;
+ pnext = &solist->next;
+ }

/* Read the new objects. */
if (info->using_xfer)
@@ -1697,17 +1872,34 @@ solist_update_incremental (struct svr4_info *info, CORE_ADDR lm)
if (!svr4_current_sos_via_xfer_libraries (&library_list, annex))
return 0;

- tail->next = library_list.head;
+ /* We expect gdbserver to provide updates for the namespace that
+ contains LM, which whould be this namespace... */
+ so_list *sos = nullptr;
+ if (library_list.solib_lists.find (debug_base)
+ != library_list.solib_lists.end ())
+ std::swap (sos, library_list.solib_lists[debug_base]);
+ if (sos == nullptr)
+ {
+ /* ...or for the special zero namespace for earlier versions... */
+ if (library_list.solib_lists.find (0)
+ != library_list.solib_lists.end ())
+ std::swap (sos, library_list.solib_lists[0]);
+ }
+
+ /* ...but nothing else. */
+ for (const std::pair<CORE_ADDR, so_list *> tuple
+ : library_list.solib_lists)
+ gdb_assert (tuple.second == nullptr);
+
+ *pnext = sos;
}
else
{
- struct so_list **link = &tail->next;
-
/* IGNORE_FIRST may safely be set to zero here because the
above check and deferral to solist_update_full ensures
that this call to svr4_read_so_list will never see the
first element. */
- if (!svr4_read_so_list (info, lm, prev_lm, &link, 0))
+ if (!svr4_read_so_list (info, lm, prev_lm, &pnext, 0))
return 0;
}

@@ -1725,7 +1917,7 @@ disable_probes_interface (svr4_info *info)
"Reverting to original interface."));

free_probes_table (info);
- free_solib_list (info);
+ free_solib_lists (info);
}

/* Update the solib list as appropriate when using the
@@ -1799,8 +1991,16 @@ svr4_handle_solib_event (void)
if (debug_base == 0)
return;

- /* Always locate the debug struct, in case it moved. */
- info->debug_base = elf_locate_base ();
+ /* If the global _r_debug object moved, we need to reload everything
+ since we cannot identify namespaces (by the location of their
+ r_debug_ext object) anymore. */
+ CORE_ADDR global_debug_base = elf_locate_base ();
+ if (global_debug_base != info->debug_base)
+ {
+ info->debug_base = global_debug_base;
+ action = FULL_RELOAD;
+ }
+
if (info->debug_base == 0)
{
/* It's possible for the reloc_complete probe to be triggered before
@@ -1824,13 +2024,6 @@ svr4_handle_solib_event (void)
return;
}

- /* GDB does not currently support libraries loaded via dlmopen
- into namespaces other than the initial one. We must ignore
- any namespace other than the initial namespace here until
- support for this is added to GDB. */
- if (debug_base != info->debug_base)
- action = DO_NOTHING;
-
if (action == UPDATE_OR_RELOAD)
{
try
@@ -1856,7 +2049,7 @@ svr4_handle_solib_event (void)

if (action == UPDATE_OR_RELOAD)
{
- if (!solist_update_incremental (info, lm))
+ if (!solist_update_incremental (info, debug_base, lm))
action = FULL_RELOAD;
}

@@ -2091,7 +2284,7 @@ enable_break (struct svr4_info *info, int from_tty)

solib_add (NULL, from_tty, auto_solib_add);
sym_addr = 0;
- if (info->debug_base && solib_svr4_r_map (info) != 0)
+ if (info->debug_base && solib_svr4_r_map (info->debug_base) != 0)
sym_addr = solib_svr4_r_brk (info);

if (sym_addr != 0)
@@ -2887,7 +3080,7 @@ svr4_solib_create_inferior_hook (int from_tty)

/* Clear the probes-based interface's state. */
free_probes_table (info);
- free_solib_list (info);
+ free_solib_lists (info);

/* Relocate the main executable if necessary. */
svr4_relocate_main_executable ();
@@ -3042,6 +3235,7 @@ svr4_ilp32_fetch_link_map_offsets (void)
lmo.r_map_offset = 4;
lmo.r_brk_offset = 8;
lmo.r_ldsomap_offset = 20;
+ lmo.r_next_offset = -1;

/* Everything we need is in the first 20 bytes. */
lmo.link_map_size = 20;
@@ -3073,6 +3267,7 @@ svr4_lp64_fetch_link_map_offsets (void)
lmo.r_map_offset = 8;
lmo.r_brk_offset = 16;
lmo.r_ldsomap_offset = 40;
+ lmo.r_next_offset = -1;

/* Everything we need is in the first 40 bytes. */
lmo.link_map_size = 40;
diff --git a/gdb/solib-svr4.h b/gdb/solib-svr4.h
index 8b4968509f2..c7ea156989b 100644
--- a/gdb/solib-svr4.h
+++ b/gdb/solib-svr4.h
@@ -66,6 +66,9 @@ struct link_map_offsets
/* Offset of r_debug.r_ldsomap. */
int r_ldsomap_offset;

+ /* Offset of r_debug_extended.r_next. */
+ int r_next_offset;
+
/* Size of struct link_map (or equivalent), or at least enough of it
to be able to obtain the fields below. */
int link_map_size;
diff --git a/gdb/testsuite/gdb.base/dlmopen-lib.c b/gdb/testsuite/gdb.base/dlmopen-lib.c
new file mode 100644
index 00000000000..616bf97b4f5
--- /dev/null
+++ b/gdb/testsuite/gdb.base/dlmopen-lib.c
@@ -0,0 +1,25 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2021-2022 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+__attribute__((visibility ("default")))
+int
+inc (int n)
+{
+ return n + 1; /* bp.inc. */
+}
diff --git a/gdb/testsuite/gdb.base/dlmopen.c b/gdb/testsuite/gdb.base/dlmopen.c
new file mode 100644
index 00000000000..2dc2f2e6d03
--- /dev/null
+++ b/gdb/testsuite/gdb.base/dlmopen.c
@@ -0,0 +1,65 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2021 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#define _GNU_SOURCE
+#include <dlfcn.h>
+#include <stddef.h>
+#include <assert.h>
+#include <unistd.h>
+
+volatile int wait_for_gdb = 1;
+
+int
+main (void)
+{
+ void *handle[4];
+ int (*fun) (int);
+ Lmid_t lmid;
+ int dl;
+
+ handle[0] = dlmopen (LM_ID_NEWLM, DSO1_NAME, RTLD_LAZY | RTLD_LOCAL);
+ assert (handle[0] != NULL);
+
+ dlinfo (handle[0], RTLD_DI_LMID, &lmid);
+
+ handle[1] = dlopen (DSO1_NAME, RTLD_LAZY | RTLD_LOCAL);
+ assert (handle[1] != NULL);
+
+ handle[2] = dlmopen (LM_ID_NEWLM, DSO1_NAME, RTLD_LAZY | RTLD_LOCAL);
+ assert (handle[2] != NULL);
+
+ handle[3] = dlmopen (lmid, DSO2_NAME, RTLD_LAZY | RTLD_LOCAL);
+ assert (handle[3] != NULL);
+
+ alarm (20);
+ while (wait_for_gdb != 0)
+ usleep (1);
+
+ for (dl = 0; dl < 4; ++dl)
+ {
+ fun = dlsym (handle[dl], "inc");
+ assert (fun != NULL);
+
+ fun (42);
+
+ dlclose (handle[dl]);
+ }
+
+ return 0; /* bp.main */
+}
diff --git a/gdb/testsuite/gdb.base/dlmopen.exp b/gdb/testsuite/gdb.base/dlmopen.exp
new file mode 100644
index 00000000000..8e86d5d5ccc
--- /dev/null
+++ b/gdb/testsuite/gdb.base/dlmopen.exp
@@ -0,0 +1,150 @@
+# This testcase is part of GDB, the GNU debugger.
+#
+# Copyright 2021-2022 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+#
+# Test shared libraries loaded into different namespaces with dlmopen().
+#
+# We test that GDB shows the correct number of instances of the libraries
+# the test loaded while unloading them one-by-one.
+
+if { [skip_dlmopen_tests] } {
+ unsupported "target does not support dlmopen debugging"
+ return -1
+}
+
+standard_testfile
+
+set basename_lib dlmopen-lib
+set srcfile_lib $srcdir/$subdir/$basename_lib.c
+set binfile_lib1 [standard_output_file $basename_lib.1.so]
+set binfile_lib2 [standard_output_file $basename_lib.2.so]
+
+if { [gdb_compile_shlib $srcfile_lib $binfile_lib1 {debug}] != "" } {
+ untested "failed to prepare shlib"
+ return -1
+}
+
+if { [gdb_compile_shlib $srcfile_lib $binfile_lib2 {debug}] != "" } {
+ untested "failed to prepare shlib"
+ return -1
+}
+
+if { [prepare_for_testing "failed to prepare" $testfile $srcfile \
+ [list additional_flags=-DDSO1_NAME=\"$binfile_lib1\" \
+ additional_flags=-DDSO2_NAME=\"$binfile_lib2\" \
+ libs=-ldl debug]] } {
+ return -1
+}
+
+if { ![runto_main] } {
+ return -1
+}
+
+# Check that 'info shared' show NUM occurrences of DSO.
+proc check_dso_count { dso num } {
+ global gdb_prompt hex
+
+ set count 0
+ gdb_test_multiple "info shared" "info shared" {
+ -re "$hex $hex Yes \[^\r\n\]*$dso\r\n" {
+ # use longer form so debug remote does not interfere
+ set count [expr $count + 1]
+ exp_continue
+ }
+ -re "$gdb_prompt " {
+ verbose -log "library: $dso, expected: $num, found: $count"
+ gdb_assert {$count == $num} "$gdb_test_name"
+ }
+ }
+}
+
+# The DSO part of the test. We run it once per DSO call.
+proc test_dlmopen_one { ndso1 ndso2 } {
+ global srcfile_lib srcfile_lib basename_lib bp_inc
+
+ # Try to reach the breakpoint in the dynamically loaded library.
+ gdb_continue_to_breakpoint "cont to bp.inc" \
+ ".*$srcfile_lib:$bp_inc\r\n.*"
+
+ # We opened all DSOs initially and close them one by one.
+ with_test_prefix "dso 1" { check_dso_count $basename_lib.1.so $ndso1 }
+ with_test_prefix "dso 2" { check_dso_count $basename_lib.2.so $ndso2 }
+
+ # This might help debugging.
+ gdb_test "info breakpoints" ".*"
+ gdb_test "print \$pc" ".*"
+}
+
+# The actual test. We run it twice.
+proc test_dlmopen {} {
+ global srcfile basename_lib bp_main
+
+ with_test_prefix "dlmopen 1" { test_dlmopen_one 3 1 }
+ with_test_prefix "dlmopen 2" { test_dlmopen_one 2 1 }
+ with_test_prefix "dlmopen 3" { test_dlmopen_one 1 1 }
+ with_test_prefix "dlmopen 4" { test_dlmopen_one 0 1 }
+
+ with_test_prefix "main" {
+ # Try to reach the breakpoint in the dynamically loaded library.
+ gdb_continue_to_breakpoint "cont to bp.main" \
+ ".*$srcfile:$bp_main\r\n.*"
+
+ # The library should not be listed.
+ with_test_prefix "dso 1" { check_dso_count $basename_lib.1.so 0 }
+ with_test_prefix "dso 2" { check_dso_count $basename_lib.2.so 0 }
+ }
+}
+
+# Remove the pause. We only need it for the attach test.
+gdb_test "print wait_for_gdb = 0" "\\\$1 = 0"
+
+# Break in the to-be-loaded library and at the end of main.
+set bp_inc [gdb_get_line_number "bp.inc" $srcfile_lib]
+set bp_main [gdb_get_line_number "bp.main" $srcfile]
+
+delete_breakpoints
+gdb_breakpoint $srcfile_lib:$bp_inc allow-pending
+gdb_breakpoint $srcfile:$bp_main
+
+test_dlmopen
+
+# Try the same again when attaching after dlmopen().
+if { ![can_spawn_for_attach] } {
+ unsupported "target does not support attach"
+ return -1
+}
+
+clean_restart $binfile
+
+# Start the test program.
+set test_spawn_id [spawn_wait_for_attach $binfile]
+set testpid [spawn_id_get_pid $test_spawn_id]
+
+# Attach.
+gdb_test "attach $testpid" "Attaching to program.*, process $testpid.*"
+
+with_test_prefix "attach" {
+ # Remove the pause. We no longer need it.
+ gdb_test "print wait_for_gdb = 0" "\\\$1 = 0"
+
+ # Set the same breakpoints again. This time, however, we do not allow the
+ # breakpoint to be pending since the library has already been loaded.
+ gdb_breakpoint $srcfile_lib:$bp_inc
+ gdb_breakpoint $srcfile:$bp_main
+
+ test_dlmopen
+}
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index 6aef39e5e02..98715fdc87e 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -2451,6 +2451,102 @@ proc skip_shlib_tests {} {
return 1
}

+# Return 1 if we should skip dlmopen tests, 0 if we should not.
+
+proc skip_dlmopen_tests {} {
+ global srcdir subdir gdb_prompt inferior_exited_re
+
+ # We need shared library support.
+ if { [skip_shlib_tests] } {
+ return 1
+ }
+
+ set me "skip_dlmopen_tests"
+ set lib {
+ int foo (void) {
+ return 42;
+ }
+ }
+ set src {
+ #define _GNU_SOURCE
+ #include <dlfcn.h>
+ #include <link.h>
+ #include <stdio.h>
+ #include <errno.h>
+
+ int main (void) {
+ struct r_debug *r_debug;
+ ElfW(Dyn) *dyn;
+ void *handle;
+
+ /* The version is kept at 1 until we create a new namespace. */
+ handle = dlmopen (LM_ID_NEWLM, DSO_NAME, RTLD_LAZY | RTLD_LOCAL);
+ if (!handle) {
+ printf ("dlmopen failed: %s.\n", dlerror ());
+ return 1;
+ }
+
+ r_debug = 0;
+ /* Taken from /usr/include/link.h. */
+ for (dyn = _DYNAMIC; dyn->d_tag != DT_NULL; ++dyn)
+ if (dyn->d_tag == DT_DEBUG)
+ r_debug = (struct r_debug *) dyn->d_un.d_ptr;
+
+ if (!r_debug) {
+ printf ("r_debug not found.\n");
+ return 1;
+ }
+ if (r_debug->r_version < 2) {
+ printf ("dlmopen debug not supported.\n");
+ return 1;
+ }
+ printf ("dlmopen debug supported.\n");
+ return 0;
+ }
+ }
+
+ set libsrc [standard_temp_file "libfoo.c"]
+ set libout [standard_temp_file "libfoo.so"]
+ gdb_produce_source $libsrc $lib
+
+ if { [gdb_compile_shlib $libsrc $libout {debug}] != "" } {
+ verbose -log "failed to build library"
+ return 1
+ }
+ if { ![gdb_simple_compile $me $src executable \
+ [list shlib_load debug \
+ additional_flags=-DDSO_NAME=\"$libout\"]] } {
+ verbose -log "failed to build executable"
+ return 1
+ }
+
+ gdb_exit
+ gdb_start
+ gdb_reinitialize_dir $srcdir/$subdir
+ gdb_load $obj
+
+ if { [gdb_run_cmd] != 0 } {
+ verbose -log "failed to start skip test"
+ return 1
+ }
+ gdb_expect {
+ -re "$inferior_exited_re normally.*${gdb_prompt} $" {
+ set skip_dlmopen_tests 0
+ }
+ -re "$inferior_exited_re with code.*${gdb_prompt} $" {
+ set skip_dlmopen_tests 1
+ }
+ default {
+ warning "\n$me: default case taken"
+ set skip_dlmopen_tests 1
+ }
+ }
+ gdb_exit
+
+ verbose "$me: returning $skip_dlmopen_tests" 2
+ return $skip_dlmopen_tests
+}
+
# Return 1 if we should skip tui related tests.

proc skip_tui_tests {} {
diff --git a/gdbserver/linux-low.cc b/gdbserver/linux-low.cc
index 8b8614f6ed4..d765139e816 100644
--- a/gdbserver/linux-low.cc
+++ b/gdbserver/linux-low.cc
@@ -6435,6 +6435,9 @@ struct link_map_offsets
/* Offset and size of r_debug.r_map. */
int r_map_offset;

+ /* Offset of r_debug_extended.r_next. */
+ int r_next_offset;
+
/* Offset to l_addr field in struct link_map. */
int l_addr_offset;

@@ -6451,6 +6454,98 @@ struct link_map_offsets
int l_prev_offset;
};

+static const link_map_offsets lmo_32bit_offsets =
+ {
+ 0, /* r_version offset. */
+ 4, /* r_debug.r_map offset. */
+ 20, /* r_debug_extended.r_next. */
+ 0, /* l_addr offset in link_map. */
+ 4, /* l_name offset in link_map. */
+ 8, /* l_ld offset in link_map. */
+ 12, /* l_next offset in link_map. */
+ 16 /* l_prev offset in link_map. */
+ };
+
+static const link_map_offsets lmo_64bit_offsets =
+ {
+ 0, /* r_version offset. */
+ 8, /* r_debug.r_map offset. */
+ 40, /* r_debug_extended.r_next. */
+ 0, /* l_addr offset in link_map. */
+ 8, /* l_name offset in link_map. */
+ 16, /* l_ld offset in link_map. */
+ 24, /* l_next offset in link_map. */
+ 32 /* l_prev offset in link_map. */
+ };
+
+/* Get the loaded shared libraries from one namespace. */
+
+static void
+read_link_map (std::string &document, CORE_ADDR lm_addr, CORE_ADDR lm_prev,
+ int ptr_size, const link_map_offsets *lmo, bool ignore_first,
+ int &header_done)
+{
+ CORE_ADDR l_name, l_addr, l_ld, l_next, l_prev;
+
+ while (lm_addr
+ && read_one_ptr (lm_addr + lmo->l_name_offset,
+ &l_name, ptr_size) == 0
+ && read_one_ptr (lm_addr + lmo->l_addr_offset,
+ &l_addr, ptr_size) == 0
+ && read_one_ptr (lm_addr + lmo->l_ld_offset,
+ &l_ld, ptr_size) == 0
+ && read_one_ptr (lm_addr + lmo->l_prev_offset,
+ &l_prev, ptr_size) == 0
+ && read_one_ptr (lm_addr + lmo->l_next_offset,
+ &l_next, ptr_size) == 0)
+ {
+ unsigned char libname[PATH_MAX];
+
+ if (lm_prev != l_prev)
+ {
+ warning ("Corrupted shared library list: 0x%s != 0x%s",
+ paddress (lm_prev), paddress (l_prev));
+ break;
+ }
+
+ /* Ignore the first entry even if it has valid name as the first entry
+ corresponds to the main executable. The first entry should not be
+ skipped if the dynamic loader was loaded late by a static executable
+ (see solib-svr4.c parameter ignore_first). But in such case the main
+ executable does not have PT_DYNAMIC present and this function already
+ exited above due to failed get_r_debug. */
+ if (ignore_first && lm_prev == 0)
+ string_appendf (document, " main-lm=\"0x%s\"", paddress (lm_addr));
+ else
+ {
+ /* Not checking for error because reading may stop before
+ we've got PATH_MAX worth of characters. */
+ libname[0] = '\0';
+ linux_read_memory (l_name, libname, sizeof (libname) - 1);
+ libname[sizeof (libname) - 1] = '\0';
+ if (libname[0] != '\0')
+ {
+ if (!header_done)
+ {
+ /* Terminate `<library-list-svr4'. */
+ document += '>';
+ header_done = 1;
+ }
+
+ string_appendf (document, "<library name=\"");
+ xml_escape_text_append (&document, (char *) libname);
+ string_appendf (document, "\" lm=\"0x%s\" l_addr=\"0x%s\" "
+ "l_ld=\"0x%s\"/>",
+ paddress (lm_addr), paddress (l_addr),
+ paddress (l_ld));
+ }
+ }
+
+ lm_prev = lm_addr;
+ lm_addr = l_next;
+ }
+}
+
/* Construct qXfer:libraries-svr4:read reply. */

int
@@ -6462,33 +6557,8 @@ linux_process_target::qxfer_libraries_svr4 (const char *annex,
struct process_info_private *const priv = current_process ()->priv;
char filename[PATH_MAX];
int pid, is_elf64;
-
- static const struct link_map_offsets lmo_32bit_offsets =
- {
- 0, /* r_version offset. */
- 4, /* r_debug.r_map offset. */
- 0, /* l_addr offset in link_map. */
- 4, /* l_name offset in link_map. */
- 8, /* l_ld offset in link_map. */
- 12, /* l_next offset in link_map. */
- 16 /* l_prev offset in link_map. */
- };
-
- static const struct link_map_offsets lmo_64bit_offsets =
- {
- 0, /* r_version offset. */
- 8, /* r_debug.r_map offset. */
- 0, /* l_addr offset in link_map. */
- 8, /* l_name offset in link_map. */
- 16, /* l_ld offset in link_map. */
- 24, /* l_next offset in link_map. */
- 32 /* l_prev offset in link_map. */
- };
- const struct link_map_offsets *lmo;
unsigned int machine;
- int ptr_size;
CORE_ADDR lm_addr = 0, lm_prev = 0;
- CORE_ADDR l_name, l_addr, l_ld, l_next, l_prev;
int header_done = 0;

if (writebuf != NULL)
@@ -6499,8 +6569,18 @@ linux_process_target::qxfer_libraries_svr4 (const char *annex,
pid = lwpid_of (current_thread);
xsnprintf (filename, sizeof filename, "/proc/%d/exe", pid);
is_elf64 = elf_64_file_p (filename, &machine);
- lmo = is_elf64 ? &lmo_64bit_offsets : &lmo_32bit_offsets;
- ptr_size = is_elf64 ? 8 : 4;
+ const link_map_offsets *lmo;
+ int ptr_size;
+ if (is_elf64)
+ {
+ lmo = &lmo_64bit_offsets;
+ ptr_size = 8;
+ }
+ else
+ {
+ lmo = &lmo_32bit_offsets;
+ ptr_size = 4;
+ }

while (annex[0] != '\0')
{
@@ -6529,95 +6609,74 @@ linux_process_target::qxfer_libraries_svr4 (const char *annex,
annex = decode_address_to_semicolon (addrp, sep + 1);
}

- if (lm_addr == 0)
+ std::string document = "<library-list-svr4 version=\"1.0\"";
+
+ /* When the starting LM_ADDR is passed in the annex, only traverse that
+ namespace.
+
+ Otherwise, start with R_DEBUG and traverse all namespaces we find. */
+ if (lm_addr != 0)
+ read_link_map (document, lm_addr, lm_prev, ptr_size, lmo, false,
+ header_done);
+ else
{
- int r_version = 0;
+ if (lm_prev != 0)
+ warning ("ignoring prev=0x%s without start", paddress (lm_prev));

- if (priv->r_debug == 0)
- priv->r_debug = get_r_debug (pid, is_elf64);
+ CORE_ADDR r_debug = priv->r_debug;
+ if (r_debug == 0)
+ r_debug = priv->r_debug = get_r_debug (pid, is_elf64);

/* We failed to find DT_DEBUG. Such situation will not change
for this inferior - do not retry it. Report it to GDB as
E01, see for the reasons at the GDB solib-svr4.c side. */
- if (priv->r_debug == (CORE_ADDR) -1)
+ if (r_debug == (CORE_ADDR) -1)
return -1;

- if (priv->r_debug != 0)
+ bool ignore_first = true;
+ while (r_debug != 0)
{
- if (linux_read_memory (priv->r_debug + lmo->r_version_offset,
+ int r_version = 0;
+ if (linux_read_memory (r_debug + lmo->r_version_offset,
(unsigned char *) &r_version,
- sizeof (r_version)) != 0
- || r_version < 1)
+ sizeof (r_version)) != 0)
+ {
+ warning ("unable to read r_version from 0x%s",
+ paddress (r_debug + lmo->r_version_offset));
+ break;
+ }
+
+ if (r_version < 1)
{
warning ("unexpected r_debug version %d", r_version);
+ break;
}
- else if (read_one_ptr (priv->r_debug + lmo->r_map_offset,
- &lm_addr, ptr_size) != 0)
+
+ if (read_one_ptr (r_debug + lmo->r_map_offset, &lm_addr,
+ ptr_size) != 0)
{
- warning ("unable to read r_map from 0x%lx",
- (long) priv->r_debug + lmo->r_map_offset);
+ warning ("unable to read r_map from 0x%s",
+ paddress (r_debug + lmo->r_map_offset));
+ break;
}
- }
- }

- std::string document = "<library-list-svr4 version=\"1.0\"";
+ read_link_map (document, lm_addr, 0, ptr_size, lmo,
+ ignore_first, header_done);

- while (lm_addr
- && read_one_ptr (lm_addr + lmo->l_name_offset,
- &l_name, ptr_size) == 0
- && read_one_ptr (lm_addr + lmo->l_addr_offset,
- &l_addr, ptr_size) == 0
- && read_one_ptr (lm_addr + lmo->l_ld_offset,
- &l_ld, ptr_size) == 0
- && read_one_ptr (lm_addr + lmo->l_prev_offset,
- &l_prev, ptr_size) == 0
- && read_one_ptr (lm_addr + lmo->l_next_offset,
- &l_next, ptr_size) == 0)
- {
- unsigned char libname[PATH_MAX];
+ if (r_version < 2)
+ break;

- if (lm_prev != l_prev)
- {
- warning ("Corrupted shared library list: 0x%lx != 0x%lx",
- (long) lm_prev, (long) l_prev);
- break;
- }
+ /* Only applies to the default namespace. */
+ ignore_first = false;

- /* Ignore the first entry even if it has valid name as the first entry
- corresponds to the main executable. The first entry should not be
- skipped if the dynamic loader was loaded late by a static executable
- (see solib-svr4.c parameter ignore_first). But in such case the main
- executable does not have PT_DYNAMIC present and this function already
- exited above due to failed get_r_debug. */
- if (lm_prev == 0)
- string_appendf (document, " main-lm=\"0x%lx\"", (unsigned long) lm_addr);
- else
- {
- /* Not checking for error because reading may stop before
- we've got PATH_MAX worth of characters. */
- libname[0] = '\0';
- linux_read_memory (l_name, libname, sizeof (libname) - 1);
- libname[sizeof (libname) - 1] = '\0';
- if (libname[0] != '\0')
+ if (read_one_ptr (r_debug + lmo->r_next_offset, &r_debug,
+ ptr_size) != 0)
{
- if (!header_done)
- {
- /* Terminate `<library-list-svr4'. */
- document += '>';
- header_done = 1;
- }
-
- string_appendf (document, "<library name=\"");
- xml_escape_text_append (&document, (char *) libname);
- string_appendf (document, "\" lm=\"0x%lx\" "
- "l_addr=\"0x%lx\" l_ld=\"0x%lx\"/>",
- (unsigned long) lm_addr, (unsigned long) l_addr,
- (unsigned long) l_ld);
+ warning ("unable to read r_next from 0x%s",
+ paddress (r_debug + lmo->r_next_offset));
+ break;
}
}
-
- lm_prev = lm_addr;
- lm_addr = l_next;
}

if (!header_done)
--
2.35.3