gdb: add target_ops::supports_displaced_step
[deliverable/binutils-gdb.git] / gold / dwp.cc
index c6453bfdef1962f468b5ecf1746d744d11f15012..7c4eef090d482056d6b4ac379aeaf402cc6c4490 100644 (file)
@@ -1,6 +1,6 @@
 // dwp.cc -- DWARF packaging utility
 
-// Copyright 2012 Free Software Foundation, Inc.
+// Copyright (C) 2012-2020 Free Software Foundation, Inc.
 // Written by Cary Coutant <ccoutant@google.com>.
 
 // This file is part of dwp, the DWARF packaging utility.
@@ -60,7 +60,15 @@ template <int size, bool big_endian>
 class Sized_relobj_dwo;
 
 // List of .dwo files to process.
-typedef std::vector<std::string> File_list;
+struct Dwo_file_entry
+{
+  Dwo_file_entry(uint64_t id, std::string name)
+    : dwo_id(id), dwo_name(name)
+  { }
+  uint64_t dwo_id;
+  std::string dwo_name;
+};
+typedef std::vector<Dwo_file_entry> File_list;
 
 // Type to hold the offset and length of an input section
 // within an output section.
@@ -115,6 +123,12 @@ class Dwo_file
   void
   read(Dwp_output_file* output_file);
 
+  // Verify a .dwp file given a list of .dwo files referenced by the
+  // corresponding executable file.  Returns true if no problems
+  // were found.
+  bool
+  verify(const File_list& files);
+
  private:
   // Types for mapping input string offsets to output string offsets.
   typedef std::pair<section_offset_type, section_offset_type>
@@ -174,6 +188,16 @@ class Dwo_file
   sized_read_unit_index(unsigned int, unsigned int *, Dwp_output_file*,
                        bool is_tu_index);
 
+  // Verify the .debug_cu_index section of a .dwp file, comparing it
+  // against the list of .dwo files referenced by the corresponding
+  // executable file.
+  bool
+  verify_dwo_list(unsigned int, const File_list& files);
+
+  template <bool big_endian>
+  bool
+  sized_verify_dwo_list(unsigned int, const File_list& files);
+
   // Merge the input string table section into the output file.
   void
   add_strings(Dwp_output_file*, unsigned int);
@@ -248,7 +272,7 @@ class Sized_relobj_dwo : public Sized_relobj<size, big_endian>
 
   // Get the name of a section.
   std::string
-  do_section_name(unsigned int shndx)
+  do_section_name(unsigned int shndx) const
   { return this->elf_file_.section_name(shndx); }
 
   // Get the size of a section.
@@ -260,14 +284,6 @@ class Sized_relobj_dwo : public Sized_relobj<size, big_endian>
   const unsigned char*
   do_section_contents(unsigned int, section_size_type*, bool);
 
-  // Return a view of the uncompressed contents of a section.  Set *PLEN
-  // to the size.  Set *IS_NEW to true if the contents need to be deleted
-  // by the caller.
-  const unsigned char*
-  do_decompressed_section_contents(unsigned int shndx,
-                                  section_size_type* plen,
-                                  bool* is_new);
-
   // The following virtual functions are abstract in the base classes,
   // but are not used here.
 
@@ -757,9 +773,36 @@ template <int size, bool big_endian>
 void
 Sized_relobj_dwo<size, big_endian>::setup()
 {
+  const int shdr_size = elfcpp::Elf_sizes<size>::shdr_size;
+  const off_t shoff = this->elf_file_.shoff();
   const unsigned int shnum = this->elf_file_.shnum();
+
   this->set_shnum(shnum);
   this->section_offsets().resize(shnum);
+
+  // Read the section headers.
+  const unsigned char* const pshdrs = this->get_view(shoff, shnum * shdr_size,
+                                                    true, false);
+
+  // Read the section names.
+  const unsigned char* pshdrnames =
+      pshdrs + this->elf_file_.shstrndx() * shdr_size;
+  typename elfcpp::Shdr<size, big_endian> shdrnames(pshdrnames);
+  if (shdrnames.get_sh_type() != elfcpp::SHT_STRTAB)
+    this->error(_("section name section has wrong type: %u"),
+               static_cast<unsigned int>(shdrnames.get_sh_type()));
+  section_size_type section_names_size =
+      convert_to_section_size_type(shdrnames.get_sh_size());
+  const unsigned char* namesu = this->get_view(shdrnames.get_sh_offset(),
+                                              section_names_size, false,
+                                              false);
+  const char* names = reinterpret_cast<const char*>(namesu);
+
+  Compressed_section_map* compressed_sections =
+      build_compressed_section_map<size, big_endian>(
+         pshdrs, this->shnum(), names, section_names_size, this, true);
+  if (compressed_sections != NULL && !compressed_sections->empty())
+    this->set_compressed_sections(compressed_sections);
 }
 
 // Return a view of the contents of a section.
