+2020-06-23  Roland McGrath  <mcgrathr@google.com>
+
+       PR 22843
+       * options.h (class General_options): Add --dependency-file option.
+       * fileread.cc (File_read::files_read): New static variable.
+       (File_read::open): Add the file to the files_read list.
+       (File_read::record_file_read): New static member function.
+       (File_read::write_dependency_file): New static member function.
+       * fileread.h (class File_read): Declare them.
+       * layout.cc (Layout::read_layout_from_file): Call record_file_read.
+       (Close_task_runner::run): Call write_dependency_file if
+       --dependency-file was passed.
+
 2020-06-18  Fangrui Song  <i@maskray.me>
 
        PR gold/26039
 
 unsigned long long File_read::total_mapped_bytes;
 unsigned long long File_read::current_mapped_bytes;
 unsigned long long File_read::maximum_mapped_bytes;
+std::vector<std::string> File_read::files_read;
 
 // Class File_read::View.
 
       gold_debug(DEBUG_FILES, "Attempt to open %s succeeded",
                 this->name_.c_str());
       this->token_.add_writer(task);
+      Hold_optional_lock hl(file_counts_lock);
+      record_file_read(this->name_);
     }
 
   return this->descriptor_ >= 0;
                          binary_to_elf.converted_size());
 }
 
+void
+File_read::record_file_read(const std::string& name)
+{
+  File_read::files_read.push_back(name);
+}
+
+void
+File_read::write_dependency_file(const char* dependency_file_name,
+                                const char* output_file_name)
+{
+  FILE *depfile = fopen(dependency_file_name, "w");
+
+  fprintf(depfile, "%s:", output_file_name);
+  for (std::vector<std::string>::const_iterator it = files_read.begin();
+       it != files_read.end();
+       ++it)
+    fprintf(depfile, " \\\n  %s", it->c_str());
+  fprintf(depfile, "\n");
+
+  for (std::vector<std::string>::const_iterator it = files_read.begin();
+       it != files_read.end();
+       ++it)
+    fprintf(depfile, "\n%s:\n", it->c_str());
+
+  fclose(depfile);
+}
+
 } // End namespace gold.
 
   static void
   print_stats();
 
+  // Write the dependency file listing all files read.
+  static void
+  write_dependency_file(const char* dependency_file_name,
+                       const char* output_file_name);
+
+  // Record that a file was read.  File_read::open does this.
+  static void
+  record_file_read(const std::string& name);
+
   // Return the open file descriptor (for plugins).
   int
   descriptor()
     this->reopen_descriptor();
     return this->descriptor_;
   }
-  
+
   // Return the file last modification time.  Calls gold_fatal if the stat
   // system call failed.
   Timespec
   // --stats.
   static unsigned long long maximum_mapped_bytes;
 
+  // Set of names of all files read.
+  static std::vector<std::string> files_read;
+
   // A view into the file.
   class View
   {
 
       // All ".text.unlikely.*" sections can be moved to a unique
       // segment with --text-unlikely-segment option.
       bool text_unlikely_segment
-          = (parameters->options().text_unlikely_segment()
-             && is_prefix_of(".text.unlikely",
-                             object->section_name(shndx).c_str()));
+         = (parameters->options().text_unlikely_segment()
+            && is_prefix_of(".text.unlikely",
+                            object->section_name(shndx).c_str()));
       if (text_unlikely_segment)
-        {
+       {
          elfcpp::Elf_Xword flags
            = this->get_output_section_flags(shdr.get_sh_flags());
 
                                                    &name_key);
          os = this->get_output_section(os_name, name_key, sh_type, flags,
                                        ORDER_INVALID, false);
-          // Map this output section to a unique segment.  This is done to
-          // separate "text" that is not likely to be executed from "text"
-          // that is likely executed.
+         // Map this output section to a unique segment.  This is done to
+         // separate "text" that is not likely to be executed from "text"
+         // that is likely executed.
          os->set_is_unique_segment();
-        }
+       }
       else
        {
          // Plugins can choose to place one or more subsets of sections in
              // We know the name of the output section, directly call
              // get_output_section here by-passing choose_output_section.
              elfcpp::Elf_Xword flags
-               = this->get_output_section_flags(shdr.get_sh_flags());
+               = this->get_output_section_flags(shdr.get_sh_flags());
 
              const char* os_name = it->second->name;
              Stringpool::Key name_key;
              os = this->get_output_section(os_name, name_key, sh_type, flags,
                                        ORDER_INVALID, false);
              if (!os->is_unique_segment())
-               {
-                 os->set_is_unique_segment();
-                 os->set_extra_segment_flags(it->second->flags);
-                 os->set_segment_alignment(it->second->align);
-               }
+               {
+                 os->set_is_unique_segment();
+                 os->set_extra_segment_flags(it->second->flags);
+                 os->set_segment_alignment(it->second->align);
+               }
            }
          }
       if (os == NULL)
       const int size = parameters->target().get_size();
       const bool is_big_endian = parameters->target().is_big_endian();
       if (size == 32)
