diff options
Diffstat (limited to 'gdb/solib-svr4.c')
-rw-r--r-- | gdb/solib-svr4.c | 352 |
1 files changed, 298 insertions, 54 deletions
diff --git a/gdb/solib-svr4.c b/gdb/solib-svr4.c index 83cb389..48aace1 100644 --- a/gdb/solib-svr4.c +++ b/gdb/solib-svr4.c @@ -35,7 +35,6 @@ #include "regcache.h" #include "observable.h" -#include "solist.h" #include "solib.h" #include "solib-svr4.h" @@ -49,7 +48,6 @@ #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 probes_table_remove_objfile_probes (struct objfile *objfile); static void svr4_iterate_over_objfiles_in_search_order @@ -194,8 +192,8 @@ svr4_same (const solib &gdb, const solib &inferior) auto *lmi = gdb::checked_static_cast<const lm_info_svr4 *> (inferior.lm_info.get ()); - return svr4_same (gdb.so_original_name.c_str (), - inferior.so_original_name.c_str (), *lmg, *lmi); + return svr4_same (gdb.original_name.c_str (), + inferior.original_name.c_str (), *lmg, *lmi); } static lm_info_svr4_up @@ -317,7 +315,7 @@ lm_addr_check (const solib &so, bfd *abfd) gdb_printf (_("Using PIC (Position Independent Code) " "prelink displacement %s for \"%s\".\n"), paddress (current_inferior ()->arch (), l_addr), - so.so_name.c_str ()); + so.name.c_str ()); } else { @@ -333,7 +331,7 @@ lm_addr_check (const solib &so, bfd *abfd) warning (_(".dynamic section for \"%s\" " "is not at the expected address " "(wrong library or version mismatch?)"), - so.so_name.c_str ()); + so.name.c_str ()); } } @@ -369,7 +367,7 @@ struct svr4_info CORE_ADDR debug_loader_offset = 0; /* Name of the dynamic linker, valid if debug_loader_offset_p. */ - char *debug_loader_name = nullptr; + std::string debug_loader_name; /* Load map address for the main executable in default namespace. */ CORE_ADDR main_lm_addr = 0; @@ -431,6 +429,14 @@ struct svr4_info /* This identifies which namespaces are active. A namespace is considered active when there is at least one shared object loaded into it. */ std::set<size_t> active_namespaces; + + /* This flag indicates whether initializations related to the + GLIBC TLS module id tracking code have been performed. */ + bool glibc_tls_slots_inited = false; + + /* A vector of link map addresses for GLIBC TLS slots. See comment + for tls_maybe_fill_slot for more information. */ + std::vector<CORE_ADDR> glibc_tls_slots; }; /* Per-program-space data key. */ @@ -451,6 +457,12 @@ svr4_maybe_add_namespace (svr4_info *info, CORE_ADDR lmid) info->namespace_id.push_back (lmid); info->active_namespaces.insert (i); + + /* Create or update the convenience variable "active_namespaces". + It only needs to be updated here, as this only changes when a + dlmopen or dlclose call happens. */ + set_internalvar_integer (lookup_internalvar ("_active_linker_namespaces"), + info->active_namespaces.size ()); } /* Return whether DEBUG_BASE is the default namespace of INFO. */ @@ -629,10 +641,10 @@ read_program_header (int type, int *p_arch_size, CORE_ADDR *base_addr) return buf; } +/* See solib-svr4.h. */ -/* Return program interpreter string. */ -static std::optional<gdb::byte_vector> -find_program_interpreter (void) +std::optional<gdb::byte_vector> +svr4_find_program_interpreter () { /* If we have a current exec_bfd, use its section table. */ if (current_program_space->exec_bfd () @@ -740,9 +752,6 @@ elf_locate_base (void) { CORE_ADDR dyn_ptr, dyn_ptr_addr; - if (!svr4_have_link_map_offsets ()) - return 0; - /* Look for DT_MIPS_RLD_MAP first. MIPS executables use this instead of DT_DEBUG, although they sometimes contain an unused DT_DEBUG. */ @@ -937,7 +946,7 @@ svr4_keep_data_in_core (CORE_ADDR vaddr, unsigned long size) return (name_lm >= vaddr && name_lm < vaddr + size); } -/* See solist.h. */ +/* See solib.h. */ static int open_symbol_file_object (int from_tty) @@ -1032,10 +1041,10 @@ svr4_clear_so (const solib &so) li->l_addr_p = 0; } -/* Create the so_list objects equivalent to the svr4_sos in SOS. */ +/* Create the solib objects equivalent to the svr4_sos in SOS. */ static owning_intrusive_list<solib> -so_list_from_svr4_sos (const std::vector<svr4_so> &sos) +solib_from_svr4_sos (const std::vector<svr4_so> &sos) { owning_intrusive_list<solib> dst; @@ -1043,8 +1052,8 @@ so_list_from_svr4_sos (const std::vector<svr4_so> &sos) { auto &newobj = dst.emplace_back (); - newobj.so_name = so.name; - newobj.so_original_name = so.name; + newobj.name = so.name; + newobj.original_name = so.name; newobj.lm_info = std::make_unique<lm_info_svr4> (*so.lm_info); } @@ -1162,11 +1171,10 @@ static const struct gdb_xml_element svr4_library_list_elements[] = { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL } }; -/* Parse qXfer:libraries:read packet into *SO_LIST_RETURN. Return 1 if +/* Parse qXfer:libraries:read packet into *LIST. - Return 0 if packet not supported, *SO_LIST_RETURN is not modified in such - case. Return 1 if *SO_LIST_RETURN contains the library list, it may be - empty, caller is responsible for freeing all its entries. */ + Return 0 if packet not supported, *LIST is not modified in such case. + Return 1 if *LIST contains the library list. */ static int svr4_parse_libraries (const char *document, struct svr4_library_list *list) @@ -1188,11 +1196,11 @@ svr4_parse_libraries (const char *document, struct svr4_library_list *list) return 0; } -/* Attempt to get so_list from target via qXfer:libraries-svr4:read packet. +/* Attempt to get the shared object list from target via + qXfer:libraries-svr4:read packet. - Return 0 if packet not supported, *SO_LIST_RETURN is not modified in such - case. Return 1 if *SO_LIST_RETURN contains the library list, it may be - empty, caller is responsible for freeing all its entries. + Return 0 if packet not supported, *LIST is not modified in such case. + Return 1 if *LIST contains the library list. Note that ANNEX must be NULL if the remote does not explicitly allow qXfer:libraries-svr4:read packets with non-empty annexes. Support for @@ -1245,8 +1253,8 @@ svr4_default_sos (svr4_info *info) auto &newobj = sos.emplace_back (); newobj.lm_info = std::move (li); - newobj.so_name = info->debug_loader_name; - newobj.so_original_name = newobj.so_name; + newobj.name = info->debug_loader_name; + newobj.original_name = newobj.name; return sos; } @@ -1436,7 +1444,7 @@ svr4_collect_probes_sos (svr4_info *info) for (const auto &tuple : info->solib_lists) { const std::vector<svr4_so> &sos = tuple.second; - res.splice (so_list_from_svr4_sos (sos)); + res.splice (solib_from_svr4_sos (sos)); } return res; @@ -1574,6 +1582,204 @@ svr4_fetch_objfile_link_map (struct objfile *objfile) return 0; } +/* Return true if bfd section BFD_SECT is a thread local section + (i.e. either named ".tdata" or ".tbss"), and false otherwise. */ + +static bool +is_thread_local_section (struct bfd_section *bfd_sect) +{ + return ((strcmp (bfd_sect->name, ".tdata") == 0 + || strcmp (bfd_sect->name, ".tbss") == 0) + && bfd_sect->size != 0); +} + +/* Return true if objfile OBJF contains a thread local section, and + false otherwise. */ + +static bool +has_thread_local_section (const objfile *objf) +{ + for (obj_section *objsec : objf->sections ()) + if (is_thread_local_section (objsec->the_bfd_section)) + return true; + return false; +} + +/* Return true if solib SO contains a thread local section, and false + otherwise. */ + +static bool +has_thread_local_section (const solib &so) +{ + for (const target_section &p : so.sections) + if (is_thread_local_section (p.the_bfd_section)) + return true; + return false; +} + +/* For the MUSL C library, given link map address LM_ADDR, return the + corresponding TLS module id, or 0 if not found. + + Background: Unlike the mechanism used by glibc (see below), the + scheme used by the MUSL C library is pretty simple. If the + executable contains TLS variables it gets module id 1. Otherwise, + the first shared object loaded which contains TLS variables is + assigned to module id 1. TLS-containing shared objects are then + assigned consecutive module ids, based on the order that they are + loaded. When unloaded via dlclose, module ids are reassigned as if + that module had never been loaded. */ + +int +musl_link_map_to_tls_module_id (CORE_ADDR lm_addr) +{ + /* When lm_addr is zero, the program is statically linked. Any TLS + variables will be in module id 1. */ + if (lm_addr == 0) + return 1; + + int mod_id = 0; + if (has_thread_local_section (current_program_space->symfile_object_file)) + mod_id++; + + struct svr4_info *info = get_svr4_info (current_program_space); + + /* Cause svr4_current_sos() to be run if it hasn't been already. */ + if (info->main_lm_addr == 0) + solib_add (NULL, 0, auto_solib_add); + + /* Handle case where lm_addr corresponds to the main program. + Return value is either 0, when there are no TLS variables, or 1, + when there are. */ + if (lm_addr == info->main_lm_addr) + return mod_id; + + /* Iterate through the shared objects, possibly incrementing the + module id, and returning mod_id should a match be found. */ + for (const solib &so : current_program_space->solibs ()) + { + if (has_thread_local_section (so)) + mod_id++; + + auto *li = gdb::checked_static_cast<lm_info_svr4 *> (so.lm_info.get ()); + if (li->lm_addr == lm_addr) + return mod_id; + } + return 0; +} + +/* For GLIBC, given link map address LM_ADDR, return the corresponding TLS + module id, or 0 if not found. */ + +int +glibc_link_map_to_tls_module_id (CORE_ADDR lm_addr) +{ + /* When lm_addr is zero, the program is statically linked. Any TLS + variables will be in module id 1. */ + if (lm_addr == 0) + return 1; + + /* Look up lm_addr in the TLS slot data structure. */ + struct svr4_info *info = get_svr4_info (current_program_space); + auto it = std::find (info->glibc_tls_slots.begin (), + info->glibc_tls_slots.end (), + lm_addr); + if (it == info->glibc_tls_slots.end ()) + return 0; + else + return 1 + it - info->glibc_tls_slots.begin (); +} + +/* Conditionally, based on whether the shared object, SO, contains TLS + variables, assign a link map address to a TLS module id slot. This + code is GLIBC-specific and may only work for specific GLIBC + versions. That said, it is known to work for (at least) GLIBC + versions 2.27 thru 2.40. + + Background: In order to implement internal TLS address lookup + code, it is necessary to find the module id that has been + associated with a specific link map address. In GLIBC, the TLS + module id is stored in struct link_map, in the member + 'l_tls_modid'. While the first several members of struct link_map + are part of the SVR4 ABI, the offset to l_tls_modid definitely is + not. Therefore, since we don't know the offset to l_tls_modid, we + cannot simply look it up - which is a shame, because things would + be so much more easy and obviously accurate, if we could access + l_tls_modid. + + GLIBC has a concept of TLS module id slots. These slots are + allocated consecutively as shared objects containing TLS variables + are loaded. When unloaded (e.g. via dlclose()), the corresponding + slot is marked as unused, but may be used again when later loading + a shared object. + + The functions tls_maybe_fill_slot and tls_maybe_erase_slot are + associated with the observers 'solib_loaded' and 'solib_unloaded'. + They (attempt to) track use of TLS module id slots in the same way + that GLIBC does, which will hopefully provide an accurate module id + when asked to provide it via glibc_link_map_to_tls_module_id(), + above. */ + +static void +tls_maybe_fill_slot (solib &so) +{ + auto *li = dynamic_cast<lm_info_svr4 *> (so.lm_info.get ()); + if (li == nullptr) + return; + + struct svr4_info *info = get_svr4_info (current_program_space); + if (!info->glibc_tls_slots_inited) + { + /* Cause svr4_current_sos() to be run if it hasn't been already. */ + if (info->main_lm_addr == 0) + svr4_current_sos_direct (info); + + /* Quit early when main_lm_addr is still 0. */ + if (info->main_lm_addr == 0) + return; + + /* Also quit early when symfile_object_file is not yet known. */ + if (current_program_space->symfile_object_file == nullptr) + return; + + if (has_thread_local_section (current_program_space->symfile_object_file)) + info->glibc_tls_slots.push_back (info->main_lm_addr); + info->glibc_tls_slots_inited = true; + } + + if (has_thread_local_section (so)) + { + auto it = std::find (info->glibc_tls_slots.begin (), + info->glibc_tls_slots.end (), + 0); + if (it == info->glibc_tls_slots.end ()) + info->glibc_tls_slots.push_back (li->lm_addr); + else + *it = li->lm_addr; + } +} + +/* Remove a link map address from the TLS module slot data structure. + As noted above, this code is GLIBC-specific. */ + +static void +tls_maybe_erase_slot (program_space *pspace, const solib &so, + bool still_in_use, bool silent) +{ + if (still_in_use) + return; + + auto *li = dynamic_cast<lm_info_svr4 *> (so.lm_info.get ()); + if (li == nullptr) + return; + + struct svr4_info *info = get_svr4_info (pspace); + auto it = std::find (info->glibc_tls_slots.begin (), + info->glibc_tls_slots.end (), + li->lm_addr); + if (it != info->glibc_tls_slots.end ()) + *it = 0; +} + /* On some systems, the only way to recognize the link map entry for the main executable file is by looking at its name. Return non-zero iff SONAME matches one of the known main executable names. */ @@ -1592,10 +1798,10 @@ match_main (const char *soname) return (0); } -/* Return 1 if PC lies in the dynamic symbol resolution code of the +/* Return true if PC lies in the dynamic symbol resolution code of the SVR4 run time loader. */ -int +bool svr4_in_dynsym_resolve_code (CORE_ADDR pc) { struct svr4_info *info = get_svr4_info (current_program_space); @@ -1852,9 +2058,9 @@ solist_update_incremental (svr4_info *info, CORE_ADDR debug_base, /* Unknown key=value pairs are ignored by the gdbstub. */ xsnprintf (annex, sizeof (annex), "lmid=%s;start=%s;prev=%s", - phex_nz (debug_base, sizeof (debug_base)), - phex_nz (lm, sizeof (lm)), - phex_nz (prev_lm, sizeof (prev_lm))); + phex_nz (debug_base), + phex_nz (lm), + phex_nz (prev_lm)); if (!svr4_current_sos_via_xfer_libraries (&library_list, annex)) return 0; @@ -2371,7 +2577,7 @@ enable_break (struct svr4_info *info, int from_tty) /* Find the program interpreter; if not found, warn the user and drop into the old breakpoint at symbol code. */ std::optional<gdb::byte_vector> interp_name_holder - = find_program_interpreter (); + = svr4_find_program_interpreter (); if (interp_name_holder) { const char *interp_name = (const char *) interp_name_holder->data (); @@ -2411,7 +2617,7 @@ enable_break (struct svr4_info *info, int from_tty) address from the shared library table. */ for (const solib &so : current_program_space->solibs ()) { - if (svr4_same_1 (interp_name, so.so_original_name.c_str ())) + if (svr4_same_1 (interp_name, so.original_name.c_str ())) { load_addr_found = 1; loader_found_in_list = 1; @@ -2471,7 +2677,7 @@ enable_break (struct svr4_info *info, int from_tty) if (!loader_found_in_list) { - info->debug_loader_name = xstrdup (interp_name); + info->debug_loader_name = interp_name; info->debug_loader_offset_p = 1; info->debug_loader_offset = load_addr; solib_add (NULL, from_tty, auto_solib_add); @@ -3117,9 +3323,6 @@ svr4_solib_create_inferior_hook (int from_tty) if (!target_has_execution ()) return; - if (!svr4_have_link_map_offsets ()) - return; - if (!enable_break (info, from_tty)) return; } @@ -3131,8 +3334,7 @@ svr4_clear_solib (program_space *pspace) info->debug_base = 0; info->debug_loader_offset_p = 0; info->debug_loader_offset = 0; - xfree (info->debug_loader_name); - info->debug_loader_name = NULL; + info->debug_loader_name.clear (); } /* Clear any bits of ADDR that wouldn't fit in a target-format @@ -3319,17 +3521,6 @@ svr4_fetch_link_map_offsets (void) return ops->fetch_link_map_offsets (); } -/* Return 1 if a link map offset fetcher has been defined, 0 otherwise. */ - -static int -svr4_have_link_map_offsets (void) -{ - struct solib_svr4_ops *ops = get_ops (current_inferior ()->arch ()); - - return (ops->fetch_link_map_offsets != NULL); -} - - /* Most OS'es that have SVR4-style ELF dynamic libraries define a `struct r_debug' and a `struct link_map' that are binary compatible with the original SVR4 implementation. */ @@ -3444,7 +3635,7 @@ find_debug_base_for_solib (const solib *solib) const std::vector<svr4_so> &sos = tuple.second; for (const svr4_so &so : sos) - if (svr4_same (solib->so_original_name.c_str (), so.name.c_str (), + if (svr4_same (solib->original_name.c_str (), so.name.c_str (), *lm_info, *so.lm_info)) return debug_base; } @@ -3552,6 +3743,54 @@ svr4_num_active_namespaces () return info->active_namespaces.size (); } +/* See solib_ops::get_solibs_in_ns in solist.h. */ +static std::vector<const solib *> +svr4_get_solibs_in_ns (int nsid) +{ + std::vector<const solib*> ns_solibs; + svr4_info *info = get_svr4_info (current_program_space); + + /* If the namespace ID is inactive, there will be no active + libraries, so we can have an early exit, as a treat. */ + if (info->active_namespaces.count (nsid) != 1) + return ns_solibs; + + /* Since we only have the names of solibs in a given namespace, + we'll need to walk through the solib list of the inferior and + find which solib objects correspond to which svr4_so. We create + an unordered map with the names and lm_info to check things + faster, and to be able to remove SOs from the map, to avoid + returning the dynamic linker multiple times. */ + CORE_ADDR debug_base = info->namespace_id[nsid]; + std::unordered_map<std::string, const lm_info_svr4 *> namespace_solibs; + for (svr4_so &so : info->solib_lists[debug_base]) + { + namespace_solibs[so.name] + = gdb::checked_static_cast<const lm_info_svr4 *> + (so.lm_info.get ()); + } + for (const solib &so: current_program_space->solibs ()) + { + auto *lm_inferior + = gdb::checked_static_cast<const lm_info_svr4 *> (so.lm_info.get ()); + + /* This is inspired by the svr4_same, by finding the svr4_so object + in the map, and then double checking if the lm_info is considered + the same. */ + if (namespace_solibs.count (so.original_name) > 0 + && namespace_solibs[so.original_name]->l_addr_inferior + == lm_inferior->l_addr_inferior) + { + ns_solibs.push_back (&so); + /* Remove the SO from the map, so that we don't end up + printing the dynamic linker multiple times. */ + namespace_solibs.erase (so.original_name); + } + } + + return ns_solibs; +} + const struct solib_ops svr4_so_ops = { svr4_relocate_section_addresses, @@ -3569,6 +3808,7 @@ const struct solib_ops svr4_so_ops = svr4_find_solib_addr, svr4_find_solib_ns, svr4_num_active_namespaces, + svr4_get_solibs_in_ns, }; void _initialize_svr4_solib (); @@ -3577,4 +3817,8 @@ _initialize_svr4_solib () { gdb::observers::free_objfile.attach (svr4_free_objfile_observer, "solib-svr4"); + + /* Set up observers for tracking GLIBC TLS module id slots. */ + gdb::observers::solib_loaded.attach (tls_maybe_fill_slot, "solib-svr4"); + gdb::observers::solib_unloaded.attach (tls_maybe_erase_slot, "solib-svr4"); } |