mirror of
https://github.com/espressif/binutils-gdb.git
synced 2025-06-02 04:27:46 +08:00
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:
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
385
gdb/solib-svr4.c
385
gdb/solib-svr4.c
@ -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;
|
||||
|
@ -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;
|
||||
|
25
gdb/testsuite/gdb.base/dlmopen-lib.c
Normal file
25
gdb/testsuite/gdb.base/dlmopen-lib.c
Normal 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. */
|
||||
}
|
65
gdb/testsuite/gdb.base/dlmopen.c
Normal file
65
gdb/testsuite/gdb.base/dlmopen.c
Normal 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 */
|
||||
}
|
150
gdb/testsuite/gdb.base/dlmopen.exp
Normal file
150
gdb/testsuite/gdb.base/dlmopen.exp
Normal 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
|
||||
}
|
@ -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 {} {
|
||||
|
@ -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)
|
||||
|
Reference in New Issue
Block a user