diff --git a/binutils/configure b/binutils/configure
index 2e33584e548..ea66a71f1f2 100755
--- a/binutils/configure
+++ b/binutils/configure
@@ -11795,19 +11795,19 @@ $as_echo "$with_debuginfod" >&6; }
 if test "x$with_debuginfod" != xno; then
 
 pkg_failed=no
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libdebuginfod >= 0.179" >&5
-$as_echo_n "checking for libdebuginfod >= 0.179... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libdebuginfod >= 0.188" >&5
+$as_echo_n "checking for libdebuginfod >= 0.188... " >&6; }
 
 if test -n "$DEBUGINFOD_CFLAGS"; then
     pkg_cv_DEBUGINFOD_CFLAGS="$DEBUGINFOD_CFLAGS"
  elif test -n "$PKG_CONFIG"; then
     if test -n "$PKG_CONFIG" && \
-    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libdebuginfod >= 0.179\""; } >&5
-  ($PKG_CONFIG --exists --print-errors "libdebuginfod >= 0.179") 2>&5
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libdebuginfod >= 0.188\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "libdebuginfod >= 0.188") 2>&5
   ac_status=$?
   $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }; then
-  pkg_cv_DEBUGINFOD_CFLAGS=`$PKG_CONFIG --cflags "libdebuginfod >= 0.179" 2>/dev/null`
+  pkg_cv_DEBUGINFOD_CFLAGS=`$PKG_CONFIG --cflags "libdebuginfod >= 0.188" 2>/dev/null`
 		      test "x$?" != "x0" && pkg_failed=yes
 else
   pkg_failed=yes
@@ -11819,12 +11819,12 @@ if test -n "$DEBUGINFOD_LIBS"; then
     pkg_cv_DEBUGINFOD_LIBS="$DEBUGINFOD_LIBS"
  elif test -n "$PKG_CONFIG"; then
     if test -n "$PKG_CONFIG" && \
-    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libdebuginfod >= 0.179\""; } >&5
-  ($PKG_CONFIG --exists --print-errors "libdebuginfod >= 0.179") 2>&5
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libdebuginfod >= 0.188\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "libdebuginfod >= 0.188") 2>&5
   ac_status=$?
   $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }; then
-  pkg_cv_DEBUGINFOD_LIBS=`$PKG_CONFIG --libs "libdebuginfod >= 0.179" 2>/dev/null`
+  pkg_cv_DEBUGINFOD_LIBS=`$PKG_CONFIG --libs "libdebuginfod >= 0.188" 2>/dev/null`
 		      test "x$?" != "x0" && pkg_failed=yes
 else
   pkg_failed=yes
@@ -11869,9 +11869,9 @@ else
         _pkg_short_errors_supported=no
 fi
         if test $_pkg_short_errors_supported = yes; then
-	        DEBUGINFOD_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libdebuginfod >= 0.179" 2>&1`
+	        DEBUGINFOD_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libdebuginfod >= 0.188" 2>&1`
         else
-	        DEBUGINFOD_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libdebuginfod >= 0.179" 2>&1`
+	        DEBUGINFOD_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libdebuginfod >= 0.188" 2>&1`
         fi
 	# Put the nasty error message in config.log where it belongs
 	echo "$DEBUGINFOD_PKG_ERRORS" >&5
diff --git a/config/debuginfod.m4 b/config/debuginfod.m4
index 2c1bfbdb544..d861156a643 100644
--- a/config/debuginfod.m4
+++ b/config/debuginfod.m4
@@ -15,7 +15,7 @@ AC_MSG_CHECKING([whether to use debuginfod])
 AC_MSG_RESULT([$with_debuginfod])
 
 if test "x$with_debuginfod" != xno; then
-  PKG_CHECK_MODULES([DEBUGINFOD], [libdebuginfod >= 0.179],
+  PKG_CHECK_MODULES([DEBUGINFOD], [libdebuginfod >= 0.188],
     [AC_DEFINE([HAVE_LIBDEBUGINFOD], [1], [Define to 1 if debuginfod is enabled.])],
     [if test "x$with_debuginfod" = xyes; then
        AC_MSG_ERROR(["--with-debuginfod was given, but libdebuginfod is missing or unusable."])
diff --git a/gdb/configure b/gdb/configure
index 33677262783..6548714da2e 100755
--- a/gdb/configure
+++ b/gdb/configure
@@ -7032,19 +7032,19 @@ $as_echo "$with_debuginfod" >&6; }
 if test "x$with_debuginfod" != xno; then
 
 pkg_failed=no
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libdebuginfod >= 0.179" >&5
-$as_echo_n "checking for libdebuginfod >= 0.179... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libdebuginfod >= 0.188" >&5
+$as_echo_n "checking for libdebuginfod >= 0.188... " >&6; }
 
 if test -n "$DEBUGINFOD_CFLAGS"; then
     pkg_cv_DEBUGINFOD_CFLAGS="$DEBUGINFOD_CFLAGS"
  elif test -n "$PKG_CONFIG"; then
     if test -n "$PKG_CONFIG" && \
-    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libdebuginfod >= 0.179\""; } >&5
-  ($PKG_CONFIG --exists --print-errors "libdebuginfod >= 0.179") 2>&5
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libdebuginfod >= 0.188\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "libdebuginfod >= 0.188") 2>&5
   ac_status=$?
   $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }; then
