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