diff --git a/gdb/dwarf2/line-header.c b/gdb/dwarf2/line-header.c
index 9d74c8fe75b..7c51fb5bb12 100644
--- a/gdb/dwarf2/line-header.c
+++ b/gdb/dwarf2/line-header.c
@@ -113,6 +113,156 @@ read_checked_initial_length_and_offset (bfd *abfd, const gdb_byte *buf,
   return length;
 }
 
+
+/* Like read_formatted_entries but the .debug_line and .debug_line_str
+   are stored in LINE_BUFP and LINE_STR_DATA.  This is used for cases
+   where these sections are read from separate files without necessarily
+   having access to the entire debuginfo file they originate from.  */
+
+static void
+read_formatted_entries_separate
+  (bfd *parent_bfd, const gdb_byte **line_bufp,
+   const gdb::array_view<const gdb_byte> line_str_data,
+   struct line_header *lh,
+   unsigned int offset_size,
+   void (*callback) (struct line_header *lh,
+		     const char *name,
+		     dir_index d_index,
+		     unsigned int mod_time,
+		     unsigned int length))
+{
+  gdb_byte format_count, formati;
+  ULONGEST data_count, datai;
+  const gdb_byte *buf = *line_bufp;
+  const gdb_byte *str_buf = line_str_data.data ();
+  const gdb_byte *format_header_data;
+  unsigned int bytes_read;
+
+  format_count = read_1_byte (parent_bfd, buf);
+  buf += 1;
+  format_header_data = buf;
+  for (formati = 0; formati < format_count; formati++)
+    {
+      read_unsigned_leb128 (parent_bfd, buf, &bytes_read);
+      buf += bytes_read;
+      read_unsigned_leb128 (parent_bfd, buf, &bytes_read);
+      buf += bytes_read;
+    }
+
+  data_count = read_unsigned_leb128 (parent_bfd, buf, &bytes_read);
+  buf += bytes_read;
+  for (datai = 0; datai < data_count; datai++)
+    {
+      const gdb_byte *format = format_header_data;
+      struct file_entry fe;
+
+      for (formati = 0; formati < format_count; formati++)
+	{
+	  ULONGEST content_type = read_unsigned_leb128 (parent_bfd, format, &bytes_read);
+	  format += bytes_read;
+
+	  ULONGEST form  = read_unsigned_leb128 (parent_bfd, format, &bytes_read);
+	  format += bytes_read;
+
+	  gdb::optional<const char *> string;
+	  gdb::optional<unsigned int> uint;
+
+	  switch (form)
+	    {
+	    case DW_FORM_string:
+	      string.emplace (read_direct_string (parent_bfd, buf, &bytes_read));
+	      buf += bytes_read;
+	      break;
+
+	    case DW_FORM_line_strp:
+	      {
+		if (line_str_data.empty ())
+		  error (_("Dwarf Error: DW_FORM_line_strp used without " \
+			   "required section"));
+		if (line_str_data.size () <= offset_size)
+		  error (_("Dwarf Error: DW_FORM_line_strp pointing outside " \
+			   "of section .debug_line"));
+
+		ULONGEST str_offset = read_offset (parent_bfd, buf, offset_size);
+
+		const char *str;
+		if (str_buf[str_offset] == '\0')
+		  str = nullptr;
+		else
+		  str = (const char *) (str_buf + str_offset);
+		string.emplace (str);
+		buf += offset_size;
+		break;
+	      }
+
+	    case DW_FORM_data1:
+	      uint.emplace (read_1_byte (parent_bfd, buf));
+	      buf += 1;
+	      break;
+
+	    case DW_FORM_data2:
+	      uint.emplace (read_2_bytes (parent_bfd, buf));
+	      buf += 2;
+	      break;
+
+	    case DW_FORM_data4:
+	      uint.emplace (read_4_bytes (parent_bfd, buf));
+	      buf += 4;
+	      break;
+
+	    case DW_FORM_data8:
+	      uint.emplace (read_8_bytes (parent_bfd, buf));
+	      buf += 8;
+	      break;
+
+	    case DW_FORM_data16:
+	      /*  This is used for MD5, but file_entry does not record MD5s. */
+	      buf += 16;
+	      break;
+
+	    case DW_FORM_udata:
+	      uint.emplace (read_unsigned_leb128 (parent_bfd, buf, &bytes_read));
+	      buf += bytes_read;
+	      break;
+
+	    case DW_FORM_block:
+	      /* It is valid only for DW_LNCT_timestamp which is ignored by
+		 current GDB.  */
+	      break;
+	    }
+
+	  switch (content_type)
+	    {
+	    case DW_LNCT_path:
+	      if (string.has_value ())
+		fe.name = *string;
+	      break;
+	    case DW_LNCT_directory_index:
+	      if (uint.has_value ())
+		fe.d_index = (dir_index) *uint;
+	      break;
+	    case DW_LNCT_timestamp:
+	      if (uint.has_value ())
+		fe.mod_time = *uint;
+	      break;
+	    case DW_LNCT_size:
+	      if (uint.has_value ())
+		fe.length = *uint;
+	      break;
+	    case DW_LNCT_MD5:
+	      break;
+	    default:
+	      complaint (_("Unknown format content type %s"),
+			 pulongest (content_type));
+	    }
+	}
+
+      callback (lh, fe.name, fe.d_index, fe.mod_time, fe.length);
+    }
+
+  *line_bufp = buf;
+}
+
 /* Read directory or file name entry format, starting with byte of
    format count entries, ULEB128 pairs of entry formats, ULEB128 of
    entries count and the entries themselves in the described entry
@@ -247,6 +397,188 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
   *bufp = buf;
 }
 
+/* See line-header.h.  */
+
+line_header_up
+dwarf_decode_line_header_separate (bfd *parent_bfd,
+				   gdb::array_view<const gdb_byte>
+				     line_data,
+				   gdb::array_view<const gdb_byte>
+				     line_str_data,
+				   const gdb_byte **debug_line_ptr)
+{
+  const gdb_byte *line_ptr, *buf;
+  unsigned int bytes_read, offset_size;
+  int i;
+  const char *cur_dir, *cur_file;
+
+  buf = *debug_line_ptr;
+
+  /* Make sure that at least there's room for the total_length field.
+     That could be 12 bytes long, but we're just going to fudge that.  */
+  if (buf + 4 >= line_data.data () + line_data.size ())
+    {
+      dwarf2_statement_list_fits_in_line_number_section_complaint (); //  need to print
+      return 0;
+    }
+
+  /* We don't have access to the CU DIE yet so we don't know the comp_dir.  */
+  line_header_up lh (new line_header (nullptr));
+
+  lh->sect_off = (sect_offset) (buf - line_data.data ());
+  lh->offset_in_dwz = false;
+
+  line_ptr = buf;
+
+  /* Read in the header.  */
+
+  LONGEST unit_length = read_initial_length (parent_bfd, buf, &bytes_read);
+  offset_size = (bytes_read == 4) ? 4 : 8;
+
+  line_ptr += bytes_read;
+
+  if (line_ptr + unit_length > buf + line_data.size ())
+    {
+      dwarf2_statement_list_fits_in_line_number_section_complaint ();
+      return 0;
+    }
+
+  const gdb_byte *start_here = line_ptr;
+
+  lh->statement_program_end = start_here + unit_length;
+  lh->version = read_2_bytes (parent_bfd, line_ptr);
+  line_ptr += 2;
+  if (lh->version > 5)
+    {
+      /* This is a version we don't understand.  The format could have
+	 changed in ways we don't handle properly so just punt.  */
+      complaint (_("unsupported version in .debug_line section"));
+      return nullptr;
+    }
+  if (lh->version >= 5)
+    {
+      gdb_byte segment_selector_size;
+
+      /* Skip address size.  */
+      read_1_byte (parent_bfd, line_ptr);
+      line_ptr += 1;
+
+      segment_selector_size = read_1_byte (parent_bfd, line_ptr);
+      line_ptr += 1;
+      if (segment_selector_size != 0)
+	{
+	  complaint (_("unsupported segment selector size %u "
+		       "in .debug_line section"),
+		     segment_selector_size);
+	  return nullptr;
+	}
+    }
+
+  LONGEST header_length = read_offset (parent_bfd, line_ptr, offset_size);
+  line_ptr += offset_size;
+  lh->statement_program_start = line_ptr + header_length;
+
+  lh->minimum_instruction_length = read_1_byte (parent_bfd, line_ptr);
+  line_ptr += 1;
+
+  if (lh->version >= 4)
+    {
+      lh->maximum_ops_per_instruction = read_1_byte (parent_bfd, line_ptr);
+      line_ptr += 1;
+    }
+  else
+    lh->maximum_ops_per_instruction = 1;
+
+  if (lh->maximum_ops_per_instruction == 0)
+    {
+      lh->maximum_ops_per_instruction = 1;
+      complaint (_("invalid maximum_ops_per_instruction "
+		   "in `.debug_line' section"));
+    }
+
+  lh->default_is_stmt = read_1_byte (parent_bfd, line_ptr);
+  line_ptr += 1;
+
+  lh->line_base = read_1_signed_byte (parent_bfd, line_ptr);
+  line_ptr += 1;
+
+  lh->line_range = read_1_byte (parent_bfd, line_ptr);
+  line_ptr += 1;
+
+  lh->opcode_base = read_1_byte (parent_bfd, line_ptr);
+  line_ptr += 1;
+
+  lh->standard_opcode_lengths.reset (new unsigned char[lh->opcode_base]);
+
+  lh->standard_opcode_lengths[0] = 1;  /* This should never be used anyway.  */
+  for (i = 1; i < lh->opcode_base; ++i)
+    {
+      lh->standard_opcode_lengths[i] = read_1_byte (parent_bfd, line_ptr);
+      line_ptr += 1;
+    }
+
+  if (lh->version >= 5)
+    {
+      /* Read directory table.  */
+      read_formatted_entries_separate
+	(parent_bfd, &line_ptr, line_str_data,
+	 lh.get (), offset_size,
+	 [] (struct line_header *header, const char *name,
+	     dir_index d_index, unsigned int mod_time,
+	     unsigned int length)
+	{
+	  header->add_include_dir (name);
+	});
+
+      /* Read file name table.  */
+      read_formatted_entries_separate
+	(parent_bfd, &line_ptr, line_str_data,
+	 lh.get (), offset_size,
+	 [] (struct line_header *header, const char *name,
+	     dir_index d_index, unsigned int mod_time,
+	     unsigned int length)
+	{
+	  header->add_file_name (name, d_index, mod_time, length);
+	});
+    }
+  else
+    {
+      /* Read directory table.  */
+      while ((cur_dir = read_direct_string (parent_bfd, line_ptr, &bytes_read)) != nullptr)
+	{
+	  line_ptr += bytes_read;
+	  lh->add_include_dir (cur_dir);
+	}
+      line_ptr += bytes_read;
+
+      /* Read file name table.  */
+      while ((cur_file = read_direct_string (parent_bfd, line_ptr, &bytes_read)) != nullptr)
+	{
+	  unsigned int mod_time, length;
+	  dir_index d_index;
+
+	  line_ptr += bytes_read;
+	  d_index = (dir_index) read_unsigned_leb128 (parent_bfd, line_ptr, &bytes_read);
+	  line_ptr += bytes_read;
+	  mod_time = read_unsigned_leb128 (parent_bfd, line_ptr, &bytes_read);
+	  line_ptr += bytes_read;
+	  length = read_unsigned_leb128 (parent_bfd, line_ptr, &bytes_read);
+	  line_ptr += bytes_read;
+
+	  lh->add_file_name (cur_file, d_index, mod_time, length);
+	}
+      line_ptr += bytes_read;
+    }
+
+  if (line_ptr > (buf + line_data.size ()))
+    complaint (_("line number info header doesn't "
+		 "fit in `.debug_line' section"));
+
+  *debug_line_ptr += unit_length + offset_size;
+  return lh;
+}
+
+
 /* See line-header.h.  */
 
 line_header_up