-  pkg_cv_DEBUGINFOD_CFLAGS=`$PKG_CONFIG --cflags "libdebuginfod >= 0.179" 2>/dev/null`
+  pkg_cv_DEBUGINFOD_CFLAGS=`$PKG_CONFIG --cflags "libdebuginfod >= 0.188" 2>/dev/null`
 		      test "x$?" != "x0" && pkg_failed=yes
 else
   pkg_failed=yes
@@ -7056,12 +7056,12 @@ if test -n "$DEBUGINFOD_LIBS"; then
     pkg_cv_DEBUGINFOD_LIBS="$DEBUGINFOD_LIBS"
  elif test -n "$PKG_CONFIG"; then
     if test -n "$PKG_CONFIG" && \
-    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libdebuginfod >= 0.179\""; } >&5
-  ($PKG_CONFIG --exists --print-errors "libdebuginfod >= 0.179") 2>&5
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libdebuginfod >= 0.188\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "libdebuginfod >= 0.188") 2>&5
   ac_status=$?
   $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }; then
-  pkg_cv_DEBUGINFOD_LIBS=`$PKG_CONFIG --libs "libdebuginfod >= 0.179" 2>/dev/null`
+  pkg_cv_DEBUGINFOD_LIBS=`$PKG_CONFIG --libs "libdebuginfod >= 0.188" 2>/dev/null`
 		      test "x$?" != "x0" && pkg_failed=yes
 else
   pkg_failed=yes
@@ -7106,9 +7106,9 @@ else
         _pkg_short_errors_supported=no
 fi
         if test $_pkg_short_errors_supported = yes; then
-	        DEBUGINFOD_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libdebuginfod >= 0.179" 2>&1`
+	        DEBUGINFOD_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libdebuginfod >= 0.188" 2>&1`
         else
-	        DEBUGINFOD_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libdebuginfod >= 0.179" 2>&1`
+	        DEBUGINFOD_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libdebuginfod >= 0.188" 2>&1`
         fi
 	# Put the nasty error message in config.log where it belongs
 	echo "$DEBUGINFOD_PKG_ERRORS" >&5
diff --git a/gdb/debuginfod-support.c b/gdb/debuginfod-support.c
index 5f04a2b38ca..c2f4d54256c 100644
--- a/gdb/debuginfod-support.c
+++ b/gdb/debuginfod-support.c
@@ -33,12 +33,14 @@ static cmd_list_element *show_debuginfod_prefix_list;
 static const char debuginfod_on[] = "on";
 static const char debuginfod_off[] = "off";
 static const char debuginfod_ask[] = "ask";
+static const char debuginfod_lazy[] = "lazy";
 
 static const char *debuginfod_enabled_enum[] =
 {
   debuginfod_on,
   debuginfod_off,
   debuginfod_ask,
+  debuginfod_lazy,
   nullptr
 };
 
@@ -79,6 +81,15 @@ debuginfod_exec_query (const unsigned char *build_id,
   return scoped_fd (-ENOSYS);
 }
 
+scoped_fd
+debuginfod_section_query (const unsigned char *build_id,
+			  int build_id_len,
+			  const char *filename,
+			  const char *section_name,
+			  gdb::unique_xmalloc_ptr<char> *destname)
+{
+  return scoped_fd (-ENOSYS);
+}
 #define NO_IMPL _("Support for debuginfod is not compiled into GDB.")
 
 #else
@@ -358,6 +369,51 @@ debuginfod_exec_query (const unsigned char *build_id,
 
   return fd;
 }
+
+/* See debuginfod-support.h  */
+
+scoped_fd
+debuginfod_section_query (const unsigned char *build_id,
+			  int build_id_len,
+			  const char *filename,
+			  const char *section_name,
+			  gdb::unique_xmalloc_ptr<char> *destname)
+{
+  if (debuginfod_enabled != debuginfod_lazy || !debuginfod_is_enabled ())
+    return scoped_fd (-ENOSYS);
+
+  debuginfod_client *c = get_debuginfod_client ();
+
+  if (c == nullptr)
+    return scoped_fd (-ENOMEM);
+
+  char *dname = nullptr;
+  std::string desc = std::string ("section ") + section_name + " for";
+  user_data data (desc.c_str (), filename);
+
+  debuginfod_set_user_data (c, &data);
+  gdb::optional<target_terminal::scoped_restore_terminal_state> term_state;
+  if (target_supports_terminal_ours ())
+    {
+      term_state.emplace ();
+      target_terminal::ours ();
+    }
+
+  scoped_fd fd (debuginfod_find_section (c, build_id, build_id_len,
+					 section_name, &dname));
+  debuginfod_set_user_data (c, nullptr);
+
+  if (fd.get () < 0 && fd.get () != -ENOENT)
+    gdb_printf (_("Download failed: %s. " \
+		  "Continuing without section %s for %ps.\n"),
+		safe_strerror (-fd.get ()), section_name,
+		styled_string (file_name_style.style (),  filename));
+
+  if (fd.get () >= 0 && destname != nullptr)
+    destname->reset (dname);
+
+  return fd;
+}
 #endif
 
 /* Set callback for "set debuginfod enabled".  */
