Commit | Line | Data |
---|---|---|
0314b390 TT |
1 | /* DWARF DWZ handling for GDB. |
2 | ||
88b9d363 | 3 | Copyright (C) 2003-2022 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" | |
32 | ||
0314b390 TT |
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 | } | |
9938d15a TT |
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); | |
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 | } |