@@ -781,43 +824,6 @@ Sized_relobj_dwo<size, big_endian>::do_section_contents(
   return this->get_view(loc.file_offset, *plen, true, cache);
 }
 
-// Return a view of the uncompressed contents of a section.  Set *PLEN
-// to the size.  Set *IS_NEW to true if the contents need to be deleted
-// by the caller.
-
-template <int size, bool big_endian>
-const unsigned char*
-Sized_relobj_dwo<size, big_endian>::do_decompressed_section_contents(
-    unsigned int shndx,
-    section_size_type* plen,
-    bool* is_new)
-{
-  section_size_type buffer_size;
-  const unsigned char* buffer = this->do_section_contents(shndx, &buffer_size,
-                                                         false);
-
-  std::string sect_name = this->do_section_name(shndx);
-  if (!is_prefix_of(".zdebug_", sect_name.c_str()))
-    {
-      *plen = buffer_size;
-      *is_new = false;
-      return buffer;
-    }
-
-  section_size_type uncompressed_size = get_uncompressed_size(buffer,
-                                                             buffer_size);
-  unsigned char* uncompressed_data = new unsigned char[uncompressed_size];
-  if (!decompress_input_section(buffer,
-                               buffer_size,
-                               uncompressed_data,
-                               uncompressed_size))
-    this->error(_("could not decompress section %s"),
-               this->section_name(shndx).c_str());
-  *plen = uncompressed_size;
-  *is_new = true;
-  return uncompressed_data;
-}
-
 // Class Dwo_file.
 
 Dwo_file::~Dwo_file()
@@ -946,10 +952,13 @@ Dwo_file::read(Dwp_output_file* output_file)
        this->read_unit_index(debug_cu_index, debug_shndx, output_file, false);
       if (debug_tu_index > 0)
         {
-         if (debug_types.size() != 1)
-           gold_fatal(_("%s: .dwp file must have exactly one "
+         if (debug_types.size() > 1)
+           gold_fatal(_("%s: .dwp file must have no more than one "
                         ".debug_types.dwo section"), this->name_);
-         debug_shndx[elfcpp::DW_SECT_TYPES] = debug_types[0];
+          if (debug_types.size() == 1)
+            debug_shndx[elfcpp::DW_SECT_TYPES] = debug_types[0];
+          else
+            debug_shndx[elfcpp::DW_SECT_TYPES] = 0;
          this->read_unit_index(debug_tu_index, debug_shndx, output_file, true);
        }
       return;
@@ -969,6 +978,48 @@ Dwo_file::read(Dwp_output_file* output_file)
     }
 }
 
+// Verify a .dwp file given a list of .dwo files referenced by the
+// corresponding executable file.  Returns true if no problems
+// were found.
+
+bool
+Dwo_file::verify(const File_list& files)
+{
+  this->obj_ = this->make_object(NULL);
+
+  unsigned int shnum = this->shnum();
+  this->is_compressed_.resize(shnum);
+  this->sect_offsets_.resize(shnum);
+
+  unsigned int debug_cu_index = 0;
+
+  // Scan the section table and collect debug sections.
+  // (Section index 0 is a dummy section; skip it.)
+  for (unsigned int i = 1; i < shnum; i++)
+    {
+      if (this->section_type(i) != elfcpp::SHT_PROGBITS)
+       continue;
+      std::string sect_name = this->section_name(i);
+      const char* suffix = sect_name.c_str();
+      if (is_prefix_of(".debug_", suffix))
+       suffix += 7;
+      else if (is_prefix_of(".zdebug_", suffix))
+       {
+         this->is_compressed_[i] = true;
+         suffix += 8;
+       }
+      else
+       continue;
+      if (strcmp(suffix, "cu_index") == 0)
+       debug_cu_index = i;
+    }
+
+  if (debug_cu_index == 0)
+    gold_fatal(_("%s: no .debug_cu_index section found"), this->name_);
+
+  return this->verify_dwo_list(debug_cu_index, files);
+}
+
 // Create a Sized_relobj_dwo of the given size and endianness,
 // and record the target info.
 
@@ -1088,7 +1139,7 @@ Dwo_file::sized_read_unit_index(unsigned int shndx,
                               : elfcpp::DW_SECT_INFO);
   unsigned int info_shndx = debug_shndx[info_sect];
 
-  gold_assert(shndx > 0 && info_shndx > 0);
+  gold_assert(shndx > 0);
 
   section_size_type index_len;
   bool index_is_new;