diff --git a/gdb/debuginfod-support.h b/gdb/debuginfod-support.h
index 5b1c1cb91f4..5294b65cac4 100644
--- a/gdb/debuginfod-support.h
+++ b/gdb/debuginfod-support.h
@@ -78,4 +78,27 @@ extern scoped_fd debuginfod_exec_query (const unsigned char *build_id,
 					const char *filename,
 					gdb::unique_xmalloc_ptr<char>
 					  *destname);
+
+/* Query debuginfod servers for the binary contents of a ELF/DWARF section
+   from a file matching BUILD_ID.  BUILD_ID can be given as a binary blob
+   or a null-terminated string.  If given as a binary blob, BUILD_ID_LEN
+   should be the number of bytes.  If given as a null-terminated string,
+   BUILD_ID_LEN should be 0.
+
+   FILENAME should be the name or path associated with the file matching
+   BUILD_ID.  It is used for printing messages to the user.
+
+   SECTION_NAME should be the name of an ELF/DWARF section beginning
+   with '.'.
+
+   If the file is successfully retrieved, its path on the local machine
+   is stored in DESTNAME.  If GDB is not built with debuginfod, this
+   function returns -ENOSYS.  */
+
+extern scoped_fd debuginfod_section_query (const unsigned char *build_id,
+					   int build_id_len,
+					   const char *filename,
+					   const char *section_name,
+					   gdb::unique_xmalloc_ptr<char>
+					     *destname);
 #endif /* DEBUGINFOD_SUPPORT_H */
diff --git a/gdb/dwarf2/index-cache.c b/gdb/dwarf2/index-cache.c
index 6de58592050..cc170e8abfe 100644
--- a/gdb/dwarf2/index-cache.c
+++ b/gdb/dwarf2/index-cache.c
@@ -222,6 +222,33 @@ index_cache::lookup_gdb_index (const bfd_build_id *build_id,
   return {};
 }
 
+/* See dwarf-index-cache.h.  */
+
+gdb::array_view<const gdb_byte>
+index_cache::lookup_gdb_index_debuginfod (const char *index_path,
+					  std::unique_ptr<index_cache_resource> *resource)
+{
+  try
+    {
+      /* Try to map that file.  */
+      index_cache_resource_mmap *mmap_resource
+	= new index_cache_resource_mmap (index_path);
+
+      /* Hand the resource to the caller.  */
+      resource->reset (mmap_resource);
+
+      return gdb::array_view<const gdb_byte>
+	  ((const gdb_byte *) mmap_resource->mapping.get (),
+	   mmap_resource->mapping.size ());
+    }
+  catch (const gdb_exception_error &except)
+    {
+      warning (_("Unable to read %s: %s"), index_path, except.what ());
+    }
+
+  return {};
+}
+
 #else /* !HAVE_SYS_MMAN_H */
 
 /* See dwarf-index-cache.h.  This is a no-op on unsupported systems.  */
@@ -233,6 +260,12 @@ index_cache::lookup_gdb_index (const bfd_build_id *build_id,
   return {};
 }
 
+gdb::array_view<const gdb_byte>
+index_cache::lookup_gdb_index_debuginfod (const char *index_path,
+					  std::unique_ptr<index_cache_resource> *resource)
+{
+  return {};
+}
 #endif
 
 /* See dwarf-index-cache.h.  */
diff --git a/gdb/dwarf2/index-cache.h b/gdb/dwarf2/index-cache.h
index 6366a9a9360..ecf74684c67 100644
--- a/gdb/dwarf2/index-cache.h
+++ b/gdb/dwarf2/index-cache.h
@@ -65,6 +65,19 @@ class index_cache
   lookup_gdb_index (const bfd_build_id *build_id,
 		    std::unique_ptr<index_cache_resource> *resource);
 
+  /* Look for an index file located at INDEX_PATH in the debuginfod cache.
+     Unlike lookup_gdb_index, this function does not exit early if the
+     index cache has not been enabled.
+
+     If found, return the contents as an array_view and store the underlying
+     resources (allocated memory, mapped file, etc) in RESOURCE.  The returned
+     array_view is valid as long as RESOURCE is not destroyed.
+
+     If no matching index file is found, return an empty array view.  */
+  gdb::array_view<const gdb_byte>
+  lookup_gdb_index_debuginfod (const char *index_path,
+			       std::unique_ptr<index_cache_resource> *resource);
+
   /* Return the number of cache hits.  */
   unsigned int n_hits () const
   { return m_n_hits; }
