gdb, gdbserver: support dlmopen()

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>
This commit is contained in:
Markus Metzger
2021-10-04 10:24:35 +02:00
parent 60d09f0a0d
commit 8d56636a0e
10 changed files with 793 additions and 186 deletions

View File

@ -2826,6 +2826,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;
@ -2854,6 +2855,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;

View File

@ -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;

View File

@ -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;

View File

@ -46,10 +46,12 @@
#include "gdb_bfd.h"
#include "probe.h"
#include <map>
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 registry<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.
@ -730,7 +765,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;
@ -738,7 +773,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)
@ -792,6 +827,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
@ -848,7 +912,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... */
@ -882,11 +946,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. */
@ -918,13 +990,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;
@ -1021,6 +1091,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.
@ -1068,13 +1144,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)
{
@ -1140,7 +1219,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);
@ -1231,17 +1310,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
@ -1257,49 +1337,114 @@ 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);
/* Replace the (empty) solib_lists in INFO with the one generated
from the target. We don't want to copy it on assignment and then
delete the original afterwards, so let's just swap the
internals. */
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
@ -1308,13 +1453,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;
/* Otherwise obtain the solib list directly from the inferior. */
return svr4_current_sos_direct (info);
/* 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);
/* 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. */
@ -1648,8 +1806,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;
}
@ -1660,29 +1817,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;
lm_info_svr4 *li = (lm_info_svr4 *) tail->lm_info;
prev_lm = li->lm_addr;
/* 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 *) solist->lm_info;
prev_lm = li->lm_addr;
pnext = &solist->next;
}
/* Read the new objects. */
if (info->using_xfer)
@ -1696,17 +1875,38 @@ 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;
/* Get the so list from the target. We replace the list in the
target response so we can easily check that the response only
covers one namespace.
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;
}
@ -1724,7 +1924,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
@ -1798,8 +1998,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
@ -1823,13 +2031,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
@ -1855,7 +2056,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;
}
@ -2090,7 +2291,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)
@ -2884,7 +3085,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 ();
@ -3033,6 +3234,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;
@ -3064,6 +3266,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;

View File

@ -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;

View File

@ -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. */
}

View File

@ -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 */
}

View File

@ -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
}

View File

@ -2512,6 +2512,102 @@ proc skip_shlib_tests {} {
return 1
}
# Return 1 if we should skip dlmopen tests, 0 if we should not.
gdb_caching_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 {} {

View File

@ -6443,6 +6443,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;
@ -6459,6 +6462,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
@ -6470,33 +6565,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)
@ -6507,8 +6577,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')
{
@ -6537,95 +6617,74 @@ linux_process_target::qxfer_libraries_svr4 (const char *annex,
annex = decode_address_to_semicolon (addrp, sep + 1);
}
if (lm_addr == 0)
{
int r_version = 0;
std::string document = "<library-list-svr4 version=\"1.0\"";
if (priv->r_debug == 0)
priv->r_debug = get_r_debug (pid, is_elf64);
/* 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
{
if (lm_prev != 0)
warning ("ignoring prev=0x%s without start", paddress (lm_prev));
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)