xref: /netbsd-src/external/gpl3/gdb.old/dist/gdb/dwarf2/dwz.c (revision f8cf1a9151c7af1cb0bd8b09c13c66bca599c027)
1 /* DWARF DWZ handling for GDB.
2 
3    Copyright (C) 2003-2023 Free Software Foundation, Inc.
4 
5    This file is part of GDB.
6 
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11 
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
19 
20 #include "defs.h"
21 #include "dwarf2/dwz.h"
22 
23 #include "build-id.h"
24 #include "debuginfod-support.h"
25 #include "dwarf2/read.h"
26 #include "dwarf2/sect-names.h"
27 #include "filenames.h"
28 #include "gdb_bfd.h"
29 #include "gdbcore.h"
30 #include "gdbsupport/pathstuff.h"
31 #include "gdbsupport/scoped_fd.h"
32 
33 const char *
34 dwz_file::read_string (struct objfile *objfile, LONGEST str_offset)
35 {
36   str.read (objfile);
37 
38   if (str.buffer == NULL)
39     error (_("DW_FORM_GNU_strp_alt used without .debug_str "
40 	     "section [in module %s]"),
41 	   bfd_get_filename (dwz_bfd.get ()));
42   if (str_offset >= str.size)
43     error (_("DW_FORM_GNU_strp_alt pointing outside of "
44 	     ".debug_str section [in module %s]"),
45 	   bfd_get_filename (dwz_bfd.get ()));
46   gdb_assert (HOST_CHAR_BIT == 8);
47   if (str.buffer[str_offset] == '\0')
48     return NULL;
49   return (const char *) (str.buffer + str_offset);
50 }
51 
52 /* A helper function to find the sections for a .dwz file.  */
53 
54 static void
55 locate_dwz_sections (bfd *abfd, asection *sectp, dwz_file *dwz_file)
56 {
57   /* Note that we only support the standard ELF names, because .dwz
58      is ELF-only (at the time of writing).  */
59   if (dwarf2_elf_names.abbrev.matches (sectp->name))
60     {
61       dwz_file->abbrev.s.section = sectp;
62       dwz_file->abbrev.size = bfd_section_size (sectp);
63     }
64   else if (dwarf2_elf_names.info.matches (sectp->name))
65     {
66       dwz_file->info.s.section = sectp;
67       dwz_file->info.size = bfd_section_size (sectp);
68     }
69   else if (dwarf2_elf_names.str.matches (sectp->name))
70     {
71       dwz_file->str.s.section = sectp;
72       dwz_file->str.size = bfd_section_size (sectp);
73     }
74   else if (dwarf2_elf_names.line.matches (sectp->name))
75     {
76       dwz_file->line.s.section = sectp;
77       dwz_file->line.size = bfd_section_size (sectp);
78     }
79   else if (dwarf2_elf_names.macro.matches (sectp->name))
80     {
81       dwz_file->macro.s.section = sectp;
82       dwz_file->macro.size = bfd_section_size (sectp);
83     }
84   else if (dwarf2_elf_names.gdb_index.matches (sectp->name))
85     {
86       dwz_file->gdb_index.s.section = sectp;
87       dwz_file->gdb_index.size = bfd_section_size (sectp);
88     }
89   else if (dwarf2_elf_names.debug_names.matches (sectp->name))
90     {
91       dwz_file->debug_names.s.section = sectp;
92       dwz_file->debug_names.size = bfd_section_size (sectp);
93     }
94 }
95 
96 /* Attempt to find a .dwz file (whose full path is represented by
97    FILENAME) in all of the specified debug file directories provided.
98 
99    Return the equivalent gdb_bfd_ref_ptr of the .dwz file found, or
100    nullptr if it could not find anything.  */
101 
102 static gdb_bfd_ref_ptr
103 dwz_search_other_debugdirs (std::string &filename, bfd_byte *buildid,
104 			    size_t buildid_len)
105 {
106   /* Let's assume that the path represented by FILENAME has the
107      "/.dwz/" subpath in it.  This is what (most) GNU/Linux
108      distributions do, anyway.  */
109   size_t dwz_pos = filename.find ("/.dwz/");
110 
111   if (dwz_pos == std::string::npos)
112     return nullptr;
113 
114   /* This is an obvious assertion, but it's here more to educate
115      future readers of this code that FILENAME at DWZ_POS *must*
116      contain a directory separator.  */
117   gdb_assert (IS_DIR_SEPARATOR (filename[dwz_pos]));
118 
119   gdb_bfd_ref_ptr dwz_bfd;
120   std::vector<gdb::unique_xmalloc_ptr<char>> debugdir_vec
121     = dirnames_to_char_ptr_vec (debug_file_directory.c_str ());
122 
123   for (const gdb::unique_xmalloc_ptr<char> &debugdir : debugdir_vec)
124     {
125       /* The idea is to iterate over the
126 	 debug file directories provided by the user and
127 	 replace the hard-coded path in the "filename" by each
128 	 debug-file-directory.
129 
130 	 For example, suppose that filename is:
131 
132 	   /usr/lib/debug/.dwz/foo.dwz
133 
134 	 And suppose that we have "$HOME/bar" as the
135 	 debug-file-directory.  We would then adjust filename
136 	 to look like:
137 
138 	   $HOME/bar/.dwz/foo.dwz
139 
140 	 which would hopefully allow us to find the alt debug
141 	 file.  */
142       std::string ddir = debugdir.get ();
143 
144       if (ddir.empty ())
145 	continue;
146 
147       /* Make sure the current debug-file-directory ends with a
148 	 directory separator.  This is needed because, if FILENAME
149 	 contains something like "/usr/lib/abcde/.dwz/foo.dwz" and
150 	 DDIR is "/usr/lib/abc", then could wrongfully skip it
151 	 below.  */
152       if (!IS_DIR_SEPARATOR (ddir.back ()))
153 	ddir += SLASH_STRING;
154 
155       /* Check whether the beginning of FILENAME is DDIR.  If it is,
156 	 then we are dealing with a file which we already attempted to
157 	 open before, so we just skip it and continue processing the
158 	 remaining debug file directories.  */
159       if (filename.size () > ddir.size ()
160 	  && filename.compare (0, ddir.size (), ddir) == 0)
161 	continue;
162 
163       /* Replace FILENAME's default debug-file-directory with
164 	 DDIR.  */
165       std::string new_filename = ddir + &filename[dwz_pos + 1];
166 
167       dwz_bfd = gdb_bfd_open (new_filename.c_str (), gnutarget);
168 
169       if (dwz_bfd == nullptr)
170 	continue;
171 
172       if (!build_id_verify (dwz_bfd.get (), buildid_len, buildid))
173 	{
174 	  dwz_bfd.reset (nullptr);
175 	  continue;
176 	}
177 
178       /* Found it.  */
179       break;
180     }
181 
182   return dwz_bfd;
183 }
184 
185 /* See dwz.h.  */
186 
187 struct dwz_file *
188 dwarf2_get_dwz_file (dwarf2_per_bfd *per_bfd, bool require)
189 {
190   bfd_size_type buildid_len_arg;
191   size_t buildid_len;
192   bfd_byte *buildid;
193 
194   if (per_bfd->dwz_file != NULL)
195     return per_bfd->dwz_file.get ();
196 
197   bfd_set_error (bfd_error_no_error);
198   gdb::unique_xmalloc_ptr<char> data
199     (bfd_get_alt_debug_link_info (per_bfd->obfd,
200 				  &buildid_len_arg, &buildid));
201   if (data == NULL)
202     {
203       if (bfd_get_error () == bfd_error_no_error)
204 	{
205 	  if (!require)
206 	    return nullptr;
207 	  error (_("could not read '.gnu_debugaltlink' section"));
208 	}
209       error (_("could not read '.gnu_debugaltlink' section: %s"),
210 	     bfd_errmsg (bfd_get_error ()));
211     }
212 
213   gdb::unique_xmalloc_ptr<bfd_byte> buildid_holder (buildid);
214 
215   buildid_len = (size_t) buildid_len_arg;
216 
217   std::string filename = data.get ();
218 
219   if (!IS_ABSOLUTE_PATH (filename.c_str ()))
220     {
221       gdb::unique_xmalloc_ptr<char> abs
222 	= gdb_realpath (bfd_get_filename (per_bfd->obfd));
223 
224       filename = ldirname (abs.get ()) + SLASH_STRING + filename;
225     }
226 
227   /* First try the file name given in the section.  If that doesn't
228      work, try to use the build-id instead.  */
229   gdb_bfd_ref_ptr dwz_bfd (gdb_bfd_open (filename.c_str (), gnutarget));
230   if (dwz_bfd != NULL)
231     {
232       if (!build_id_verify (dwz_bfd.get (), buildid_len, buildid))
233 	dwz_bfd.reset (nullptr);
234     }
235 
236   if (dwz_bfd == NULL)
237     dwz_bfd = build_id_to_debug_bfd (buildid_len, buildid);
238 
239   if (dwz_bfd == nullptr)
240     {
241       /* If the user has provided us with different
242 	 debug file directories, we can try them in order.  */
243       dwz_bfd = dwz_search_other_debugdirs (filename, buildid, buildid_len);
244     }
245 
246   if (dwz_bfd == nullptr)
247     {
248       gdb::unique_xmalloc_ptr<char> alt_filename;
249       const char *origname = bfd_get_filename (per_bfd->obfd);
250 
251       scoped_fd fd (debuginfod_debuginfo_query (buildid,
252 						buildid_len,
253 						origname,
254 						&alt_filename));
255 
256       if (fd.get () >= 0)
257 	{
258 	  /* File successfully retrieved from server.  */
259 	  dwz_bfd = gdb_bfd_open (alt_filename.get (), gnutarget);
260 
261 	  if (dwz_bfd == nullptr)
262 	    warning (_("File \"%s\" from debuginfod cannot be opened as bfd"),
263 		     alt_filename.get ());
264 	  else if (!build_id_verify (dwz_bfd.get (), buildid_len, buildid))
265 	    dwz_bfd.reset (nullptr);
266 	}
267     }
268 
269   if (dwz_bfd == NULL)
270     error (_("could not find '.gnu_debugaltlink' file for %s"),
271 	   bfd_get_filename (per_bfd->obfd));
272 
273   std::unique_ptr<struct dwz_file> result
274     (new struct dwz_file (std::move (dwz_bfd)));
275 
276   for (asection *sec : gdb_bfd_sections (result->dwz_bfd))
277     locate_dwz_sections (result->dwz_bfd.get (), sec, result.get ());
278 
279   gdb_bfd_record_inclusion (per_bfd->obfd, result->dwz_bfd.get ());
280   per_bfd->dwz_file = std::move (result);
281   return per_bfd->dwz_file.get ();
282 }
283