diff --git a/gdb/dwarf2/public.h b/gdb/dwarf2/public.h
index a9d4682c856..9971654c62f 100644
--- a/gdb/dwarf2/public.h
+++ b/gdb/dwarf2/public.h
@@ -40,4 +40,6 @@ extern void dwarf2_initialize_objfile (struct objfile *objfile);
 
 extern void dwarf2_build_frame_info (struct objfile *);
 
+extern bool dwarf2_has_separate_index (struct objfile *);
+
 #endif /* DWARF2_PUBLIC_H */
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 60e120a9d76..fff4b1eb136 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -93,6 +93,9 @@
 #include "split-name.h"
 #include "gdbsupport/parallel-for.h"
 #include "gdbsupport/thread-pool.h"
+#include "symfile.h"
+#include "inferior.h"
+#include "debuginfod-support.h"
 
 /* When == 1, print basic high level tracing messages.
    When > 1, be more verbose.
@@ -1846,6 +1849,10 @@ struct dwarf2_base_index_functions : public quick_symbol_functions
      CORE_ADDR pc, struct obj_section *section, int warn_if_readin)
        override final;
 
+  struct compunit_symtab *_find_pc_sect_compunit_symtab
+    (struct objfile *objfile, struct bound_minimal_symbol msymbol,
+     CORE_ADDR pc, struct obj_section *section, int warn_if_readin);
+
   struct compunit_symtab *find_compunit_symtab_by_address
     (struct objfile *objfile, CORE_ADDR address) override
   {
@@ -1911,6 +1918,16 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
      block_search_flags search_flags,
      domain_enum domain,
      enum search_domain kind) override;
+
+  bool _expand_symtabs_matching
+    (struct objfile *objfile,
+     gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
+     const lookup_name_info *lookup_name,
+     gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
+     gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
+     block_search_flags search_flags,
+     domain_enum domain,
+     enum search_domain kind);
 };
 
 struct dwarf2_debug_names_index : public dwarf2_base_index_functions
@@ -2677,28 +2694,31 @@ dwarf2_read_gdb_index
 
   /* If there is a .dwz file, read it so we can get its CU list as
      well.  */
-  dwz = dwarf2_get_dwz_file (per_bfd);
-  if (dwz != NULL)
+  if (get_gdb_index_contents_dwz != nullptr)
     {
-      struct mapped_index dwz_map;
-      const gdb_byte *dwz_types_ignore;
-      offset_type dwz_types_elements_ignore;
+      dwz = dwarf2_get_dwz_file (per_bfd);
+      if (dwz != NULL)
+	{
+	  struct mapped_index dwz_map;
+	  const gdb_byte *dwz_types_ignore;
+	  offset_type dwz_types_elements_ignore;
 
-      gdb::array_view<const gdb_byte> dwz_index_content
-	= get_gdb_index_contents_dwz (objfile, dwz);
+	  gdb::array_view<const gdb_byte> dwz_index_content
+	    = get_gdb_index_contents_dwz (objfile, dwz);
 
-      if (dwz_index_content.empty ())
-	return 0;
+	  if (dwz_index_content.empty ())
+	    return 0;
 
-      if (!read_gdb_index_from_buffer (bfd_get_filename (dwz->dwz_bfd.get ()),
-				       1, dwz_index_content, &dwz_map,
-				       &dwz_list, &dwz_list_elements,
-				       &dwz_types_ignore,
-				       &dwz_types_elements_ignore))
-	{
-	  warning (_("could not read '.gdb_index' section from %s; skipping"),
-		   bfd_get_filename (dwz->dwz_bfd.get ()));
-	  return 0;
+	  if (!read_gdb_index_from_buffer (bfd_get_filename (dwz->dwz_bfd.get ()),
+					   1, dwz_index_content, &dwz_map,
+					   &dwz_list, &dwz_list_elements,
+					   &dwz_types_ignore,
+					   &dwz_types_elements_ignore))
+	    {
+	      warning (_("could not read '.gdb_index' section from %s; skipping"),
+		       bfd_get_filename (dwz->dwz_bfd.get ()));
+	      return 0;
+	    }
 	}
     }
 
@@ -4192,8 +4212,10 @@ dw_expand_symtabs_matching_file_matcher
     }
 }
 
+static bool read_full_dwarf_from_debuginfod (struct objfile *);
+
 bool
-dwarf2_gdb_index::expand_symtabs_matching
+dwarf2_gdb_index::_expand_symtabs_matching
     (struct objfile *objfile,
      gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
      const lookup_name_info *lookup_name,
@@ -4242,6 +4264,44 @@ dwarf2_gdb_index::expand_symtabs_matching
   return result;
 }
 