diff --git a/gdb/dwarf2/line-header.h b/gdb/dwarf2/line-header.h
index 59a42e336f5..18af953343c 100644
--- a/gdb/dwarf2/line-header.h
+++ b/gdb/dwarf2/line-header.h
@@ -217,4 +217,14 @@ extern line_header_up dwarf_decode_line_header
    struct dwarf2_section_info *section, const struct comp_unit_head *cu_header,
    const char *comp_dir);
 
+/* Like dwarf_decode_line_header but the .debug_line and .debug_line_str
+   are stored in LINE_DATA and LINE_STR_DATA.  This is used when these
+   sections are read from separate files without necessarily having
+   access to the entire debuginfo file they originate from.  */
+
+extern line_header_up dwarf_decode_line_header_separate
+  (bfd *parent_bfd, gdb::array_view<const gdb_byte> line_data,
+   gdb::array_view<const gdb_byte> line_str_data,
+   const gdb_byte **debug_line_ptr);
+
 #endif /* DWARF2_LINE_HEADER_H */
diff --git a/gdb/dwarf2/read-gdb-index.c b/gdb/dwarf2/read-gdb-index.c
index f066d55bd1a..15f7d79e2d3 100644
--- a/gdb/dwarf2/read-gdb-index.c
+++ b/gdb/dwarf2/read-gdb-index.c
@@ -128,6 +128,9 @@ struct mapped_gdb_index final : public mapped_index_base
   }
 };
 