-        {
-          if (is_big_endian)
-            {
+       {
+         if (is_big_endian)
+           {
 #ifdef HAVE_TARGET_32_BIG
              parameters->sized_target<32, true>()->
                  record_gnu_property(note_type, pr_type, pr_datasz, pr_data,
 #else
              gold_unreachable();
 #endif
-            }
-          else
-            {
+           }
+         else
+           {
 #ifdef HAVE_TARGET_32_LITTLE
              parameters->sized_target<32, false>()->
                  record_gnu_property(note_type, pr_type, pr_datasz, pr_data,
 #else
              gold_unreachable();
 #endif
-            }
-        }
+           }
+       }
       else if (size == 64)
-        {
-          if (is_big_endian)
-            {
+       {
+         if (is_big_endian)
+           {
 #ifdef HAVE_TARGET_64_BIG
              parameters->sized_target<64, true>()->
                  record_gnu_property(note_type, pr_type, pr_datasz, pr_data,
 #else
              gold_unreachable();
 #endif
-            }
-          else
-            {
+           }
+         else
+           {
 #ifdef HAVE_TARGET_64_LITTLE
              parameters->sized_target<64, false>()->
                  record_gnu_property(note_type, pr_type, pr_datasz, pr_data,
 #else
              gold_unreachable();
 #endif
-            }
-        }
+           }
+       }
       else
-        gold_unreachable();
+       gold_unreachable();
       return;
     }
 
     gold_fatal(_("unable to open --section-ordering-file file %s: %s"),
               filename, strerror(errno));
 
+  File_read::record_file_read(filename);
+
   std::getline(in, line);   // this chops off the trailing \n, if any
   unsigned int position = 1;
   this->set_section_ordering_specified();
       write_sized_value(datasz, 4, p + 4, is_big_endian);
       memcpy(p + 8, prop->second.pr_data, datasz);
       if (aligned_datasz > datasz)
-        memset(p + 8 + datasz, 0, aligned_datasz - datasz);
+       memset(p + 8 + datasz, 0, aligned_datasz - datasz);
       p += 8 + aligned_datasz;
     }
   Output_section_data* posd = new Output_data_const(desc, descsz, 4);
   if (this->options_->oformat_enum() != General_options::OBJECT_FORMAT_ELF)
     this->layout_->write_binary(this->of_);
 
+  if (this->options_->dependency_file())
+    File_read::write_dependency_file(this->options_->dependency_file(),
+                                    this->options_->output_file_name());
+
   this->of_->close();
 }
 
 
   options::String_set::const_iterator                                    \
   varname__##_end() const                                                \
   { return this->varname__##_.value.end(); }                              \
-                                                                          \
+                                                                         \
   options::String_set::size_type                                          \
   varname__##_size() const                                                \
   { return this->varname__##_.value.size(); }                             \
              N_("Do not demangle C++ symbols in log messages"),
              NULL);
 
+  DEFINE_string(dependency_file, options::TWO_DASHES, '\0', NULL,
+               N_("Write a dependency file listing all files read"),
+               N_("FILE"));
+
   DEFINE_bool(detect_odr_violations, options::TWO_DASHES, '\0', false,
              N_("Look for violations of the C++ One Definition Rule"),
              N_("Do not look for violations of the C++ One Definition Rule"));
   DEFINE_uint64(stack_size, options::DASH_Z, '\0', 0,
                N_("Set PT_GNU_STACK segment p_memsz to SIZE"), N_("SIZE"));
   DEFINE_enum(start_stop_visibility, options::DASH_Z, '\0', "protected",
-              N_("ELF symbol visibility for synthesized "
-                 "__start_* and __stop_* symbols"),
-              ("[default,internal,hidden,protected]"),
-              {"default", "internal", "hidden", "protected"});
+             N_("ELF symbol visibility for synthesized "
+                "__start_* and __stop_* symbols"),
+             ("[default,internal,hidden,protected]"),
+             {"default", "internal", "hidden", "protected"});
   DEFINE_bool(text, options::DASH_Z, '\0', false,
              N_("Do not permit relocations in read-only segments"),
              N_("Permit relocations in read-only segments"));
 
+2020-06-23  Roland McGrath  <mcgrathr@google.com>
+
+       PR 22843
+       * NEWS: Note --dependency-file.
+       * ld.texi (Options): Document --dependency-file.
+       * ldlex.h (enum option_values): Add OPTION_DEPENDENCY_FILE.
+       * ld.h (ld_config_type): New member dependency_file.
+       * lexsup.c (ld_options, parse_args): Parse --dependency-file.
+       * ldmain.c (struct dependency_file): New type.
+       (dependency_files, dependency_files_tail): New static variables.
+       (track_dependency_files): New function.
+       (write_dependency_file): New function.
+       (main): Call it when --dependency-file was passed.
+       * ldfile.c (ldfile_try_open_bfd): Call track_dependency_files.
+       (ldfile_open_command_file_1): Likewise.
+       * ldelf.c (ldelf_try_needed): Likewise.
+       * pe-dll.c (pe_implied_import_dll): Likewise.
+
 2020-06-23  Alan Modra  <amodra@gmail.com>
 
        PR 26150
 
 * Add ELF linker command-line option `-z start-stop-visibility=...' to control
   the visibility of synthetic `__start_SECNAME` and `__stop_SECNAME` symbols.
 
+* Add command-line option --dependency-file to write a Make-style dependency
+  file listing the input files consulted by the linker, like the files written
+  by the compiler's -M -MP options.
+
 Changes in 2.34:
 
 * The ld check for "PHDR segment not covered by LOAD segment" is more
 
   char *map_filename;
   FILE *map_file;
 
+  char *dependency_file;
+
   unsigned int split_by_reloc;
   bfd_size_type split_by_file;
 
 
 option is not specified, the name @file{a.out} is used by default.  The
 script command @code{OUTPUT} can also specify the output file name.
 
+@kindex --dependency-file=@var{depfile}
+@cindex dependency file
+@item --dependency-file=@var{depfile}
+Write a @dfn{dependency file} to @var{depfile}.  This file contains a rule
+suitable for @code{make} describing the output file and all the input files
+that were read to produce it.  The output is similar to the compiler's
+output with @samp{-M -MP} (@pxref{Preprocessor Options,, Options
+Controlling the Preprocessor, gcc.info, Using the GNU Compiler
+Collection}).  Note that there is no option like the compiler's @samp{-MM},
+to exclude ``system files'' (which is not a well-specified concept in the
+linker, unlike ``system headers'' in the compiler).  So the output from
+@samp{--dependency-file} is always specific to the exact state of the
+installation where it was produced, and should not be copied into
+distributed makefiles without careful editing.
+
 @kindex -O @var{level}
 @cindex generating optimized output
 @item -O @var{level}
 
       return FALSE;
     }
 
+  track_dependency_files (name);
+
   /* Linker needs to decompress sections.  */
   abfd->flags |= BFD_DECOMPRESS;
 
        }
       return;
     }
-  
+
   if (!link_info.traditional_format)
     {
       bfd *elfbfd = NULL;
 
       return FALSE;
     }
 
+  track_dependency_files (attempt);
+
   /* Linker needs to decompress sections.  */
   entry->the_bfd->flags |= BFD_DECOMPRESS;
 
       bfd_boolean found = FALSE;
 
       /* If extra_search_path is set, entry->filename is a relative path.
-         Search the directory of the current linker script before searching
-         other paths. */
+        Search the directory of the current linker script before searching
+        other paths. */
       if (entry->extra_search_path)
-        {
-          char *path = concat (entry->extra_search_path, slash, entry->filename,
-                               (const char *)0);
-          if (ldfile_try_open_bfd (path, entry))
-            {
-              entry->filename = path;
-              entry->flags.search_dirs = FALSE;
-              return;
-            }
+       {
+         char *path = concat (entry->extra_search_path, slash, entry->filename,
+                              (const char *)0);
+         if (ldfile_try_open_bfd (path, entry))
+           {
+             entry->filename = path;
+             entry->flags.search_dirs = FALSE;
+             return;
+           }
 
          free (path);
-        }
+       }
 
       /* Try to open <filename><suffix> or lib<filename><suffix>.a.  */
       for (arch = search_arch_head; arch != NULL; arch = arch->next)
       return;
     }
 
+  track_dependency_files (name);
+
   lex_push_file (ldlex_input_stack, name, sysrooted);
 
   lineno = 1;
 
   OPTION_NO_PRINT_MAP_DISCARDED,
   OPTION_NON_CONTIGUOUS_REGIONS,
   OPTION_NON_CONTIGUOUS_REGIONS_WARNINGS,
+  OPTION_DEPENDENCY_FILE,
 };
 
 /* The initial parser states.  */
 
 
 struct bfd_link_info link_info;
 \f
+struct dependency_file
+{
+  struct dependency_file *next;
+  char *name;
+};
+
+static struct dependency_file *dependency_files, *dependency_files_tail;
+
+void
+track_dependency_files (const char *filename)
+{
+  struct dependency_file *dep
+    = (struct dependency_file *) xmalloc (sizeof (*dep));
+  dep->name = xstrdup (filename);
+  dep->next = NULL;
+  if (dependency_files == NULL)
+    dependency_files = dep;
+  else
+    dependency_files_tail->next = dep;
+  dependency_files_tail = dep;
+}
+
+static void
+write_dependency_file (void)
+{
+  FILE *out;
+  struct dependency_file *dep;
+
+  out = fopen (config.dependency_file, FOPEN_WT);
+  if (out == NULL)
+    {
+      einfo (_("%F%P: cannot open dependency file %s: %E\n"),
+            config.dependency_file);
+    }
+
+  fprintf (out, "%s:", output_filename);
+
+  for (dep = dependency_files; dep != NULL; dep = dep->next)
+    fprintf (out, " \\\n  %s", dep->name);
+
+  fprintf (out, "\n");
+  for (dep = dependency_files; dep != NULL; dep = dep->next)
+    fprintf (out, "\n%s:\n", dep->name);
+
+  fclose (out);
+}
+\f
 static void
 ld_cleanup (void)
 {
       /* is_sysrooted_pathname() relies on no trailing dirsep.  */
       if (ld_canon_sysroot_len > 0
          && IS_DIR_SEPARATOR (ld_canon_sysroot [ld_canon_sysroot_len - 1]))
-        ld_canon_sysroot [--ld_canon_sysroot_len] = '\0';
+       ld_canon_sysroot [--ld_canon_sysroot_len] = '\0';
     }
   else
     ld_canon_sysroot_len = -1;
   ldexp_finish ();
   lang_finish ();
 
+  if (config.dependency_file != NULL)
+    write_dependency_file ();
+
   /* Even if we're producing relocatable output, some non-fatal errors should
      be reported in the exit status.  (What non-fatal errors, if any, do we
      want to ignore for relocatable output?)  */
 
 extern void add_wrap (const char *);
 extern void add_ignoresym (struct bfd_link_info *, const char *);
 extern void add_keepsyms_file (const char *);
+extern void track_dependency_files (const char *);
 
 #endif
 
     'c', N_("FILE"), N_("Read MRI format linker script"), TWO_DASHES },
   { {"dc", no_argument, NULL, 'd'},
     'd', NULL, N_("Force common symbols to be defined"), ONE_DASH },
+  { {"dependency-file", required_argument, NULL, OPTION_DEPENDENCY_FILE},
+    '\0', N_("FILE"), N_("Write dependency file"), TWO_DASHES },
   { {"dp", no_argument, NULL, 'd'},
     '\0', NULL, NULL, ONE_DASH },
   { {"force-group-allocation", no_argument, NULL,
              = lang_new_vers_pattern (NULL, xstrdup (optarg), NULL,
                                       FALSE);
            lang_append_dynamic_list (&export_list, expr);
-          }
+         }
          break;
        case OPTION_EXPORT_DYNAMIC_SYMBOL_LIST:
          /* This option indicates a small script that only specifies
        case OPTION_PRINT_MAP_DISCARDED:
          config.print_map_discarded = TRUE;
          break;
+
+       case OPTION_DEPENDENCY_FILE:
+         config.dependency_file = optarg;
+         break;
        }
     }
 
     }
   fprintf (file, _("\
   -P AUDITLIB, --depaudit=AUDITLIB\n" "\
-                              Specify a library to use for auditing dependencies\n"));
+                             Specify a library to use for auditing dependencies\n"));
   fprintf (file, _("\
   -z combreloc                Merge dynamic relocs into one section and sort\n"));
   fprintf (file, _("\
   -z nocombreloc              Don't merge dynamic relocs into one section\n"));
   fprintf (file, _("\
   -z global                   Make symbols in DSO available for subsequently\n\
-                               loaded objects\n"));
+                              loaded objects\n"));
   fprintf (file, _("\
   -z initfirst                Mark DSO to be initialized first at runtime\n"));
   fprintf (file, _("\
   -z now                      Mark object non-lazy runtime binding\n"));
   fprintf (file, _("\
   -z origin                   Mark object requiring immediate $ORIGIN\n\
-                                processing at runtime\n"));
+                               processing at runtime\n"));
 #if DEFAULT_LD_Z_RELRO
   fprintf (file, _("\
   -z relro                    Create RELRO program header (default)\n"));
   --build-id[=STYLE]          Generate build ID note\n"));
   fprintf (file, _("\
   --compress-debug-sections=[none|zlib|zlib-gnu|zlib-gabi]\n\
-                              Compress DWARF debug sections using zlib\n"));
+                             Compress DWARF debug sections using zlib\n"));
 #ifdef DEFAULT_FLAG_COMPRESS_DEBUG
   fprintf (file, _("\
-                               Default: zlib-gabi\n"));
+                              Default: zlib-gabi\n"));
 #else
   fprintf (file, _("\
-                               Default: none\n"));
+                              Default: none\n"));
 #endif
   fprintf (file, _("\
   -z common-page-size=SIZE    Set common page size to SIZE\n"));
   --ld-generated-unwind-info  Generate exception handling info for PLT\n"));
   fprintf (file, _("\
   --no-ld-generated-unwind-info\n\
-                              Don't generate exception handling info for PLT\n"));
+                             Don't generate exception handling info for PLT\n"));
 }
 
 static void
 
       return FALSE;
     }
 
+  track_dependency_files (filename);
+
   /* PEI dlls seem to be bfd_objects.  */
   if (!bfd_check_format (dll, bfd_object))
     {