+bool
+dwarf2_gdb_index::expand_symtabs_matching
+    (struct objfile *objfile,
+     gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
+     const lookup_name_info *lookup_name,
+     gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
+     gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
+     block_search_flags search_flags,
+     domain_enum domain,
+     enum search_domain kind)
+{
+  if (objfile->flags & OBJF_READNEVER)
+    return false;
+
+  try
+    {
+      return _expand_symtabs_matching (objfile, file_matcher, lookup_name,
+				       symbol_matcher, expansion_notify,
+				       search_flags, domain, kind);
+    }
+  catch (gdb_exception e)
+    {
+      if ((objfile->flags & OBJF_INDEX_READLATER) == 0)
+	{
+	  exception_print (gdb_stderr, e);
+	  return false;
+	}
+
+      /* Objfile is a stub holding only index information.  Try to reinitialize
+	 objfile with the full debuginfo.  */
+      if (!read_full_dwarf_from_debuginfod (objfile))
+	  return false;
+      return _expand_symtabs_matching (objfile, file_matcher, lookup_name,
+				       symbol_matcher, expansion_notify,
+				       search_flags, domain, kind);
+    }
+}
+
 /* A helper for dw2_find_pc_sect_compunit_symtab which finds the most specific
    symtab.  */
 
@@ -4281,7 +4341,7 @@ dwarf2_base_index_functions::find_per_cu (dwarf2_per_bfd *per_bfd,
 }
 
 struct compunit_symtab *
-dwarf2_base_index_functions::find_pc_sect_compunit_symtab
+dwarf2_base_index_functions::_find_pc_sect_compunit_symtab
      (struct objfile *objfile,
       struct bound_minimal_symbol msymbol,
       CORE_ADDR pc,
@@ -4312,6 +4372,40 @@ dwarf2_base_index_functions::find_pc_sect_compunit_symtab
   return result;
 }
 
+struct compunit_symtab *
+dwarf2_base_index_functions::find_pc_sect_compunit_symtab
+     (struct objfile *objfile,
+      struct bound_minimal_symbol msymbol,
+      CORE_ADDR pc,
+      struct obj_section *section,
+      int warn_if_readin)
+{
+  if (objfile->flags & OBJF_READNEVER)
+    return nullptr;
+
+  try
+    {
+      return _find_pc_sect_compunit_symtab (objfile, msymbol, pc,
+					    section, warn_if_readin);
+    }
+  catch (gdb_exception e)
+    {
+      if ((objfile->flags & OBJF_INDEX_READLATER) == 0)
+	{
+	  exception_print (gdb_stderr, e);
+	  return nullptr;
+	}
+
+      /* Objfile is a stub holding only index information.  Try to reinitialize
+	 objfile with the full debuginfo.  */
+      if (!read_full_dwarf_from_debuginfod (objfile))
+	return nullptr;
+
+      return _find_pc_sect_compunit_symtab (objfile, msymbol, pc,
+					    section, warn_if_readin);
+    }
+}
+
 void
 dwarf2_base_index_functions::map_symbol_filenames
      (struct objfile *objfile,
@@ -5417,6 +5511,180 @@ dwarf2_initialize_objfile (struct objfile *objfile)
   objfile->qf.push_front (make_cooked_index_funcs ());
 }
 