+struct mapped_debug_line;
+typedef std::unique_ptr<mapped_debug_line> mapped_debug_line_up;
+
 struct dwarf2_gdb_index : public dwarf2_base_index_functions
 {
   /* This dumps minimal information about the index.
@@ -165,6 +168,15 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
      block_search_flags search_flags,
      domain_enum domain,
      enum search_domain kind);
+
+  /* Filename information related to this .gdb_index.  */
+  mapped_debug_line_up mdl;
+
+  /* Return true if any of the filenames in this .gdb_index's .debug_line
+     mapping match FILE_MATCHER.  Initializes the mapping if necessary.  */
+  bool filename_in_debug_line
+  (objfile *objfile,
+   gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher);
 };
 
 /* This dumps minimal information about the index.
@@ -517,6 +529,17 @@ dwarf2_gdb_index::do_expand_symtabs_matching
   return result;
 }
 
+bool
+dwarf2_gdb_index::filename_in_debug_line
+  (objfile *objfile,
+   gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
+{
+  if (mdl == nullptr)
+    mdl.reset (new mapped_debug_line (objfile));
+
+  return mdl->contains_matching_filename (file_matcher);
+}
+
 bool
 dwarf2_gdb_index::expand_symtabs_matching
     (struct objfile *objfile,
@@ -547,6 +570,9 @@ dwarf2_gdb_index::expand_symtabs_matching
 
       /* Objfile is a stub holding only index information.  Try to reinitialize
 	 objfile with the full debuginfo.  */
