diff --git a/gdb/build-id.c b/gdb/build-id.c
--- a/gdb/build-id.c
+++ b/gdb/build-id.c
-@@ -24,13 +24,70 @@
+@@ -24,13 +24,71 @@
#include "gdbsupport/gdb_vecs.h"
#include "symfile.h"
#include "objfiles.h"
+#include "gdb_bfd.h"
+#include "gdbcmd.h"
#include "gdbcore.h"
++#include "inferior.h"
+#include "objfiles.h"
+#include "observable.h"
+#include "symfile.h"
{
if (!bfd_check_format (abfd, bfd_object)
&& !bfd_check_format (abfd, bfd_core))
-@@ -43,6 +100,348 @@ build_id_bfd_get (bfd *abfd)
+@@ -43,6 +101,348 @@ build_id_bfd_get (bfd *abfd)
return NULL;
}
/* See build-id.h. */
int
-@@ -51,7 +450,7 @@ build_id_verify (bfd *abfd, size_t check_len, const bfd_byte *check)
+@@ -51,7 +451,7 @@ build_id_verify (bfd *abfd, size_t check_len, const bfd_byte *check)
const struct bfd_build_id *found;
int retval = 0;
if (found == NULL)
warning (_("File \"%s\" has no build-id, file skipped"),
-@@ -66,56 +465,159 @@ build_id_verify (bfd *abfd, size_t check_len, const bfd_byte *check)
+@@ -66,63 +466,166 @@ build_id_verify (bfd *abfd, size_t check_len, const bfd_byte *check)
return retval;
}
+static char *
+link_resolve (const char *symlink, int level)
+{
-+ char buf[PATH_MAX + 1], *target, *retval;
++ char buf[PATH_MAX + 1], *retval;
++ gdb::unique_xmalloc_ptr<char> target;
+ ssize_t got;
+
+ if (level > 10)
+ buf[got] = '\0';
+
+ if (IS_ABSOLUTE_PATH (buf))
-+ target = xstrdup (buf);
++ target = make_unique_xstrdup (buf);
+ else
+ {
+ const std::string dir (ldirname (symlink));
+ "%s", dir.c_str(), buf);
+ }
+
-+ retval = link_resolve (target, level + 1);
-+ xfree (target);
++ retval = link_resolve (target.get (), level + 1);
+ return retval;
+}
+
+
if (separate_debug_file_debug)
{
-- printf_unfiltered (_(" Trying %s..."), link.c_str ());
-+ printf_unfiltered (_(" Trying %s..."), orig_link.c_str ());
- gdb_flush (gdb_stdout);
+- fprintf_unfiltered (gdb_stdlog, _(" Trying %s..."), link.c_str ());
+- gdb_flush (gdb_stdlog);
++ fprintf_unfiltered (gdb_stdlog, _(" Trying %s..."), orig_link.c_str ());
++ gdb_flush (gdb_stdout);
}
- /* lrealpath() is expensive even for the usually non-existent files. */
-- gdb::unique_xmalloc_ptr<char> filename;
-- if (access (link.c_str (), F_OK) == 0)
-- filename.reset (lrealpath (link.c_str ()));
--
-- if (filename == NULL)
+- gdb::unique_xmalloc_ptr<char> filename_holder;
+- const char *filename = nullptr;
+- if (startswith (link, TARGET_SYSROOT_PREFIX))
+- filename = link.c_str ();
+- else if (access (link.c_str (), F_OK) == 0)
+ for (unsigned seqno = 0;; seqno++)
{
-- if (separate_debug_file_debug)
-- printf_unfiltered (_(" no, unable to compute real path\n"));
+- filename_holder.reset (lrealpath (link.c_str ()));
+- filename = filename_holder.get ();
+- }
+ std::string link = orig_link;
-- return {};
-- }
+- if (filename == NULL)
+- {
+- if (separate_debug_file_debug)
+- fprintf_unfiltered (gdb_stdlog,
+- _(" no, unable to compute real path\n"));
+ if (seqno > 0)
+ {
+ /* There can be multiple build-id symlinks pointing to real files
+ with the same build-id (such as hard links). Some of the real
+ files may not be installed. */
-+
+
+- return {};
+- }
+ string_appendf (link, ".%u", seqno);
+ }
- /* We expect to be silent on the non-existing files. */
-- gdb_bfd_ref_ptr debug_bfd = gdb_bfd_open (filename.get (), gnutarget);
+- gdb_bfd_ref_ptr debug_bfd = gdb_bfd_open (filename, gnutarget);
+ ret_link = link;
- if (debug_bfd == NULL)
- {
- if (separate_debug_file_debug)
-- printf_unfiltered (_(" no, unable to open.\n"));
+- fprintf_unfiltered (gdb_stdlog, _(" no, unable to open.\n"));
+ struct stat statbuf_trash;
+
+ /* `access' automatically dereferences LINK. */
+ }
+
+ /* lrealpath() is expensive even for the usually non-existent files. */
-+ gdb::unique_xmalloc_ptr<char> filename;
-+
-+ if (access (link.c_str (), F_OK) == 0)
-+ filename.reset (lrealpath (link.c_str ()));
++ gdb::unique_xmalloc_ptr<char> filename_holder;
++ const char *filename = nullptr;
++ if (startswith (link, TARGET_SYSROOT_PREFIX))
++ filename = link.c_str ();
++ else if (access (link.c_str (), F_OK) == 0)
++ {
++ filename_holder.reset (lrealpath (link.c_str ()));
++ filename = filename_holder.get ();
++ }
+
+ if (filename == NULL)
+ {
+ if (separate_debug_file_debug)
-+ printf_unfiltered (_(" no, unable to compute real path\n"));
++ fprintf_unfiltered (gdb_stdlog,
++ _(" no, unable to compute real path\n"));
+
+ continue;
+ }
+
+ /* We expect to be silent on the non-existing files. */
-+ gdb_bfd_ref_ptr debug_bfd = gdb_bfd_open (filename.get (), gnutarget, -1);
-
-- return {};
++ gdb_bfd_ref_ptr debug_bfd = gdb_bfd_open (filename, gnutarget);
++
+ if (debug_bfd == NULL)
+ {
+ if (separate_debug_file_debug)
-+ printf_unfiltered (_(" no, unable to open.\n"));
-+
++ fprintf_unfiltered (gdb_stdlog, _(" no, unable to open.\n"));
+
+- return {};
+ continue;
+ }
+
+ if (!build_id_verify (debug_bfd.get(), build_id_len, build_id))
+ {
+ if (separate_debug_file_debug)
-+ printf_unfiltered (_(" no, build-id does not match.\n"));
++ fprintf_unfiltered (gdb_stdlog,
++ _(" no, build-id does not match.\n"));
+
+ continue;
+ }
+ if (ret_bfd != NULL)
{
if (separate_debug_file_debug)
-- printf_unfiltered (_(" no, build-id does not match.\n"));
--
-- return {};
-+ printf_unfiltered (_(" yes!\n"));
+- fprintf_unfiltered (gdb_stdlog, _(" no, build-id does not match.\n"));
++ fprintf_unfiltered (gdb_stdlog, _(" yes!\n"));
+ }
+ else
+ {
+ /* If none of the real files is found report as missing file
+ always the non-.%u-suffixed file. */
+ std::string link0 = orig_link;
-+
+
+- return {};
+ /* If the symlink has target request to install the target.
+ BASE-debuginfo.rpm contains the symlink but BASE.rpm may be missing.
+ https://bugzilla.redhat.com/show_bug.cgi?id=981154 */
}
- if (separate_debug_file_debug)
-- printf_unfiltered (_(" yes!\n"));
+- fprintf_unfiltered (gdb_stdlog, _(" yes!\n"));
+ if (link_return != NULL)
+ {
+ if (ret_bfd != NULL)
}
/* Common code for finding BFDs of a given build-id. This function
-@@ -124,7 +626,7 @@ build_id_to_debug_bfd_1 (const std::string &link, size_t build_id_len,
+@@ -131,7 +634,7 @@ build_id_to_debug_bfd_1 (const std::string &link, size_t build_id_len,
static gdb_bfd_ref_ptr
build_id_to_bfd_suffix (size_t build_id_len, const bfd_byte *build_id,
{
/* Keep backward compatibility so that DEBUG_FILE_DIRECTORY being "" will
cause "/.build-id/..." lookups. */
-@@ -147,16 +649,17 @@ build_id_to_bfd_suffix (size_t build_id_len, const bfd_byte *build_id,
+@@ -154,16 +657,17 @@ build_id_to_bfd_suffix (size_t build_id_len, const bfd_byte *build_id,
if (size > 0)
{
size--;
if (debug_bfd != NULL)
return debug_bfd;
-@@ -170,7 +673,8 @@ build_id_to_bfd_suffix (size_t build_id_len, const bfd_byte *build_id,
- if (strcmp (gdb_sysroot, TARGET_SYSROOT_PREFIX) != 0)
+@@ -174,7 +678,8 @@ build_id_to_bfd_suffix (size_t build_id_len, const bfd_byte *build_id,
+ if (!gdb_sysroot.empty ())
{
link = gdb_sysroot + link;
- debug_bfd = build_id_to_debug_bfd_1 (link, build_id_len, build_id);
if (debug_bfd != NULL)
return debug_bfd;
}
-@@ -179,38 +683,208 @@ build_id_to_bfd_suffix (size_t build_id_len, const bfd_byte *build_id,
+@@ -183,30 +688,649 @@ build_id_to_bfd_suffix (size_t build_id_len, const bfd_byte *build_id,
return {};
}
+ return result;
+}
+
++#ifdef HAVE_LIBRPM
++
++#include <rpm/rpmlib.h>
++#include <rpm/rpmts.h>
++#include <rpm/rpmdb.h>
++#include <rpm/header.h>
++#ifdef DLOPEN_LIBRPM
++#include <dlfcn.h>
++#endif
++
++/* Workarodun https://bugzilla.redhat.com/show_bug.cgi?id=643031
++ librpm must not exit() an application on SIGINT
++
++ Enable or disable a signal handler. SIGNUM: signal to enable (or disable
++ if negative). HANDLER: sa_sigaction handler (or NULL to use
++ rpmsqHandler()). Returns: no. of refs, -1 on error. */
++extern int rpmsqEnable (int signum, /* rpmsqAction_t handler */ void *handler);
++int
++rpmsqEnable (int signum, /* rpmsqAction_t handler */ void *handler)
++{
++ return 0;
++}
++
++/* This MISSING_RPM_HASH tracker is used to collect all the missing rpm files
++ and avoid their duplicities during a single inferior run. */
++
++static struct htab *missing_rpm_hash;
++
++/* This MISSING_RPM_LIST tracker is used to collect and print as a single line
++ all the rpms right before the nearest GDB prompt. It gets cleared after
++ each such print (it is questionable if we should clear it after the print).
++ */
++
++struct missing_rpm
++ {
++ struct missing_rpm *next;
++ char rpm[1];
++ };
++static struct missing_rpm *missing_rpm_list;
++static int missing_rpm_list_entries;
++
++/* Returns the count of newly added rpms. */
++
++static int
++#ifndef GDB_INDEX_VERIFY_VENDOR
++missing_rpm_enlist (const char *filename)
++#else
++missing_rpm_enlist_1 (const char *filename, int verify_vendor)
++#endif
++{
++ static int rpm_init_done = 0;
++ rpmts ts;
++ rpmdbMatchIterator mi;
++ int count = 0;
++
++#ifdef DLOPEN_LIBRPM
++ /* Duplicate here the declarations to verify they match. The same sanity
++ check is present also in `configure.ac'. */
++ extern char * headerFormat(Header h, const char * fmt, errmsg_t * errmsg);
++ static char *(*headerFormat_p) (Header h, const char * fmt, errmsg_t *errmsg);
++ extern int rpmReadConfigFiles(const char * file, const char * target);
++ static int (*rpmReadConfigFiles_p) (const char * file, const char * target);
++ extern rpmdbMatchIterator rpmdbFreeIterator(rpmdbMatchIterator mi);
++ static rpmdbMatchIterator (*rpmdbFreeIterator_p) (rpmdbMatchIterator mi);
++ extern Header rpmdbNextIterator(rpmdbMatchIterator mi);
++ static Header (*rpmdbNextIterator_p) (rpmdbMatchIterator mi);
++ extern rpmts rpmtsCreate(void);
++ static rpmts (*rpmtsCreate_p) (void);
++ extern rpmts rpmtsFree(rpmts ts);
++ static rpmts (*rpmtsFree_p) (rpmts ts);
++ extern rpmdbMatchIterator rpmtsInitIterator(const rpmts ts, rpmTag rpmtag,
++ const void * keyp, size_t keylen);
++ static rpmdbMatchIterator (*rpmtsInitIterator_p) (const rpmts ts,
++ rpmTag rpmtag,
++ const void *keyp,
++ size_t keylen);
++#else /* !DLOPEN_LIBRPM */
++# define headerFormat_p headerFormat
++# define rpmReadConfigFiles_p rpmReadConfigFiles
++# define rpmdbFreeIterator_p rpmdbFreeIterator
++# define rpmdbNextIterator_p rpmdbNextIterator
++# define rpmtsCreate_p rpmtsCreate
++# define rpmtsFree_p rpmtsFree
++# define rpmtsInitIterator_p rpmtsInitIterator
++#endif /* !DLOPEN_LIBRPM */
++
++ gdb_assert (filename != NULL);
++
++ if (strcmp (filename, BUILD_ID_MAIN_EXECUTABLE_FILENAME) == 0)
++ return 0;
++
++ if (is_target_filename (filename))
++ return 0;
++
++ if (filename[0] != '/')
++ {
++ warning (_("Ignoring non-absolute filename: <%s>"), filename);
++ return 0;
++ }
++
++ if (!rpm_init_done)
++ {
++ static int init_tried;
++
++ /* Already failed the initialization before? */
++ if (init_tried)
++ return 0;
++ init_tried = 1;
++
++#ifdef DLOPEN_LIBRPM
++ {
++ void *h;
++
++ h = dlopen (DLOPEN_LIBRPM, RTLD_LAZY);
++ if (!h)
++ {
++ warning (_("Unable to open \"%s\" (%s), "
++ "missing debuginfos notifications will not be displayed"),
++ DLOPEN_LIBRPM, dlerror ());
++ return 0;
++ }
++
++ if (!((headerFormat_p = (char *(*) (Header h, const char * fmt, errmsg_t *errmsg)) dlsym (h, "headerFormat"))
++ && (rpmReadConfigFiles_p = (int (*) (const char * file, const char * target)) dlsym (h, "rpmReadConfigFiles"))
++ && (rpmdbFreeIterator_p = (rpmdbMatchIterator (*) (rpmdbMatchIterator mi)) dlsym (h, "rpmdbFreeIterator"))
++ && (rpmdbNextIterator_p = (Header (*) (rpmdbMatchIterator mi)) dlsym (h, "rpmdbNextIterator"))
++ && (rpmtsCreate_p = (rpmts (*) (void)) dlsym (h, "rpmtsCreate"))
++ && (rpmtsFree_p = (rpmts (*) (rpmts ts)) dlsym (h, "rpmtsFree"))
++ && (rpmtsInitIterator_p = (rpmdbMatchIterator (*) (const rpmts ts, rpmTag rpmtag, const void *keyp, size_t keylen)) dlsym (h, "rpmtsInitIterator"))))
++ {
++ warning (_("Opened library \"%s\" is incompatible (%s), "
++ "missing debuginfos notifications will not be displayed"),
++ DLOPEN_LIBRPM, dlerror ());
++ if (dlclose (h))
++ warning (_("Error closing library \"%s\": %s\n"), DLOPEN_LIBRPM,
++ dlerror ());
++ return 0;
++ }
++ }
++#endif /* DLOPEN_LIBRPM */
++
++ if (rpmReadConfigFiles_p (NULL, NULL) != 0)
++ {
++ warning (_("Error reading the rpm configuration files"));
++ return 0;
++ }
++
++ rpm_init_done = 1;
++ }
++
++ ts = rpmtsCreate_p ();
++
++ mi = rpmtsInitIterator_p (ts, RPMTAG_BASENAMES, filename, 0);
++ if (mi != NULL)
++ {
++#ifndef GDB_INDEX_VERIFY_VENDOR
++ for (;;)
++#else
++ if (!verify_vendor) for (;;)
++#endif
++ {
++ Header h;
++ char *debuginfo, **slot, *s, *s2;
++ errmsg_t err;
++ size_t srcrpmlen = sizeof (".src.rpm") - 1;
++ size_t debuginfolen = sizeof ("-debuginfo") - 1;
++ rpmdbMatchIterator mi_debuginfo;
++
++ h = rpmdbNextIterator_p (mi);
++ if (h == NULL)
++ break;
++
++ /* Verify the debuginfo file is not already installed. */
++
++ debuginfo = headerFormat_p (h, "%{sourcerpm}-debuginfo.%{arch}",
++ &err);
++ if (!debuginfo)
++ {
++ warning (_("Error querying the rpm file `%s': %s"), filename,
++ err);
++ continue;
++ }
++ /* s = `.src.rpm-debuginfo.%{arch}' */
++ s = strrchr (debuginfo, '-') - srcrpmlen;
++ s2 = NULL;
++ if (s > debuginfo && memcmp (s, ".src.rpm", srcrpmlen) == 0)
++ {
++ /* s2 = `-%{release}.src.rpm-debuginfo.%{arch}' */
++ s2 = (char *) memrchr (debuginfo, '-', s - debuginfo);
++ }
++ if (s2)
++ {
++ /* s2 = `-%{version}-%{release}.src.rpm-debuginfo.%{arch}' */
++ s2 = (char *) memrchr (debuginfo, '-', s2 - debuginfo);
++ }
++ if (!s2)
++ {
++ warning (_("Error querying the rpm file `%s': %s"), filename,
++ debuginfo);
++ xfree (debuginfo);
++ continue;
++ }
++ /* s = `.src.rpm-debuginfo.%{arch}' */
++ /* s2 = `-%{version}-%{release}.src.rpm-debuginfo.%{arch}' */
++ memmove (s2 + debuginfolen, s2, s - s2);
++ memcpy (s2, "-debuginfo", debuginfolen);
++ /* s = `XXXX.%{arch}' */
++ /* strlen ("XXXX") == srcrpmlen + debuginfolen */
++ /* s2 = `-debuginfo-%{version}-%{release}XX.%{arch}' */
++ /* strlen ("XX") == srcrpmlen */
++ memmove (s + debuginfolen, s + srcrpmlen + debuginfolen,
++ strlen (s + srcrpmlen + debuginfolen) + 1);
++ /* s = `-debuginfo-%{version}-%{release}.%{arch}' */
++
++ /* RPMDBI_PACKAGES requires keylen == sizeof (int). */
++ /* RPMDBI_LABEL is an interface for NVR-based dbiFindByLabel(). */
++ mi_debuginfo = rpmtsInitIterator_p (ts, (rpmTag) RPMDBI_LABEL, debuginfo, 0);
++ xfree (debuginfo);
++ if (mi_debuginfo)
++ {
++ rpmdbFreeIterator_p (mi_debuginfo);
++ count = 0;
++ break;
++ }
++
++ /* The allocated memory gets utilized below for MISSING_RPM_HASH. */
++ debuginfo = headerFormat_p (h,
++ "%{name}-%{version}-%{release}.%{arch}",
++ &err);
++ if (!debuginfo)
++ {
++ warning (_("Error querying the rpm file `%s': %s"), filename,
++ err);
++ continue;
++ }
++
++ /* Base package name for `debuginfo-install'. We do not use the
++ `yum' command directly as the line
++ yum --enablerepo='*debug*' install NAME-debuginfo.ARCH
++ would be more complicated than just:
++ debuginfo-install NAME-VERSION-RELEASE.ARCH
++ Do not supply the rpm base name (derived from .src.rpm name) as
++ debuginfo-install is unable to install the debuginfo package if
++ the base name PKG binary rpm is not installed while for example
++ PKG-libs would be installed (RH Bug 467901).
++ FUTURE: After multiple debuginfo versions simultaneously installed
++ get supported the support for the VERSION-RELEASE tags handling
++ may need an update. */
++
++ if (missing_rpm_hash == NULL)
++ {
++ /* DEL_F is passed NULL as MISSING_RPM_LIST's HTAB_DELETE
++ should not deallocate the entries. */
++
++ missing_rpm_hash = htab_create_alloc (64, htab_hash_string,
++ (int (*) (const void *, const void *)) streq,
++ NULL, xcalloc, xfree);
++ }
++ slot = (char **) htab_find_slot (missing_rpm_hash, debuginfo, INSERT);
++ /* XCALLOC never returns NULL. */
++ gdb_assert (slot != NULL);
++ if (*slot == NULL)
++ {
++ struct missing_rpm *missing_rpm;
++
++ *slot = debuginfo;
++
++ missing_rpm = (struct missing_rpm *) xmalloc (sizeof (*missing_rpm) + strlen (debuginfo));
++ strcpy (missing_rpm->rpm, debuginfo);
++ missing_rpm->next = missing_rpm_list;
++ missing_rpm_list = missing_rpm;
++ missing_rpm_list_entries++;
++ }
++ else
++ xfree (debuginfo);
++ count++;
++ }
++#ifdef GDB_INDEX_VERIFY_VENDOR
++ else /* verify_vendor */
++ {
++ int vendor_pass = 0, vendor_fail = 0;
++
++ for (;;)
++ {
++ Header h;
++ errmsg_t err;
++ char *vendor;
++
++ h = rpmdbNextIterator_p (mi);
++ if (h == NULL)
++ break;
++
++ vendor = headerFormat_p (h, "%{vendor}", &err);
++ if (!vendor)
++ {
++ warning (_("Error querying the rpm file `%s': %s"), filename,
++ err);
++ continue;
++ }
++ if (strcmp (vendor, "Red Hat, Inc.") == 0)
++ vendor_pass = 1;
++ else
++ vendor_fail = 1;
++ xfree (vendor);
++ }
++ count = vendor_pass != 0 && vendor_fail == 0;
++ }
++#endif
++
++ rpmdbFreeIterator_p (mi);
++ }
++
++ rpmtsFree_p (ts);
++
++ return count;
++}
++
++#ifdef GDB_INDEX_VERIFY_VENDOR
++missing_rpm_enlist (const char *filename)
++{
++ return missing_rpm_enlist_1 (filename, 0);
++}
++
++extern int rpm_verify_vendor (const char *filename);
++int
++rpm_verify_vendor (const char *filename)
++{
++ return missing_rpm_enlist_1 (filename, 1);
++}
++#endif
++
++static bool
++missing_rpm_list_compar (const char *ap, const char *bp)
++{
++ return strcoll (ap, bp) < 0;
++}
++
++/* It returns a NULL-terminated array of strings needing to be FREEd. It may
++ also return only NULL. */
++
++static void
++missing_rpm_list_print (void)
++{
++ struct missing_rpm *list_iter;
++
++ if (missing_rpm_list_entries == 0)
++ return;
++
++ std::vector<const char *> array (missing_rpm_list_entries);
++ size_t idx = 0;
++
++ for (list_iter = missing_rpm_list; list_iter != NULL;
++ list_iter = list_iter->next)
++ {
++ array[idx++] = list_iter->rpm;
++ }
++ gdb_assert (idx == missing_rpm_list_entries);
++
++ std::sort (array.begin (), array.end (), missing_rpm_list_compar);
++
++ /* We zero out the number of missing RPMs here because of a nasty
++ bug (see RHBZ 1801974).
++
++ When we call 'puts_unfiltered' below, if pagination is on and if
++ the number of missing RPMs is big enough to trigger pagination,
++ we will end up in an infinite recursion. The call chain looks
++ like this:
++
++ missing_rpm_list_print -> puts_unfiltered -> fputs_maybe_filtered
++ -> prompt_for_continue -> display_gdb_prompt ->
++ debug_flush_missing -> missing_rpm_list_print ...
++
++ For this reason, we make sure MISSING_RPM_LIST_ENTRIES is zero
++ *before* calling any print function. */
++ missing_rpm_list_entries = 0;
++
++ printf_unfiltered (_("Missing separate debuginfos, use: %s"),
++#ifdef DNF_DEBUGINFO_INSTALL
++ "dnf "
++#endif
++ "debuginfo-install");
++ for (const char *el : array)
++ {
++ puts_unfiltered (" ");
++ puts_unfiltered (el);
++ }
++ puts_unfiltered ("\n");
++
++ while (missing_rpm_list != NULL)
++ {
++ list_iter = missing_rpm_list;
++ missing_rpm_list = list_iter->next;
++ xfree (list_iter);
++ }
++}
++
++static void
++missing_rpm_change (void)
++{
++ debug_flush_missing ();
++
++ gdb_assert (missing_rpm_list == NULL);
++ if (missing_rpm_hash != NULL)
++ {
++ htab_delete (missing_rpm_hash);
++ missing_rpm_hash = NULL;
++ }
++}
++
++enum missing_exec
++ {
++ /* Init state. EXEC_BFD also still could be NULL. */
++ MISSING_EXEC_NOT_TRIED,
++ /* We saw a non-NULL EXEC_BFD but RPM has no info about it. */
++ MISSING_EXEC_NOT_FOUND,
++ /* We found EXEC_BFD by RPM and we either have its symbols (either embedded
++ or separate) or the main executable's RPM is now contained in
++ MISSING_RPM_HASH. */
++ MISSING_EXEC_ENLISTED
++ };
++static enum missing_exec missing_exec = MISSING_EXEC_NOT_TRIED;
++
++#endif /* HAVE_LIBRPM */
++
++void
++debug_flush_missing (void)
++{
++#ifdef HAVE_LIBRPM
++ missing_rpm_list_print ();
++#endif
++}
++
+/* This MISSING_FILEPAIR_HASH tracker is used only for the duplicite messages
-+ Try to install the hash file ...
++ yum --enablerepo='*debug*' install ...
+ avoidance. */
+
+struct missing_filepair
+ /* All their memory came just from missing_filepair_OBSTACK. */
+ missing_filepair_hash = NULL;
+ }
++#ifdef HAVE_LIBRPM
++ missing_exec = MISSING_EXEC_NOT_TRIED;
++#endif
+}
+
+static void
+debug_print_executable_changed (void)
+{
++#ifdef HAVE_LIBRPM
++ missing_rpm_change ();
++#endif
+ missing_filepair_change ();
+}
+
+
+ *slot = missing_filepair;
+
-+ /* We do not collect and flush these messages as each such message
-+ already requires its own separate lines. */
++#ifdef HAVE_LIBRPM
++ if (missing_exec == MISSING_EXEC_NOT_TRIED)
++ {
++ const char *execfilename = get_exec_file (0);
+
-+ fprintf_unfiltered (gdb_stdlog,
-+ _("Missing separate debuginfo for %s\n"), binary);
-+ if (debug != NULL)
-+ fprintf_unfiltered (gdb_stdlog, _("Try to install the hash file %s\n"),
-+ debug);
++ if (execfilename != NULL)
++ {
++ if (missing_rpm_enlist (execfilename) == 0)
++ missing_exec = MISSING_EXEC_NOT_FOUND;
++ else
++ missing_exec = MISSING_EXEC_ENLISTED;
++ }
++ }
++ if (missing_exec != MISSING_EXEC_ENLISTED)
++ if ((binary[0] == 0 || missing_rpm_enlist (binary) == 0)
++ && (debug == NULL || missing_rpm_enlist (debug) == 0))
++#endif /* HAVE_LIBRPM */
++ {
++ /* We do not collect and flush these messages as each such message
++ already requires its own separate lines. */
++
++ fprintf_unfiltered (gdb_stdlog,
++ _("Missing separate debuginfo for %s\n"), binary);
++ if (debug != NULL)
++ fprintf_unfiltered (gdb_stdlog, _("Try: %s %s\n"),
++#ifdef DNF_DEBUGINFO_INSTALL
++ "dnf"
++#else
++ "yum"
++#endif
++ " --enablerepo='*debug*' install", debug);
++ }
+}
+
/* See build-id.h. */
if (build_id != NULL)
{
if (separate_debug_file_debug)
- printf_unfiltered (_("\nLooking for separate debug info (build-id) for "
- "%s\n"), objfile_name (objfile));
+@@ -214,8 +1338,21 @@ find_separate_debug_file_by_buildid (struct objfile *objfile)
+ _("\nLooking for separate debug info (build-id) for "
+ "%s\n"), objfile_name (objfile));
+ char *build_id_filename_cstr = NULL;
gdb_bfd_ref_ptr abfd (build_id_to_debug_bfd (build_id->size,
/* Prevent looping on a stripped .debug file. */
if (abfd != NULL
&& filename_cmp (bfd_get_filename (abfd.get ()),
-@@ -223,3 +897,22 @@ find_separate_debug_file_by_buildid (struct objfile *objfile)
+@@ -228,3 +1365,22 @@ find_separate_debug_file_by_buildid (struct objfile *objfile)
return std::string ();
}
+
-+extern void _initialize_build_id (void);
++void _initialize_build_id ();
+
+void
-+_initialize_build_id (void)
++_initialize_build_id ()
+{
+ add_setshow_zinteger_cmd ("build-id-verbose", no_class, &build_id_verbose,
+ _("\
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
-@@ -21415,6 +21415,27 @@ information files.
+@@ -21524,6 +21524,27 @@ information files.
@end table
diff --git a/gdb/dwarf2/index-cache.c b/gdb/dwarf2/index-cache.c
--- a/gdb/dwarf2/index-cache.c
+++ b/gdb/dwarf2/index-cache.c
-@@ -95,7 +95,7 @@ index_cache::store (dwarf2_per_objfile *per_objfile)
+@@ -97,7 +97,7 @@ index_cache::store (dwarf2_per_objfile *per_objfile)
return;
/* Get build id of objfile. */
+ const bfd_build_id *build_id = build_id_bfd_shdr_get (obj->obfd);
if (build_id == nullptr)
{
- if (debug_index_cache)
-@@ -113,7 +113,8 @@ index_cache::store (dwarf2_per_objfile *per_objfile)
+ index_cache_debug ("objfile %s has no build id",
+@@ -114,7 +114,8 @@ index_cache::store (dwarf2_per_objfile *per_objfile)
if (dwz != nullptr)
{
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
-@@ -5447,7 +5447,7 @@ get_gdb_index_contents_from_section (objfile *obj, T *section_owner)
+@@ -5476,7 +5476,7 @@ get_gdb_index_contents_from_section (objfile *obj, T *section_owner)
static gdb::array_view<const gdb_byte>
get_gdb_index_contents_from_cache (objfile *obj, dwarf2_per_bfd *dwarf2_per_bfd)
{
if (build_id == nullptr)
return {};
-@@ -5460,7 +5460,7 @@ get_gdb_index_contents_from_cache (objfile *obj, dwarf2_per_bfd *dwarf2_per_bfd)
+@@ -5489,7 +5489,7 @@ get_gdb_index_contents_from_cache (objfile *obj, dwarf2_per_bfd *dwarf2_per_bfd)
static gdb::array_view<const gdb_byte>
get_gdb_index_contents_from_cache_dwz (objfile *obj, dwz_file *dwz)
{
diff --git a/gdb/elfread.c b/gdb/elfread.c
--- a/gdb/elfread.c
+++ b/gdb/elfread.c
-@@ -1272,7 +1272,9 @@ elf_symfile_read (struct objfile *objfile, symfile_add_flags symfile_flags)
+@@ -1270,7 +1270,9 @@ elf_symfile_read (struct objfile *objfile, symfile_add_flags symfile_flags)
&& objfile->separate_debug_objfile == NULL
&& objfile->separate_debug_objfile_backlink == NULL)
{
if (debugfile.empty ())
debugfile = find_separate_debug_file_by_debuglink (objfile);
-@@ -1287,7 +1289,7 @@ elf_symfile_read (struct objfile *objfile, symfile_add_flags symfile_flags)
+@@ -1285,7 +1287,7 @@ elf_symfile_read (struct objfile *objfile, symfile_add_flags symfile_flags)
else
{
has_dwarf2 = false;
if (build_id != nullptr)
{
-@@ -1312,6 +1314,10 @@ elf_symfile_read (struct objfile *objfile, symfile_add_flags symfile_flags)
+@@ -1310,6 +1312,10 @@ elf_symfile_read (struct objfile *objfile, symfile_add_flags symfile_flags)
has_dwarf2 = true;
}
}
diff --git a/gdb/exec.c b/gdb/exec.c
--- a/gdb/exec.c
+++ b/gdb/exec.c
-@@ -237,7 +237,7 @@ validate_exec_file (int from_tty)
+@@ -238,7 +238,7 @@ validate_exec_file (int from_tty)
current_exec_file = get_exec_file (0);
const bfd_build_id *exec_file_build_id
if (exec_file_build_id != nullptr)
{
/* Prepend the target prefix, to force gdb_bfd_open to open the
-@@ -250,7 +250,7 @@ validate_exec_file (int from_tty)
+@@ -251,7 +251,7 @@ validate_exec_file (int from_tty)
if (abfd != nullptr)
{
const bfd_build_id *target_exec_file_build_id
diff --git a/gdb/objfiles.h b/gdb/objfiles.h
--- a/gdb/objfiles.h
+++ b/gdb/objfiles.h
-@@ -812,6 +812,10 @@ struct objfile
+@@ -769,6 +769,10 @@ struct objfile
bool skip_jit_symbol_lookup = false;
};
static struct link_map_offsets *svr4_fetch_link_map_offsets (void);
static int svr4_have_link_map_offsets (void);
-@@ -1348,9 +1349,51 @@ svr4_read_so_list (svr4_info *info, CORE_ADDR lm, CORE_ADDR prev_lm,
+@@ -1248,9 +1249,51 @@ svr4_read_so_list (svr4_info *info, CORE_ADDR lm, CORE_ADDR prev_lm,
continue;
}
diff --git a/gdb/source.c b/gdb/source.c
--- a/gdb/source.c
+++ b/gdb/source.c
-@@ -1178,7 +1178,7 @@ open_source_file (struct symtab *s)
+@@ -1199,7 +1199,7 @@ open_source_file (struct symtab *s)
srcpath += s->filename;
}
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
-@@ -2130,6 +2130,17 @@ proc default_gdb_start { } {
+@@ -141,7 +141,8 @@ if ![info exists INTERNAL_GDBFLAGS] {
+ "-nx" \
+ "-data-directory $BUILD_DATA_DIRECTORY" \
+ {-iex "set height 0"} \
+- {-iex "set width 0"}]]
++ {-iex "set width 0"} \
++ {-iex "set build-id-verbose 0"}]]
+ }
+
+ # The variable gdb_prompt is a regexp which matches the gdb prompt.
+@@ -2200,6 +2201,17 @@ proc default_gdb_start { } {
}
}