+/* Query debuginfod for the .gdb_index matching OBJFILE's build-id.  Return the
+   contents if successful.  */
+
+static gdb::array_view<const gdb_byte>
+get_gdb_index_contents_from_debuginfod (objfile *objfile, dwarf2_per_bfd *per_bfd)
+{
+  const bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ());
+  if (build_id == nullptr)
+    return {};
+
+  gdb::unique_xmalloc_ptr<char> index_path;
+  scoped_fd fd = debuginfod_section_query (build_id->data, build_id->size,
+					   bfd_get_filename
+					     (objfile->obfd.get ()),
+					   ".gdb_index",
+					   &index_path);
+  if (fd.get () < 0)
+    return {};
+
+  return global_index_cache.lookup_gdb_index_debuginfod
+    (index_path.get (), &per_bfd->index_cache_res);
+}
+
+/* If OBJFILE is a stub holding only information from a .gdb_index, then attempt
+   to download the full debuginfo and reinitialize OBJFILE with it.  */
+
+static bool
+read_full_dwarf_from_debuginfod (struct objfile *objfile)
+{
+  if ((objfile->flags & OBJF_INDEX_READLATER) == 0)
+    return false;
+
+  const struct bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ());
+  if (build_id == nullptr)
+    return false;
+
+  const char *filename = bfd_get_filename (objfile->obfd.get ());
+  dwarf2_per_bfd *per_bfd;
+  dwarf2_per_objfile *per_objfile;
+  gdb_bfd_ref_ptr debug_bfd;
+  gdb::unique_xmalloc_ptr<char> symfile_path;
+
+  scoped_fd fd (debuginfod_debuginfo_query (build_id->data,
+					    build_id->size,
+					    filename,
+					    &symfile_path));
+
+  if (fd.get () < 0)
+    goto fail;
+
+  /* File successfully retrieved from server.  Open as a bfd.  */
+  debug_bfd = symfile_bfd_open (symfile_path.get ());
+
+  if (debug_bfd == nullptr
+      || !build_id_verify (debug_bfd.get (), build_id->size, build_id->data))
+    {
+      warning (_("File \"%s\" from debuginfod cannot be opened as bfd"),
+	       filename);
+      goto fail;
+    }
+
+  /* Fill in objfile's missing information using the debuginfo.  */
+  objfile->reinit (debug_bfd.release ());
+
+  /* Create new per_bfd and per_objfile.  Placeholders based on the
+     separate_debug_objfile_backlink were deleted during reinit.  */
+  per_bfd = new dwarf2_per_bfd (objfile->obfd.get (), nullptr, false);
+  dwarf2_per_bfd_objfile_data_key.set (objfile, per_bfd);
+  per_objfile = dwarf2_objfile_data_key.emplace (objfile, objfile, per_bfd);
+
+  objfile->flags &= ~OBJF_INDEX_READLATER;
+
+  /* Have to attach the separate index again.  The dwz will be downloaded at
+     this point if applicable.  */
+  if (dwarf2_read_gdb_index (per_objfile,
+			     get_gdb_index_contents_from_debuginfod,
+			     get_gdb_index_contents_from_section<dwz_file>))
+    {
+      dwarf_read_debug_printf ("found gdb index from debuginfod");
+      objfile->qf.push_front (per_objfile->per_bfd->index_table->make_quick_functions ());
+
+      objfile->flags &= ~OBJF_NOT_FILENAME;
+      return true;
+    }
+
+fail:
+  objfile->flags |= OBJF_READNEVER;
+  return false;
+}
+
+/* Query debuginfod for the separate .gdb_index associated with OBJFILE.  If
+   successful, create an objfile to hold the .gdb_index information and act
+   as a placeholder until the full debuginfo needs to be downloaded.  */
+
+bool
+dwarf2_has_separate_index (struct objfile *objfile)
+{
+  if (objfile->flags & OBJF_MAINLINE
+      || objfile->separate_debug_objfile_backlink != nullptr)
+    return 0;
+
+  if (objfile->flags & OBJF_INDEX_READLATER)
+    return 1;
+
+  gdb::unique_xmalloc_ptr<char> index_path;
+  const bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ());
+
+  scoped_fd fd = debuginfod_section_query (build_id->data,
+					   build_id->size,
+					   bfd_get_filename
+					     (objfile->obfd.get ()),
+					   ".gdb_index",
+					   &index_path);
+  if (fd.get () >= 0)
+    {
+      /* We have a separate .gdb_index file so a separate debuginfo file
+	 should exist.  We just don't want to read it until we really
+	 have to.  Create an objfile to own the index information and to
+	 act as a placeholder for the debuginfo that we have the option
+	 of aquiring later.  */
+      gdb_bfd_ref_ptr abfd (gdb_bfd_open (objfile_filename (objfile), gnutarget));
+      if (abfd == nullptr)
+	return false;
+
+      dwarf2_per_bfd_objfile_data_key.clear (objfile);
+      dwarf2_objfile_data_key.clear (objfile);
+
+      symbol_file_add_from_index
+	(abfd, current_inferior ()->symfile_flags | SYMFILE_NO_READ, objfile);
+
+      dwarf2_per_bfd *per_bfd;
+      dwarf2_per_objfile *per_objfile = get_dwarf2_per_objfile (objfile);
+
+      if (per_objfile == nullptr)
+	{
+	  per_bfd = dwarf2_per_bfd_objfile_data_key.get (objfile);
+	  if (per_bfd == nullptr)
+	    {
+	      per_bfd = new dwarf2_per_bfd (objfile->obfd.get (), nullptr, false);
+	      dwarf2_per_bfd_objfile_data_key.set (objfile, per_bfd);
+	    }
+	  per_objfile = dwarf2_objfile_data_key.emplace (objfile, objfile, per_bfd);
+	}
+
+      struct objfile *stub = objfile->separate_debug_objfile;
+      per_objfile = get_dwarf2_per_objfile (stub);
+      if (per_objfile == nullptr)
+	{
+	  per_bfd = dwarf2_per_bfd_objfile_data_key.get (stub);
+	  if (per_bfd == nullptr)
+	    {
+	      per_bfd = new dwarf2_per_bfd (stub->obfd.get (), nullptr, false);
+	      dwarf2_per_bfd_objfile_data_key.set (stub, per_bfd);
+	    }
+	  per_objfile = dwarf2_objfile_data_key.emplace (stub, stub, per_bfd);
+	}
+
+      if (dwarf2_read_gdb_index (per_objfile,
+				 get_gdb_index_contents_from_debuginfod,
+				 nullptr))
+	{
+	  dwarf_read_debug_printf ("found .gdb_index from debuginfod");
+	  stub->qf.push_front (per_bfd->index_table->make_quick_functions ());
+	  return 1;
+	}
+
+      /* Unable to use the index.  Delete the stub.  */
+      objfile->flags &= ~OBJF_INDEX_READLATER;
+      stub->unlink ();
+    }
+
+  return 0;
+}
+
 
 
 /* Build a partial symbol table.  */
