| /* DWARF DWZ handling for GDB. | 
 |  | 
 |    Copyright (C) 2003-2023 Free Software Foundation, Inc. | 
 |  | 
 |    This file is part of GDB. | 
 |  | 
 |    This program is free software; you can redistribute it and/or modify | 
 |    it under the terms of the GNU General Public License as published by | 
 |    the Free Software Foundation; either version 3 of the License, or | 
 |    (at your option) any later version. | 
 |  | 
 |    This program is distributed in the hope that it will be useful, | 
 |    but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 |    GNU General Public License for more details. | 
 |  | 
 |    You should have received a copy of the GNU General Public License | 
 |    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ | 
 |  | 
 | #include "defs.h" | 
 | #include "dwarf2/dwz.h" | 
 |  | 
 | #include "build-id.h" | 
 | #include "debuginfod-support.h" | 
 | #include "dwarf2/read.h" | 
 | #include "dwarf2/sect-names.h" | 
 | #include "filenames.h" | 
 | #include "gdb_bfd.h" | 
 | #include "gdbcore.h" | 
 | #include "gdbsupport/pathstuff.h" | 
 | #include "gdbsupport/scoped_fd.h" | 
 |  | 
 | const char * | 
 | dwz_file::read_string (struct objfile *objfile, LONGEST str_offset) | 
 | { | 
 |   str.read (objfile); | 
 |  | 
 |   if (str.buffer == NULL) | 
 |     error (_("DW_FORM_GNU_strp_alt used without .debug_str " | 
 | 	     "section [in module %s]"), | 
 | 	   bfd_get_filename (dwz_bfd.get ())); | 
 |   if (str_offset >= str.size) | 
 |     error (_("DW_FORM_GNU_strp_alt pointing outside of " | 
 | 	     ".debug_str section [in module %s]"), | 
 | 	   bfd_get_filename (dwz_bfd.get ())); | 
 |   gdb_assert (HOST_CHAR_BIT == 8); | 
 |   if (str.buffer[str_offset] == '\0') | 
 |     return NULL; | 
 |   return (const char *) (str.buffer + str_offset); | 
 | } | 
 |  | 
 | /* A helper function to find the sections for a .dwz file.  */ | 
 |  | 
 | static void | 
 | locate_dwz_sections (bfd *abfd, asection *sectp, dwz_file *dwz_file) | 
 | { | 
 |   /* Note that we only support the standard ELF names, because .dwz | 
 |      is ELF-only (at the time of writing).  */ | 
 |   if (dwarf2_elf_names.abbrev.matches (sectp->name)) | 
 |     { | 
 |       dwz_file->abbrev.s.section = sectp; | 
 |       dwz_file->abbrev.size = bfd_section_size (sectp); | 
 |     } | 
 |   else if (dwarf2_elf_names.info.matches (sectp->name)) | 
 |     { | 
 |       dwz_file->info.s.section = sectp; | 
 |       dwz_file->info.size = bfd_section_size (sectp); | 
 |     } | 
 |   else if (dwarf2_elf_names.str.matches (sectp->name)) | 
 |     { | 
 |       dwz_file->str.s.section = sectp; | 
 |       dwz_file->str.size = bfd_section_size (sectp); | 
 |     } | 
 |   else if (dwarf2_elf_names.line.matches (sectp->name)) | 
 |     { | 
 |       dwz_file->line.s.section = sectp; | 
 |       dwz_file->line.size = bfd_section_size (sectp); | 
 |     } | 
 |   else if (dwarf2_elf_names.macro.matches (sectp->name)) | 
 |     { | 
 |       dwz_file->macro.s.section = sectp; | 
 |       dwz_file->macro.size = bfd_section_size (sectp); | 
 |     } | 
 |   else if (dwarf2_elf_names.gdb_index.matches (sectp->name)) | 
 |     { | 
 |       dwz_file->gdb_index.s.section = sectp; | 
 |       dwz_file->gdb_index.size = bfd_section_size (sectp); | 
 |     } | 
 |   else if (dwarf2_elf_names.debug_names.matches (sectp->name)) | 
 |     { | 
 |       dwz_file->debug_names.s.section = sectp; | 
 |       dwz_file->debug_names.size = bfd_section_size (sectp); | 
 |     } | 
 | } | 
 |  | 
 | /* Attempt to find a .dwz file (whose full path is represented by | 
 |    FILENAME) in all of the specified debug file directories provided. | 
 |  | 
 |    Return the equivalent gdb_bfd_ref_ptr of the .dwz file found, or | 
 |    nullptr if it could not find anything.  */ | 
 |  | 
 | static gdb_bfd_ref_ptr | 
 | dwz_search_other_debugdirs (std::string &filename, bfd_byte *buildid, | 
 | 			    size_t buildid_len) | 
 | { | 
 |   /* Let's assume that the path represented by FILENAME has the | 
 |      "/.dwz/" subpath in it.  This is what (most) GNU/Linux | 
 |      distributions do, anyway.  */ | 
 |   size_t dwz_pos = filename.find ("/.dwz/"); | 
 |  | 
 |   if (dwz_pos == std::string::npos) | 
 |     return nullptr; | 
 |  | 
 |   /* This is an obvious assertion, but it's here more to educate | 
 |      future readers of this code that FILENAME at DWZ_POS *must* | 
 |      contain a directory separator.  */ | 
 |   gdb_assert (IS_DIR_SEPARATOR (filename[dwz_pos])); | 
 |  | 
 |   gdb_bfd_ref_ptr dwz_bfd; | 
 |   std::vector<gdb::unique_xmalloc_ptr<char>> debugdir_vec | 
 |     = dirnames_to_char_ptr_vec (debug_file_directory.c_str ()); | 
 |  | 
 |   for (const gdb::unique_xmalloc_ptr<char> &debugdir : debugdir_vec) | 
 |     { | 
 |       /* The idea is to iterate over the | 
 | 	 debug file directories provided by the user and | 
 | 	 replace the hard-coded path in the "filename" by each | 
 | 	 debug-file-directory. | 
 |  | 
 | 	 For example, suppose that filename is: | 
 |  | 
 | 	   /usr/lib/debug/.dwz/foo.dwz | 
 |  | 
 | 	 And suppose that we have "$HOME/bar" as the | 
 | 	 debug-file-directory.  We would then adjust filename | 
 | 	 to look like: | 
 |  | 
 | 	   $HOME/bar/.dwz/foo.dwz | 
 |  | 
 | 	 which would hopefully allow us to find the alt debug | 
 | 	 file.  */ | 
 |       std::string ddir = debugdir.get (); | 
 |  | 
 |       if (ddir.empty ()) | 
 | 	continue; | 
 |  | 
 |       /* Make sure the current debug-file-directory ends with a | 
 | 	 directory separator.  This is needed because, if FILENAME | 
 | 	 contains something like "/usr/lib/abcde/.dwz/foo.dwz" and | 
 | 	 DDIR is "/usr/lib/abc", then could wrongfully skip it | 
 | 	 below.  */ | 
 |       if (!IS_DIR_SEPARATOR (ddir.back ())) | 
 | 	ddir += SLASH_STRING; | 
 |  | 
 |       /* Check whether the beginning of FILENAME is DDIR.  If it is, | 
 | 	 then we are dealing with a file which we already attempted to | 
 | 	 open before, so we just skip it and continue processing the | 
 | 	 remaining debug file directories.  */ | 
 |       if (filename.size () > ddir.size () | 
 | 	  && filename.compare (0, ddir.size (), ddir) == 0) | 
 | 	continue; | 
 |  | 
 |       /* Replace FILENAME's default debug-file-directory with | 
 | 	 DDIR.  */ | 
 |       std::string new_filename = ddir + &filename[dwz_pos + 1]; | 
 |  | 
 |       dwz_bfd = gdb_bfd_open (new_filename.c_str (), gnutarget); | 
 |  | 
 |       if (dwz_bfd == nullptr) | 
 | 	continue; | 
 |  | 
 |       if (!build_id_verify (dwz_bfd.get (), buildid_len, buildid)) | 
 | 	{ | 
 | 	  dwz_bfd.reset (nullptr); | 
 | 	  continue; | 
 | 	} | 
 |  | 
 |       /* Found it.  */ | 
 |       break; | 
 |     } | 
 |  | 
 |   return dwz_bfd; | 
 | } | 
 |  | 
 | /* See dwz.h.  */ | 
 |  | 
 | struct dwz_file * | 
 | dwarf2_get_dwz_file (dwarf2_per_bfd *per_bfd, bool require) | 
 | { | 
 |   bfd_size_type buildid_len_arg; | 
 |   size_t buildid_len; | 
 |   bfd_byte *buildid; | 
 |  | 
 |   if (per_bfd->dwz_file != NULL) | 
 |     return per_bfd->dwz_file.get (); | 
 |  | 
 |   bfd_set_error (bfd_error_no_error); | 
 |   gdb::unique_xmalloc_ptr<char> data | 
 |     (bfd_get_alt_debug_link_info (per_bfd->obfd, | 
 | 				  &buildid_len_arg, &buildid)); | 
 |   if (data == NULL) | 
 |     { | 
 |       if (bfd_get_error () == bfd_error_no_error) | 
 | 	{ | 
 | 	  if (!require) | 
 | 	    return nullptr; | 
 | 	  error (_("could not read '.gnu_debugaltlink' section")); | 
 | 	} | 
 |       error (_("could not read '.gnu_debugaltlink' section: %s"), | 
 | 	     bfd_errmsg (bfd_get_error ())); | 
 |     } | 
 |  | 
 |   gdb::unique_xmalloc_ptr<bfd_byte> buildid_holder (buildid); | 
 |  | 
 |   buildid_len = (size_t) buildid_len_arg; | 
 |  | 
 |   std::string filename = data.get (); | 
 |  | 
 |   if (!IS_ABSOLUTE_PATH (filename.c_str ())) | 
 |     { | 
 |       gdb::unique_xmalloc_ptr<char> abs | 
 | 	= gdb_realpath (bfd_get_filename (per_bfd->obfd)); | 
 |  | 
 |       filename = ldirname (abs.get ()) + SLASH_STRING + filename; | 
 |     } | 
 |  | 
 |   /* First try the file name given in the section.  If that doesn't | 
 |      work, try to use the build-id instead.  */ | 
 |   gdb_bfd_ref_ptr dwz_bfd (gdb_bfd_open (filename.c_str (), gnutarget)); | 
 |   if (dwz_bfd != NULL) | 
 |     { | 
 |       if (!build_id_verify (dwz_bfd.get (), buildid_len, buildid)) | 
 | 	dwz_bfd.reset (nullptr); | 
 |     } | 
 |  | 
 |   if (dwz_bfd == NULL) | 
 |     dwz_bfd = build_id_to_debug_bfd (buildid_len, buildid); | 
 |  | 
 |   if (dwz_bfd == nullptr) | 
 |     { | 
 |       /* If the user has provided us with different | 
 | 	 debug file directories, we can try them in order.  */ | 
 |       dwz_bfd = dwz_search_other_debugdirs (filename, buildid, buildid_len); | 
 |     } | 
 |  | 
 |   if (dwz_bfd == nullptr) | 
 |     { | 
 |       gdb::unique_xmalloc_ptr<char> alt_filename; | 
 |       const char *origname = bfd_get_filename (per_bfd->obfd); | 
 |  | 
 |       scoped_fd fd (debuginfod_debuginfo_query (buildid, | 
 | 						buildid_len, | 
 | 						origname, | 
 | 						&alt_filename)); | 
 |  | 
 |       if (fd.get () >= 0) | 
 | 	{ | 
 | 	  /* File successfully retrieved from server.  */ | 
 | 	  dwz_bfd = gdb_bfd_open (alt_filename.get (), gnutarget); | 
 |  | 
 | 	  if (dwz_bfd == nullptr) | 
 | 	    warning (_("File \"%s\" from debuginfod cannot be opened as bfd"), | 
 | 		     alt_filename.get ()); | 
 | 	  else if (!build_id_verify (dwz_bfd.get (), buildid_len, buildid)) | 
 | 	    dwz_bfd.reset (nullptr); | 
 | 	} | 
 |     } | 
 |  | 
 |   if (dwz_bfd == NULL) | 
 |     error (_("could not find '.gnu_debugaltlink' file for %s"), | 
 | 	   bfd_get_filename (per_bfd->obfd)); | 
 |  | 
 |   std::unique_ptr<struct dwz_file> result | 
 |     (new struct dwz_file (std::move (dwz_bfd))); | 
 |  | 
 |   for (asection *sec : gdb_bfd_sections (result->dwz_bfd)) | 
 |     locate_dwz_sections (result->dwz_bfd.get (), sec, result.get ()); | 
 |  | 
 |   gdb_bfd_record_inclusion (per_bfd->obfd, result->dwz_bfd.get ()); | 
 |   per_bfd->dwz_file = std::move (result); | 
 |   return per_bfd->dwz_file.get (); | 
 | } |