@@ -1114,6 +1165,8 @@ Dwo_file::sized_read_unit_index(unsigned int shndx,
   if (ncols == 0 || nused == 0)
     return;
 
+  gold_assert(info_shndx > 0);
+
   unsigned int nslots =
       elfcpp::Swap_unaligned<32, big_endian>::readval(contents
                                                      + 3 * sizeof(uint32_t));
@@ -1211,6 +1264,102 @@ Dwo_file::sized_read_unit_index(unsigned int shndx,
     delete[] info_contents;
 }
 
+// Verify the .debug_cu_index section of a .dwp file, comparing it
+// against the list of .dwo files referenced by the corresponding
+// executable file.
+
+bool
+Dwo_file::verify_dwo_list(unsigned int shndx, const File_list& files)
+{
+  if (this->obj_->is_big_endian())
+    return this->sized_verify_dwo_list<true>(shndx, files);
+  else
+    return this->sized_verify_dwo_list<false>(shndx, files);
+}
+
+template <bool big_endian>
+bool
+Dwo_file::sized_verify_dwo_list(unsigned int shndx, const File_list& files)
+{
+  gold_assert(shndx > 0);
+
+  section_size_type index_len;
+  bool index_is_new;
+  const unsigned char* contents =
+      this->section_contents(shndx, &index_len, &index_is_new);
+
+  unsigned int version =
+      elfcpp::Swap_unaligned<32, big_endian>::readval(contents);
+
+  // We don't support version 1 anymore because it was experimental
+  // and because in normal use, dwp is not expected to read .dwp files
+  // produced by an earlier version of the tool.
+  if (version != 2)
+    gold_fatal(_("%s: section %s has unsupported version number %d"),
+              this->name_, this->section_name(shndx).c_str(), version);
+
+  unsigned int ncols =
+      elfcpp::Swap_unaligned<32, big_endian>::readval(contents
+                                                     + sizeof(uint32_t));
+  unsigned int nused =
+      elfcpp::Swap_unaligned<32, big_endian>::readval(contents
+                                                     + 2 * sizeof(uint32_t));
+  if (ncols == 0 || nused == 0)
+    return true;
+
+  unsigned int nslots =
+      elfcpp::Swap_unaligned<32, big_endian>::readval(contents
+                                                     + 3 * sizeof(uint32_t));
+
+  const unsigned char* phash = contents + 4 * sizeof(uint32_t);
+  const unsigned char* pindex = phash + nslots * sizeof(uint64_t);
+  const unsigned char* pcolhdrs = pindex + nslots * sizeof(uint32_t);
+  const unsigned char* poffsets = pcolhdrs + ncols * sizeof(uint32_t);
+  const unsigned char* psizes = poffsets + nused * ncols * sizeof(uint32_t);
+  const unsigned char* pend = psizes + nused * ncols * sizeof(uint32_t);
+
+  if (pend > contents + index_len)
+    gold_fatal(_("%s: section %s is corrupt"), this->name_,
+              this->section_name(shndx).c_str());
+
+  int nmissing = 0;
+  for (File_list::const_iterator f = files.begin(); f != files.end(); ++f)
+    {
+      uint64_t dwo_id = f->dwo_id;
+      unsigned int slot = static_cast<unsigned int>(dwo_id) & (nslots - 1);
+      const unsigned char* ph = phash + slot * sizeof(uint64_t);
+      const unsigned char* pi = pindex + slot * sizeof(uint32_t);
+      uint64_t probe = elfcpp::Swap_unaligned<64, big_endian>::readval(ph);
+      uint32_t row_index = elfcpp::Swap_unaligned<32, big_endian>::readval(pi);
+      if (row_index != 0 && probe != dwo_id)
+       {
+         unsigned int h2 = ((static_cast<unsigned int>(dwo_id >> 32)
+                             & (nslots - 1)) | 1);
+         do
+           {
+             slot = (slot + h2) & (nslots - 1);
+             ph = phash + slot * sizeof(uint64_t);
+             pi = pindex + slot * sizeof(uint32_t);
+             probe = elfcpp::Swap_unaligned<64, big_endian>::readval(ph);
+             row_index = elfcpp::Swap_unaligned<32, big_endian>::readval(pi);
+           } while (row_index != 0 && probe != dwo_id);
+       }
+      if (row_index == 0)
+       {
+         printf(_("missing .dwo file: %016llx %s\n"),
+                static_cast<long long>(dwo_id), f->dwo_name.c_str());
+         ++nmissing;
+       }
+    }
+
+  gold_info(_("Found %d missing .dwo files"), nmissing);
+
+  if (index_is_new)
+    delete[] contents;
+
+  return nmissing == 0;
+}
+
 // Merge the input string table section into the output file.
 
 void
@@ -2063,7 +2212,10 @@ Dwo_name_info_reader::visit_compilation_unit(off_t, off_t, Dwarf_die* die)
 {
   const char* dwo_name = die->string_attribute(elfcpp::DW_AT_GNU_dwo_name);
   if (dwo_name != NULL)
-      this->files_->push_back(dwo_name);
+    {
+      uint64_t dwo_id = die->uint_attribute(elfcpp::DW_AT_GNU_dwo_id);
+      this->files_->push_back(Dwo_file_entry(dwo_id, dwo_name));
+    }
 }
 
 // Class Unit_reader.
@@ -2138,12 +2290,17 @@ using namespace gold;
 
 // Options.
 
+enum Dwp_options {
+  VERIFY_ONLY = 0x101,
+};
+
 struct option dwp_options[] =
   {
     { "exec", required_argument, NULL, 'e' },
     { "help", no_argument, NULL, 'h' },
     { "output", required_argument, NULL, 'o' },
     { "verbose", no_argument, NULL, 'v' },
+    { "verify-only", no_argument, NULL, VERIFY_ONLY },
     { "version", no_argument, NULL, 'V' },
     { NULL, 0, NULL, 0 }
   };
@@ -2156,9 +2313,11 @@ usage(FILE* fd, int exit_status)
   fprintf(fd, _("Usage: %s [options] [file...]\n"), program_name);
   fprintf(fd, _("  -h, --help               Print this help message\n"));
   fprintf(fd, _("  -e EXE, --exec EXE       Get list of dwo files from EXE"
-               " (defaults output to EXE.dwp)\n"));
+                                          " (defaults output to EXE.dwp)\n"));
   fprintf(fd, _("  -o FILE, --output FILE   Set output dwp file name\n"));
   fprintf(fd, _("  -v, --verbose            Verbose output\n"));