diff --git a/gdb/dwarf2/section.c b/gdb/dwarf2/section.c
index 32c86cc5d8d..4156ed2fdcb 100644
--- a/gdb/dwarf2/section.c
+++ b/gdb/dwarf2/section.c
@@ -54,7 +54,9 @@ dwarf2_section_info::get_bfd_owner () const
       section = get_containing_section ();
       gdb_assert (!section->is_virtual);
     }
-  gdb_assert (section->s.section != nullptr);
+  if (section->s.section == nullptr)
+    throw_error (NOT_FOUND_ERROR,
+		 _("Can't find owner of DWARF2 section."));
   return section->s.section->owner;
 }
 
diff --git a/gdb/elfread.c b/gdb/elfread.c
index 64aeb239670..c229575d1b3 100644
--- a/gdb/elfread.c
+++ b/gdb/elfread.c
@@ -1225,7 +1225,7 @@ elf_symfile_read_dwarf2 (struct objfile *objfile,
 	  symbol_file_add_separate (debug_bfd, debugfile.c_str (),
 				    symfile_flags, objfile);
 	}
-      else
+      else if (!dwarf2_has_separate_index (objfile))
 	{
 	  has_dwarf2 = false;
 	  const struct bfd_build_id *build_id
diff --git a/gdb/objfile-flags.h b/gdb/objfile-flags.h
index b2e07110571..df9bf27d778 100644
--- a/gdb/objfile-flags.h
+++ b/gdb/objfile-flags.h
@@ -68,6 +68,10 @@ enum objfile_flag : unsigned
     /* User requested that we do not read this objfile's symbolic
        information.  */
     OBJF_READNEVER = 1 << 7,
+
+    /* This objfile only holds information from an index.  It should
+       be reinitialized with full debuginfo before expanding symtabs.  */
+    OBJF_INDEX_READLATER = 1 << 8,
   };
 
 DEF_ENUM_FLAGS_TYPE (enum objfile_flag, objfile_flags);
diff --git a/gdb/objfiles.c b/gdb/objfiles.c
index 09aba0f80f0..c2c16c97758 100644
--- a/gdb/objfiles.c
+++ b/gdb/objfiles.c
@@ -53,6 +53,7 @@
 #include "gdb_bfd.h"
 #include "btrace.h"
 #include "gdbsupport/pathstuff.h"
+#include "symfile.h"
 
 #include <algorithm>
 #include <vector>
@@ -299,6 +300,16 @@ build_objfile_section_table (struct objfile *objfile)
 			   objfile, 1);
 }
 
+void
+objfile::reinit (struct bfd *abfd)
+{
+  if ((flags & OBJF_INDEX_READLATER) == 0)
+    return;
+
+  this->obfd.reset (abfd);
+  deferred_read_symbols (this, 0);
+}
+
 /* Given a pointer to an initialized bfd (ABFD) and some flag bits,
    initialize the new objfile as best we can and link it into the list
    of all known objfiles.
@@ -455,6 +466,11 @@ objfile::make (gdb_bfd_ref_ptr bfd_, const char *name_, objfile_flags flags_,
   if (parent != nullptr)
     add_separate_debug_objfile (result, parent);
 
+  /* Objfile was initialized using only an index.  Borrow offsets from the
+     parent until debuginfo is read.  */
+  if (flags_ & OBJF_INDEX_READLATER)
+    result->section_offsets = parent->section_offsets;
+
   current_program_space->add_objfile (std::unique_ptr<objfile> (result),
 				      parent);
 
diff --git a/gdb/objfiles.h b/gdb/objfiles.h
index 9a152cbc387..971fa7d587d 100644
--- a/gdb/objfiles.h
+++ b/gdb/objfiles.h
@@ -497,6 +497,11 @@ struct objfile
   /* See quick_symbol_functions.  */
   struct symtab *find_last_source_symtab ();
 
+  /* Reinitialize this objfile using ABFD.  Objfile should have been originally
+     initialized using a separate index from ABFD.  Updates this objfile with
+     ABFD's symbols and section information.  */
+  void reinit (struct bfd *abfd);
+
   /* See quick_symbol_functions.  */
   void forget_cached_source_info ();
 
