/* Handle shared libraries for GDB, the GNU Debugger.
- Copyright (C) 1990-2021 Free Software Foundation, Inc.
+ Copyright (C) 1990-2022 Free Software Foundation, Inc.
This file is part of GDB.
#include <fcntl.h>
#include "symtab.h"
#include "bfd.h"
+#include "build-id.h"
#include "symfile.h"
#include "objfiles.h"
#include "gdbcore.h"
#include "command.h"
#include "target.h"
#include "frame.h"
-#include "gdb_regex.h"
+#include "gdbsupport/gdb_regex.h"
#include "inferior.h"
#include "gdbsupport/environ.h"
#include "language.h"
#include "filesystem.h"
#include "gdb_bfd.h"
#include "gdbsupport/filestuff.h"
+#include "gdbsupport/scoped_fd.h"
+#include "debuginfod-support.h"
#include "source.h"
#include "cli/cli-style.h"
+#include "solib-target.h"
-/* Architecture-specific operations. */
-
-/* Per-architecture data key. */
-static struct gdbarch_data *solib_data;
-
-static void *
-solib_init (struct obstack *obstack)
-{
- struct target_so_ops **ops;
-
- ops = OBSTACK_ZALLOC (obstack, struct target_so_ops *);
- *ops = current_target_so_ops;
- return ops;
-}
-
-static const struct target_so_ops *
-solib_ops (struct gdbarch *gdbarch)
-{
- const struct target_so_ops **ops
- = (const struct target_so_ops **) gdbarch_data (gdbarch, solib_data);
-
- return *ops;
-}
-
-/* Set the solib operations for GDBARCH to NEW_OPS. */
-
-void
-set_solib_ops (struct gdbarch *gdbarch, const struct target_so_ops *new_ops)
-{
- const struct target_so_ops **ops
- = (const struct target_so_ops **) gdbarch_data (gdbarch, solib_data);
-
- *ops = new_ops;
-}
-\f
-
-/* external data declarations */
-
-/* FIXME: gdbarch needs to control this variable, or else every
- configuration needs to call set_solib_ops. */
-struct target_so_ops *current_target_so_ops;
+/* See solib.h. */
-/* Local function prototypes */
+bool debug_solib;
/* If non-empty, this is a search path for loading non-absolute shared library
symbol files. This takes precedence over the environment variables PATH
and LD_LIBRARY_PATH. */
-static char *solib_search_path = NULL;
+static std::string solib_search_path;
static void
show_solib_search_path (struct ui_file *file, int from_tty,
struct cmd_list_element *c, const char *value)
{
- fprintf_filtered (file, _("The search path for loading non-absolute "
- "shared library symbol files is %s.\n"),
- value);
+ gdb_printf (file, _("The search path for loading non-absolute "
+ "shared library symbol files is %s.\n"),
+ value);
}
/* Same as HAVE_DOS_BASED_FILE_SYSTEM, but useable as an rvalue. */
static gdb::unique_xmalloc_ptr<char>
solib_find_1 (const char *in_pathname, int *fd, bool is_solib)
{
- const struct target_so_ops *ops = solib_ops (target_gdbarch ());
+ const struct target_so_ops *ops = gdbarch_so_ops (target_gdbarch ());
int found_file = -1;
gdb::unique_xmalloc_ptr<char> temp_pathname;
const char *fskind = effective_target_file_system_kind ();
- const char *sysroot = gdb_sysroot;
+ const char *sysroot = gdb_sysroot.c_str ();
int prefix_len, orig_prefix_len;
/* If the absolute prefix starts with "target:" but the filesystem
}
/* Now see if we can open it. */
- found_file = gdb_open_cloexec (temp_pathname.get (), O_RDONLY | O_BINARY, 0);
+ found_file = gdb_open_cloexec (temp_pathname.get (),
+ O_RDONLY | O_BINARY, 0).release ();
/* If the search in gdb_sysroot failed, and the path name has a
drive spec (e.g, c:/foo), try stripping ':' from the drive spec,
in_pathname + 2, (char *) NULL));
found_file = gdb_open_cloexec (temp_pathname.get (),
- O_RDONLY | O_BINARY, 0);
+ O_RDONLY | O_BINARY, 0).release ();
if (found_file < 0)
{
/* If the search in gdb_sysroot still failed, try fully
in_pathname + 2, (char *) NULL));
found_file = gdb_open_cloexec (temp_pathname.get (),
- O_RDONLY | O_BINARY, 0);
+ O_RDONLY | O_BINARY, 0).release ();
}
}
/* If not found, and we're looking for a solib, search the
solib_search_path (if any). */
- if (is_solib && found_file < 0 && solib_search_path != NULL)
- found_file = openp (solib_search_path,
+ if (is_solib && found_file < 0 && !solib_search_path.empty ())
+ found_file = openp (solib_search_path.c_str (),
OPF_TRY_CWD_FIRST | OPF_RETURN_REALPATH,
in_pathname, O_RDONLY | O_BINARY, &temp_pathname);
solib_search_path (if any) for the basename only (ignoring the
path). This is to allow reading solibs from a path that differs
from the opened path. */
- if (is_solib && found_file < 0 && solib_search_path != NULL)
- found_file = openp (solib_search_path,
+ if (is_solib && found_file < 0 && !solib_search_path.empty ())
+ found_file = openp (solib_search_path.c_str (),
OPF_TRY_CWD_FIRST | OPF_RETURN_REALPATH,
target_lbasename (fskind, in_pathname),
O_RDONLY | O_BINARY, &temp_pathname);
if (in_pathname == NULL)
return NULL;
- if (*gdb_sysroot != '\0' && IS_TARGET_ABSOLUTE_PATH (fskind, in_pathname))
+ if (!gdb_sysroot.empty () && IS_TARGET_ABSOLUTE_PATH (fskind, in_pathname))
{
result = solib_find_1 (in_pathname, fd, false);
/* Check bfd arch. */
b = gdbarch_bfd_arch_info (target_gdbarch ());
if (!b->compatible (b, bfd_get_arch_info (abfd.get ())))
- warning (_("`%s': Shared library architecture %s is not compatible "
- "with target architecture %s."), bfd_get_filename (abfd.get ()),
- bfd_get_arch_info (abfd.get ())->printable_name,
- b->printable_name);
+ error (_("`%s': Shared library architecture %s is not compatible "
+ "with target architecture %s."), bfd_get_filename (abfd.get ()),
+ bfd_get_arch_info (abfd.get ())->printable_name,
+ b->printable_name);
return abfd;
}
+/* Mapping of a core file's shared library sonames to their respective
+ build-ids. Added to the registries of core file bfds. */
+
+typedef std::unordered_map<std::string, std::string> soname_build_id_map;
+
+/* Key used to associate a soname_build_id_map to a core file bfd. */
+
+static const struct registry<bfd>::key<soname_build_id_map>
+ cbfd_soname_build_id_data_key;
+
+/* See solib.h. */
+
+void
+set_cbfd_soname_build_id (gdb_bfd_ref_ptr abfd,
+ const char *soname,
+ const bfd_build_id *build_id)
+{
+ gdb_assert (abfd.get () != nullptr);
+ gdb_assert (soname != nullptr);
+ gdb_assert (build_id != nullptr);
+
+ soname_build_id_map *mapptr = cbfd_soname_build_id_data_key.get (abfd.get ());
+
+ if (mapptr == nullptr)
+ mapptr = cbfd_soname_build_id_data_key.emplace (abfd.get ());
+
+ (*mapptr)[soname] = build_id_to_string (build_id);
+}
+
+/* See solib.h. */
+
+gdb::unique_xmalloc_ptr<char>
+get_cbfd_soname_build_id (gdb_bfd_ref_ptr abfd, const char *soname)
+{
+ if (abfd.get () == nullptr || soname == nullptr)
+ return {};
+
+ soname_build_id_map *mapptr
+ = cbfd_soname_build_id_data_key.get (abfd.get ());
+
+ if (mapptr == nullptr)
+ return {};
+
+ auto it = mapptr->find (lbasename (soname));
+ if (it == mapptr->end ())
+ return {};
+
+ return make_unique_xstrdup (it->second.c_str ());
+}
+
/* Given a pointer to one of the shared objects in our list of mapped
objects, use the recorded name to open a bfd descriptor for the
object, build a section table, relocate all the section addresses
static int
solib_map_sections (struct so_list *so)
{
- const struct target_so_ops *ops = solib_ops (target_gdbarch ());
+ const struct target_so_ops *ops = gdbarch_so_ops (target_gdbarch ());
gdb::unique_xmalloc_ptr<char> filename (tilde_expand (so->so_name));
gdb_bfd_ref_ptr abfd (ops->bfd_open (filename.get ()));
+ gdb::unique_xmalloc_ptr<char> build_id_hexstr
+ = get_cbfd_soname_build_id (current_program_space->cbfd, so->so_name);
+
+ /* If we already know the build-id of this solib from a core file, verify
+ it matches ABFD's build-id. If there is a mismatch or the solib wasn't
+ found, attempt to query debuginfod for the correct solib. */
+ if (build_id_hexstr.get () != nullptr)
+ {
+ bool mismatch = false;
+
+ if (abfd != nullptr && abfd->build_id != nullptr)
+ {
+ std::string build_id = build_id_to_string (abfd->build_id);
+
+ if (build_id != build_id_hexstr.get ())
+ mismatch = true;
+ }
+ if (abfd == nullptr || mismatch)
+ {
+ scoped_fd fd = debuginfod_exec_query ((const unsigned char*)
+ build_id_hexstr.get (),
+ 0, so->so_name, &filename);
+
+ if (fd.get () >= 0)
+ abfd = ops->bfd_open (filename.get ());
+ else if (mismatch)
+ warning (_("Build-id of %ps does not match core file."),
+ styled_string (file_name_style.style (), filename.get ()));
+ }
+ }
if (abfd == NULL)
return 0;
static void
clear_so (struct so_list *so)
{
- const struct target_so_ops *ops = solib_ops (target_gdbarch ());
+ const struct target_so_ops *ops = gdbarch_so_ops (target_gdbarch ());
delete so->sections;
so->sections = NULL;
void
free_so (struct so_list *so)
{
- const struct target_so_ops *ops = solib_ops (target_gdbarch ());
+ const struct target_so_ops *ops = gdbarch_so_ops (target_gdbarch ());
clear_so (so);
ops->free_so (so);
{
section_addr_info sap
= build_section_addr_info_from_section_table (*so->sections);
- so->objfile = symbol_file_add_from_bfd (so->abfd, so->so_name,
+ gdb_bfd_ref_ptr tmp_bfd
+ (gdb_bfd_ref_ptr::new_reference (so->abfd));
+ so->objfile = symbol_file_add_from_bfd (tmp_bfd, so->so_name,
flags, &sap,
OBJF_SHARED, NULL);
so->objfile->addr_low = so->addr_low;
void
update_solib_list (int from_tty)
{
- const struct target_so_ops *ops = solib_ops (target_gdbarch ());
+ const struct target_so_ops *ops = gdbarch_so_ops (target_gdbarch ());
struct so_list *inferior = ops->current_sos();
struct so_list *gdb, **gdb_link;
{
if (pattern != NULL)
{
- printf_unfiltered (_("Loading symbols for shared libraries: %s\n"),
- pattern);
+ gdb_printf (_("Loading symbols for shared libraries: %s\n"),
+ pattern);
}
else
- printf_unfiltered (_("Loading symbols for shared libraries.\n"));
+ gdb_printf (_("Loading symbols for shared libraries.\n"));
}
current_program_space->solib_add_generation++;
/* If no pattern was given, be quiet for shared
libraries we have already loaded. */
if (pattern && (from_tty || info_verbose))
- printf_unfiltered (_("Symbols already loaded for %s\n"),
- gdb->so_name);
+ gdb_printf (_("Symbols already loaded for %s\n"),
+ gdb->so_name);
}
else if (solib_read_symbols (gdb, add_flags))
loaded_any_symbols = true;
breakpoint_re_set ();
if (from_tty && pattern && ! any_matches)
- printf_unfiltered
+ gdb_printf
("No loaded shared libraries match the pattern `%s'.\n", pattern);
if (loaded_any_symbols)
breakpoints which are in shared libraries that are not currently
mapped in. */
-char *
+const char *
solib_name_from_address (struct program_space *pspace, CORE_ADDR address)
{
struct so_list *so = NULL;
bool
solib_keep_data_in_core (CORE_ADDR vaddr, unsigned long size)
{
- const struct target_so_ops *ops = solib_ops (target_gdbarch ());
+ const struct target_so_ops *ops = gdbarch_so_ops (target_gdbarch ());
if (ops->keep_data_in_core)
return ops->keep_data_in_core (vaddr, size) != 0;
void
clear_solib (void)
{
- const struct target_so_ops *ops = solib_ops (target_gdbarch ());
+ const struct target_so_ops *ops = gdbarch_so_ops (target_gdbarch ());
disable_breakpoints_in_shlibs ();
void
solib_create_inferior_hook (int from_tty)
{
- const struct target_so_ops *ops = solib_ops (target_gdbarch ());
+ const struct target_so_ops *ops = gdbarch_so_ops (target_gdbarch ());
ops->solib_create_inferior_hook (from_tty);
}
bool
in_solib_dynsym_resolve_code (CORE_ADDR pc)
{
- const struct target_so_ops *ops = solib_ops (target_gdbarch ());
+ const struct target_so_ops *ops = gdbarch_so_ops (target_gdbarch ());
return ops->in_dynsym_resolve_code (pc) != 0;
}
void
update_solib_breakpoints (void)
{
- const struct target_so_ops *ops = solib_ops (target_gdbarch ());
+ const struct target_so_ops *ops = gdbarch_so_ops (target_gdbarch ());
if (ops->update_breakpoints != NULL)
ops->update_breakpoints ();
void
handle_solib_event (void)
{
- const struct target_so_ops *ops = solib_ops (target_gdbarch ());
+ const struct target_so_ops *ops = gdbarch_so_ops (target_gdbarch ());
if (ops->handle_event != NULL)
ops->handle_event ();
reload_shared_libraries_1 (int from_tty)
{
if (print_symbol_loading_p (from_tty, 0, 0))
- printf_unfiltered (_("Loading symbols for shared libraries.\n"));
+ gdb_printf (_("Loading symbols for shared libraries.\n"));
for (struct so_list *so : current_program_space->solibs ())
{
reload_shared_libraries_1 (from_tty);
- ops = solib_ops (target_gdbarch ());
+ ops = gdbarch_so_ops (target_gdbarch ());
/* Creating inferior hooks here has two purposes. First, if we reload
shared libraries then the address of solib breakpoint we've computed
const char *old_prefix = "remote:";
const char *new_prefix = TARGET_SYSROOT_PREFIX;
- if (startswith (gdb_sysroot, old_prefix))
+ if (startswith (gdb_sysroot.c_str (), old_prefix))
{
static bool warning_issued = false;
gdb_assert (strlen (old_prefix) == strlen (new_prefix));
- memcpy (gdb_sysroot, new_prefix, strlen (new_prefix));
+ gdb_sysroot = new_prefix + gdb_sysroot.substr (strlen (old_prefix));
if (!warning_issued)
{
warning (_("\"%s\" is deprecated, use \"%s\" instead."),
old_prefix, new_prefix);
- warning (_("sysroot set to \"%s\"."), gdb_sysroot);
+ warning (_("sysroot set to \"%s\"."), gdb_sysroot.c_str ());
warning_issued = true;
}
show_auto_solib_add (struct ui_file *file, int from_tty,
struct cmd_list_element *c, const char *value)
{
- fprintf_filtered (file, _("Autoloading of shared library symbols is %s.\n"),
- value);
+ gdb_printf (file, _("Autoloading of shared library symbols is %s.\n"),
+ value);
}
{
struct minimal_symbol msym {};
- SET_MSYMBOL_VALUE_ADDRESS (&msym, symaddr);
+ msym.set_value_address (symaddr);
gdbarch_elf_make_msymbol_special (gdbarch, sym, &msym);
- symaddr = MSYMBOL_VALUE_RAW_ADDRESS (&msym);
+ symaddr = msym.value_raw_address ();
}
/* BFD symbols are section relative. */
return 0;
}
+/* See solib.h. */
+
+gdb::unique_xmalloc_ptr<char>
+gdb_bfd_read_elf_soname (const char *filename)
+{
+ gdb_bfd_ref_ptr abfd = gdb_bfd_open (filename, gnutarget);
+
+ if (abfd == nullptr)
+ return {};
+
+ /* Check that ABFD is an ET_DYN ELF file. */
+ if (!bfd_check_format (abfd.get (), bfd_object)
+ || !(bfd_get_file_flags (abfd.get ()) & DYNAMIC))
+ return {};
+
+ CORE_ADDR idx;
+ if (!gdb_bfd_scan_elf_dyntag (DT_SONAME, abfd.get (), &idx, nullptr))
+ return {};
+
+ struct bfd_section *dynstr = bfd_get_section_by_name (abfd.get (), ".dynstr");
+ int sect_size = bfd_section_size (dynstr);
+ if (dynstr == nullptr || sect_size <= idx)
+ return {};
+
+ /* Read soname from the string table. */
+ gdb::byte_vector dynstr_buf;
+ if (!gdb_bfd_get_full_section_contents (abfd.get (), dynstr, &dynstr_buf))
+ return {};
+
+ /* Ensure soname is null-terminated before returning a copy. */
+ char *soname = (char *) dynstr_buf.data () + idx;
+ if (strnlen (soname, sect_size - idx) == sect_size - idx)
+ return {};
+
+ return make_unique_xstrdup (soname);
+}
+
/* Lookup the value for a specific symbol from symbol table. Look up symbol
from ABFD. MATCH_SYM is a callback function to determine whether to pick
up a symbol. DATA is the input of this callback function. Return NULL
void
_initialize_solib ()
{
- solib_data = gdbarch_data_register_pre_init (solib_init);
-
gdb::observers::free_objfile.attach (remove_user_added_objfile,
"solib");
gdb::observers::inferior_execd.attach ([] (inferior *inf)
reload_shared_libraries,
show_solib_search_path,
&setlist, &showlist);
+
+ add_setshow_boolean_cmd ("solib", class_maintenance,
+ &debug_solib, _("\
+Set solib debugging."), _("\
+Show solib debugging."), _("\
+When true, solib-related debugging output is enabled."),
+ nullptr, nullptr,
+ &setdebuglist, &showdebuglist);
}