+      if (file_matcher != nullptr
+	  && !filename_in_debug_line (objfile, file_matcher))
+	return true;
       if (!read_full_dwarf_from_debuginfod (objfile))
 	return false;
       return do_expand_symtabs_matching (objfile, file_matcher, lookup_name,
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 12b0dbd1ee8..8bcdf9824cc 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -81,6 +81,7 @@
 #include "gdbsupport/gdb_optional.h"
 #include "gdbsupport/underlying.h"
 #include "gdbsupport/hash_enum.h"
+#include "gdbsupport/scoped_mmap.h"
 #include "filename-seen-cache.h"
 #include "producer.h"
 #include <fcntl.h>
@@ -2103,6 +2104,169 @@ dw2_get_file_names (dwarf2_per_cu_data *this_cu,
   return this_cu->file_names;
 }
 
+#if !HAVE_SYS_MMAN_H
+
+bool
+mapped_debug_line::contains_matching_filename
+  (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
+{
+  return false;
+}
+
+gdb::array_view<const gdb_byte>
+mapped_debug_line::read_debug_line_separate
+  (char *filename, std::unique_ptr<index_cache_resource> *resource)
+{
+  return {};
+}
+
+bool
+mapped_debug_line::read_debug_line_from_debuginfod (objfile *objfile)
+{
+  return false;
+}
+
+#else /* !HAVE_SYS_MMAN_H */
+
+struct line_resource_mmap final : public index_cache_resource
+{
+  /* Try to mmap FILENAME.  Throw an exception on failure, including if the
+     file doesn't exist. */
+  line_resource_mmap (const char *filename)
+    : mapping (mmap_file (filename))
+  {}
+
+  scoped_mmap mapping;
+};
+
+/* See read.h.  */
+
+bool
+mapped_debug_line::contains_matching_filename
+  (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
+{
+  for (line_header_up &lh : line_headers)
+    for (file_entry &fe : lh->file_names ())
+      {
+	const char *filename = fe.name;
+
+	if (file_matcher (fe.name, false))
+	  return true;
+
+	bool basename_match = file_matcher (lbasename (fe.name), true);
+
+	if (!basenames_may_differ && !basename_match)
+	  continue;
+
+	/* DW_AT_comp_dir is not explicitly mentioned in the .debug_line
+	   until DWARF5.  Since we don't have access to the CU at this
+	   point we just check for a partial match on the filename.
+	   If there is a match, the full debuginfo will be downloaded
+	   ane the match will be re-evalute with DW_AT_comp_dir.  */
+	if (lh->version < 5 && fe.d_index == 0)
+	  return basename_match;
+
+	const char *dirname = fe.include_dir (&*lh);
+	std::string fullname;
+
+	if (dirname == nullptr || IS_ABSOLUTE_PATH (filename))
+	  fullname = filename;
+	else
+	  fullname = std::string (dirname) + SLASH_STRING + filename;
+
+	gdb::unique_xmalloc_ptr<char> rewritten
+	  = rewrite_source_path (fullname.c_str ());
+	if (rewritten != nullptr)
+	  fullname = rewritten.release ();
+
+	if (file_matcher (fullname.c_str (), false))
+	  return true;
+      }
+
+  return false;
+}
+
+/* See read.h.  */
+
+gdb::array_view<const gdb_byte>
+mapped_debug_line::read_debug_line_separate
+  (char *filename, std::unique_ptr<index_cache_resource> *resource)
+{
+  if (filename == nullptr)
+    return {};
+
+  try
+  {
+    line_resource_mmap *mmap_resource
+      = new line_resource_mmap (filename);
+
+    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 &except)
+  {
+    exception_print (gdb_stderr, except);
+  }
+
+  return {};
+}
+
+/* See read.h.  */
+
+bool
+mapped_debug_line::read_debug_line_from_debuginfod (objfile *objfile)
+{
+  const bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ());
+  if (build_id == nullptr)
+    return false;
+
+  gdb::unique_xmalloc_ptr<char> line_path;
+  scoped_fd line_fd = debuginfod_section_query (build_id->data,
+						build_id->size,
+						bfd_get_filename
+						  (objfile->obfd.get ()),
+						".debug_line",
+						&line_path);
+
+  gdb::unique_xmalloc_ptr<char> line_str_path;
+  scoped_fd line_str_fd = debuginfod_section_query (build_id->data,
+						    build_id->size,
+						    bfd_get_filename
+						      (objfile->obfd.get ()),
+						    ".debug_line_str",
+						    &line_str_path);
+
+  if (line_fd.get () < 0)
+    return false;
+
+  line_data = read_debug_line_separate (line_path.get (), &line_resource);
+  line_str_data = read_debug_line_separate (line_str_path.get (),
+					    &line_str_resource);
+
+  const gdb_byte *line_ptr = line_data.data ();
+
+  while (line_ptr < line_data.data () + line_data.size ())
+    {
+      line_header_up lh
+	= dwarf_decode_line_header_separate (objfile->obfd.get (),
+					     line_data, line_str_data,
+					     &line_ptr);
+      line_headers.emplace_back (lh.release ());
+    }
+
+  return true;
+}
+#endif /* !HAVE_SYS_MMAN_H */
+
+mapped_debug_line::mapped_debug_line (objfile *objfile)
+{
+  if (!read_debug_line_from_debuginfod (objfile))
+    line_headers.clear ();
+}
+
 /* A helper for the "quick" functions which computes and caches the
    real path for a given file name from the line table.  */
 
diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h
index 4e6c35cba5e..63d804f7b44 100644
--- a/gdb/dwarf2/read.h
+++ b/gdb/dwarf2/read.h
@@ -34,6 +34,7 @@
 #include "gdbsupport/hash_enum.h"
 #include "gdbsupport/function-view.h"
 #include "gdbsupport/packed.h"
+#include "dwarf2/line-header.h"
 
 /* Hold 'maintenance (set|show) dwarf' commands.  */
 extern struct cmd_list_element *set_dwarf_cmdlist;
@@ -951,4 +952,34 @@ extern bool read_addrmap_from_aranges (dwarf2_per_objfile *per_objfile,
 
 extern bool read_full_dwarf_from_debuginfod (struct objfile *objfile);
 
+struct mapped_debug_line
+{
+  mapped_debug_line (objfile *objfile);
+
+  /* Return true if any of the mapped .debug_line's filenames match
+     FILE_MATCHER.  */
+
+  bool contains_matching_filename
+    (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher);
+
+private:
+  std::vector<line_header_up> line_headers;
+
+  gdb::array_view<const gdb_byte> line_data;
+  gdb::array_view<const gdb_byte> line_str_data;
+
+  std::unique_ptr<index_cache_resource> line_resource;
+  std::unique_ptr<index_cache_resource> line_str_resource;
+
+  /* Download the .debug_line and .debug_line_str associated with OBJFILE
+     and populate line_headers.  */
+
+  bool read_debug_line_from_debuginfod (objfile *objfile);
+
+  /* Initialize line_data and line_str_data with the .debug_line and
+    .debug_line_str downloaded read_debug_line_from_debuginfod.  */
+
+  gdb::array_view<const gdb_byte> read_debug_line_separate
+    (char *filename, std::unique_ptr<index_cache_resource> *resource);
+};
 #endif /* DWARF2READ_H */