diff --git a/gdb/symfile.c b/gdb/symfile.c
index eb27668f9d3..7edd50d92a2 100644
--- a/gdb/symfile.c
+++ b/gdb/symfile.c
@@ -1074,6 +1074,14 @@ symbol_file_add_with_addrs (const gdb_bfd_ref_ptr &abfd, const char *name,
     flags |= OBJF_MAINLINE;
   objfile = objfile::make (abfd, name, flags, parent);
 
+  if (objfile->flags & OBJF_INDEX_READLATER)
+    {
+      /* objfile was initialized only using a separate index so don't
+	 try to read symbols yet.  */
+      gdb::observers::new_objfile.notify (objfile);
+      return objfile;
+    }
+
   /* We either created a new mapped symbol table, mapped an existing
      symbol table file which has not had initial symbol reading
      performed, or need to read an unmapped symbol table.  */
@@ -1135,6 +1143,29 @@ symbol_file_add_with_addrs (const gdb_bfd_ref_ptr &abfd, const char *name,
   return (objfile);
 }
 
+/* We know a separate debuginfo file should exist, but we don't want to
+   read it yet.  Infer some of it's properties from the parent objfile.  */
+
+void
+symbol_file_add_from_index (const gdb_bfd_ref_ptr &bfd,
+			    symfile_add_flags symfile_flags,
+			    struct objfile *parent)
+{
+  section_addr_info sap = build_section_addr_info_from_objfile (parent);
+
+  symbol_file_add_with_addrs
+    (bfd, "<separate .gdb_index stub>", symfile_flags, &sap,
+     (parent->flags & (OBJF_REORDERED | OBJF_SHARED | OBJF_READNOW
+		      | OBJF_USERLOADED | OBJF_MAINLINE | OBJF_PSYMTABS_READ))
+		   | OBJF_INDEX_READLATER | OBJF_NOT_FILENAME,
+     parent);
+
+  objfile *result = parent->separate_debug_objfile;
+  init_objfile_sect_indices (result);
+
+  return;
+}
+
 /* Add BFD as a separate debug file for OBJFILE.  For NAME description
    see the objfile constructor.  */
 
@@ -2415,6 +2446,57 @@ remove_symbol_file_command (const char *args, int from_tty)
   clear_symtab_users (0);
 }
 
+/* Read a separate debuginfo OBJFILE that was originally initialized using
+   only an index and section information from its parent file.  */
+
+void
+deferred_read_symbols (struct objfile *objfile, int from_tty)
+{
+  gdb_assert (objfile->flags & OBJF_INDEX_READLATER);
+
+  /* Nuke all the state that we will re-read.  */
+  objfile->registry_fields.clear_registry ();
+
+  objfile->sections = NULL;
+  objfile->sect_index_bss = -1;
+  objfile->sect_index_data = -1;
+  objfile->sect_index_rodata = -1;
+  objfile->sect_index_text = -1;
+  objfile->compunit_symtabs = NULL;
+  objfile->template_symbols = NULL;
+
+  {
+    gdb_bfd_ref_ptr obfd = objfile->obfd;
+    const char *obfd_filename;
+
+    obfd_filename = bfd_get_filename (objfile->obfd.get ());
+    /* Open the new BFD before freeing the old one, so that
+       the filename remains live.  */
+    gdb_bfd_ref_ptr temp (gdb_bfd_open (obfd_filename, gnutarget));
+    objfile->obfd = std::move (temp);
+    if (objfile->obfd == NULL)
+      error (_("Can't open %s to read symbols."), obfd_filename);
+  }
+
+  std::string original_name = objfile->original_name;
+
+  /* bfd_openr sets cacheable to true, which is what we want.  */
+  if (!bfd_check_format (objfile->obfd.get (), bfd_object))
+    error (_("Can't read symbols from %s: %s."), objfile_name (objfile),
+	   bfd_errmsg (bfd_get_error ()));
+
+  objfile->original_name
+    = obstack_strdup (&objfile->objfile_obstack, original_name);
+
+  objfile_set_sym_fns (objfile, find_sym_fns (objfile->obfd.get ()));
+  build_objfile_section_table (objfile);
+  (*objfile->sf->sym_init) (objfile);
+  init_objfile_sect_indices (objfile);
+
+  read_symbols (objfile, 0);
+  objfile->mtime = bfd_get_mtime (objfile->obfd.get ());
+}
+
 /* Re-read symbols if a symbol-file has changed.  */
 
 void
diff --git a/gdb/symfile.h b/gdb/symfile.h
index ffd1acddfdb..9b42ec64aeb 100644
--- a/gdb/symfile.h
+++ b/gdb/symfile.h
@@ -241,6 +241,9 @@ extern struct objfile *symbol_file_add_from_bfd (const gdb_bfd_ref_ptr &,
 extern void symbol_file_add_separate (const gdb_bfd_ref_ptr &, const char *,
 				      symfile_add_flags, struct objfile *);
 
+extern void symbol_file_add_from_index (const gdb_bfd_ref_ptr &,
+					symfile_add_flags, struct objfile *);
+
 extern std::string find_separate_debug_file_by_debuglink (struct objfile *);
 
 /* Build (allocate and populate) a section_addr_info struct from an
diff --git a/gdb/symtab.h b/gdb/symtab.h
index 4f3e84bbbe9..b5d266ae39b 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -2206,6 +2206,8 @@ extern bool find_pc_line_pc_range (CORE_ADDR, CORE_ADDR *, CORE_ADDR *);
 
 extern void reread_symbols (int from_tty);
 
+extern void deferred_read_symbols (struct objfile *, int from_tty);
+
 /* Look up a type named NAME in STRUCT_DOMAIN in the current language.
    The type returned must not be opaque -- i.e., must have at least one field
    defined.  */