+  fprintf(fd, _("  --verify-only            Verify output file against"
+                                          " exec file\n"));
   fprintf(fd, _("  -V, --version            Print version number\n"));
 
   // REPORT_BUGS_TO is defined in bfd/bfdver.h.
@@ -2175,7 +2334,7 @@ print_version()
 {
   // This output is intended to follow the GNU standards.
   printf("GNU dwp %s\n", BFD_VERSION_STRING);
-  printf(_("Copyright 2012 Free Software Foundation, Inc.\n"));
+  printf(_("Copyright (C) 2020 Free Software Foundation, Inc.\n"));
   printf(_("\
 This program is free software; you may redistribute it under the terms of\n\
 the GNU General Public License version 3 or (at your option) any later version.\n\
@@ -2218,6 +2377,7 @@ main(int argc, char** argv)
   std::string output_filename;
   const char* exe_filename = NULL;
   bool verbose = false;
+  bool verify_only = false;
   int c;
   while ((c = getopt_long(argc, argv, "e:ho:vV", dwp_options, NULL)) != -1)
     {
@@ -2234,6 +2394,9 @@ main(int argc, char** argv)
          case 'v':
            verbose = true;
            break;
+         case VERIFY_ONLY:
+           verify_only = true;
+           break;
          case 'V':
            print_version();
          case '?':
@@ -2250,8 +2413,6 @@ main(int argc, char** argv)
       output_filename.append(".dwp");
     }
 
-  Dwp_output_file output_file(output_filename.c_str());
-
   // Get list of .dwo files from the executable.
   if (exe_filename != NULL)
     {
@@ -2261,20 +2422,29 @@ main(int argc, char** argv)
 
   // Add any additional files listed on command line.
   for (int i = optind; i < argc; ++i)
-    files.push_back(argv[i]);
+    files.push_back(Dwo_file_entry(0, argv[i]));
 
   if (exe_filename == NULL && files.empty())
     gold_fatal(_("no input files and no executable specified"));
 
+  if (verify_only)
+    {
+      // Get list of DWO files in the DWP file and compare with
+      // references found in the EXE file.
+      Dwo_file dwp_file(output_filename.c_str());
+      bool ok = dwp_file.verify(files);
+      return ok ? EXIT_SUCCESS : EXIT_FAILURE;
+    }
+
   // Process each file, adding its contents to the output file.
+  Dwp_output_file output_file(output_filename.c_str());
   for (File_list::const_iterator f = files.begin(); f != files.end(); ++f)
     {
       if (verbose)
-        fprintf(stderr, "%s\n", f->c_str());
-      Dwo_file dwo_file(f->c_str());
+       fprintf(stderr, "%s\n", f->dwo_name.c_str());
+      Dwo_file dwo_file(f->dwo_name.c_str());
       dwo_file.read(&output_file);
     }
-
   output_file.finalize();
 
   return EXIT_SUCCESS;
This page took 0.032059 seconds and 4 git commands to generate.