/src/elfutils/libdwfl/find-debuginfo.c
Line | Count | Source |
1 | | /* Standard find_debuginfo callback for libdwfl. |
2 | | Copyright (C) 2005-2010, 2014, 2015, 2019 Red Hat, Inc. |
3 | | This file is part of elfutils. |
4 | | |
5 | | This file is free software; you can redistribute it and/or modify |
6 | | it under the terms of either |
7 | | |
8 | | * the GNU Lesser General Public License as published by the Free |
9 | | Software Foundation; either version 3 of the License, or (at |
10 | | your option) any later version |
11 | | |
12 | | or |
13 | | |
14 | | * the GNU General Public License as published by the Free |
15 | | Software Foundation; either version 2 of the License, or (at |
16 | | your option) any later version |
17 | | |
18 | | or both in parallel, as here. |
19 | | |
20 | | elfutils is distributed in the hope that it will be useful, but |
21 | | WITHOUT ANY WARRANTY; without even the implied warranty of |
22 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
23 | | General Public License for more details. |
24 | | |
25 | | You should have received copies of the GNU General Public License and |
26 | | the GNU Lesser General Public License along with this program. If |
27 | | not, see <http://www.gnu.org/licenses/>. */ |
28 | | |
29 | | #ifdef HAVE_CONFIG_H |
30 | | # include <config.h> |
31 | | #endif |
32 | | |
33 | | #include "libdwflP.h" |
34 | | #include <stdio.h> |
35 | | #include <fcntl.h> |
36 | | #include <sys/stat.h> |
37 | | #include "system.h" |
38 | | |
39 | | |
40 | | /* Try to open [DIR/][SUBDIR/]DEBUGLINK, return file descriptor or -1. |
41 | | On success, *DEBUGINFO_FILE_NAME has the malloc'd name of the open file. */ |
42 | | static int |
43 | | try_open (const struct stat *main_stat, |
44 | | const char *dir, const char *subdir, const char *debuglink, |
45 | | char **debuginfo_file_name) |
46 | 4.27k | { |
47 | 4.27k | char *fname; |
48 | 4.27k | if (dir == NULL && subdir == NULL) |
49 | 0 | { |
50 | 0 | fname = strdup (debuglink); |
51 | 0 | if (unlikely (fname == NULL)) |
52 | 0 | return -1; |
53 | 0 | } |
54 | 4.27k | else if ((subdir == NULL ? asprintf (&fname, "%s/%s", dir, debuglink) |
55 | 4.27k | : dir == NULL ? asprintf (&fname, "%s/%s", subdir, debuglink) |
56 | 4 | : asprintf (&fname, "%s/%s/%s", dir, subdir, debuglink)) < 0) |
57 | 0 | return -1; |
58 | | |
59 | 4.27k | struct stat st; |
60 | 4.27k | int fd = TEMP_FAILURE_RETRY (open (fname, O_RDONLY)); |
61 | 4.27k | if (fd < 0) |
62 | 4.21k | free (fname); |
63 | 61 | else if (fstat (fd, &st) == 0 |
64 | 61 | && st.st_ino == main_stat->st_ino |
65 | 0 | && st.st_dev == main_stat->st_dev) |
66 | 0 | { |
67 | | /* This is the main file by another name. Don't look at it again. */ |
68 | 0 | free (fname); |
69 | 0 | close (fd); |
70 | 0 | errno = ENOENT; |
71 | 0 | fd = -1; |
72 | 0 | } |
73 | 61 | else |
74 | 61 | *debuginfo_file_name = fname; |
75 | | |
76 | 4.27k | return fd; |
77 | 4.27k | } |
78 | | |
79 | | /* Return true iff the FD's contents CRC matches DEBUGLINK_CRC. */ |
80 | | static inline bool |
81 | | check_crc (int fd, GElf_Word debuglink_crc) |
82 | 48 | { |
83 | 48 | uint32_t file_crc; |
84 | 48 | return (__libdwfl_crc32_file (fd, &file_crc) == 0 |
85 | 0 | && file_crc == debuglink_crc); |
86 | 48 | } |
87 | | |
88 | | static bool |
89 | | validate (Dwfl_Module *mod, int fd, bool check, GElf_Word debuglink_crc) |
90 | 61 | { |
91 | | /* For alt debug files always check the build-id from the Dwarf and alt. */ |
92 | 61 | if (mod->dw != NULL) |
93 | 3 | { |
94 | 3 | bool valid = false; |
95 | 3 | const void *build_id; |
96 | 3 | const char *altname; |
97 | 3 | ssize_t build_id_len = INTUSE(dwelf_dwarf_gnu_debugaltlink) (mod->dw, |
98 | 3 | &altname, |
99 | 3 | &build_id); |
100 | 3 | if (build_id_len > 0) |
101 | 3 | { |
102 | | /* We need to open an Elf handle on the file so we can check its |
103 | | build ID note for validation. Backdoor the handle into the |
104 | | module data structure since we had to open it early anyway. */ |
105 | 3 | Dwfl_Error error = __libdw_open_file (&fd, &mod->alt_elf, |
106 | 3 | false, false); |
107 | 3 | if (error != DWFL_E_NOERROR) |
108 | 3 | __libdwfl_seterrno (error); |
109 | 0 | else |
110 | 0 | { |
111 | 0 | const void *alt_build_id; |
112 | 0 | ssize_t alt_len = INTUSE(dwelf_elf_gnu_build_id) (mod->alt_elf, |
113 | 0 | &alt_build_id); |
114 | 0 | if (alt_len > 0 && alt_len == build_id_len |
115 | 0 | && memcmp (build_id, alt_build_id, alt_len) == 0) |
116 | 0 | valid = true; |
117 | 0 | else |
118 | 0 | { |
119 | | /* A mismatch! */ |
120 | 0 | elf_end (mod->alt_elf); |
121 | 0 | mod->alt_elf = NULL; |
122 | 0 | close (fd); |
123 | 0 | fd = -1; |
124 | 0 | } |
125 | 0 | } |
126 | 3 | } |
127 | 3 | return valid; |
128 | 3 | } |
129 | | |
130 | | /* If we have a build ID, check only that. */ |
131 | 58 | if (mod->build_id_len > 0) |
132 | 0 | { |
133 | | /* We need to open an Elf handle on the file so we can check its |
134 | | build ID note for validation. Backdoor the handle into the |
135 | | module data structure since we had to open it early anyway. */ |
136 | |
|
137 | 0 | mod->debug.valid = false; |
138 | 0 | Dwfl_Error error = __libdw_open_file (&fd, &mod->debug.elf, false, false); |
139 | 0 | if (error != DWFL_E_NOERROR) |
140 | 0 | __libdwfl_seterrno (error); |
141 | 0 | else if (likely (__libdwfl_find_build_id (mod, false, |
142 | 0 | mod->debug.elf) == 2)) |
143 | | /* Also backdoor the gratuitous flag. */ |
144 | 0 | mod->debug.valid = true; |
145 | 0 | else |
146 | 0 | { |
147 | | /* A mismatch! */ |
148 | 0 | elf_end (mod->debug.elf); |
149 | 0 | mod->debug.elf = NULL; |
150 | 0 | close (fd); |
151 | 0 | fd = -1; |
152 | 0 | } |
153 | |
|
154 | 0 | return mod->debug.valid; |
155 | 0 | } |
156 | | |
157 | 58 | return !check || check_crc (fd, debuglink_crc); |
158 | 58 | } |
159 | | |
160 | | static int |
161 | | find_debuginfo_in_path (Dwfl_Module *mod, const char *file_name, |
162 | | const char *debuglink_file, GElf_Word debuglink_crc, |
163 | | char **debuginfo_file_name) |
164 | 4.27k | { |
165 | 4.27k | bool cancheck = debuglink_crc != (GElf_Word) 0; |
166 | | |
167 | 4.27k | const char *file_basename = file_name == NULL ? NULL : xbasename (file_name); |
168 | 4.27k | char *localname = NULL; |
169 | | |
170 | | /* We invent a debuglink .debug name if NULL, but then want to try the |
171 | | basename too. */ |
172 | 4.27k | bool debuglink_null = debuglink_file == NULL; |
173 | 4.27k | if (debuglink_null) |
174 | 4.18k | { |
175 | | /* For a alt debug multi file we need a name, for a separate debug |
176 | | name we may be able to fall back on file_basename.debug. */ |
177 | 4.18k | if (file_basename == NULL || mod->dw != NULL) |
178 | 0 | { |
179 | 0 | errno = 0; |
180 | 0 | return -1; |
181 | 0 | } |
182 | | |
183 | 4.18k | size_t len = strlen (file_basename); |
184 | 4.18k | localname = malloc (len + sizeof ".debug"); |
185 | 4.18k | if (unlikely (localname == NULL)) |
186 | 0 | return -1; |
187 | 4.18k | memcpy (localname, file_basename, len); |
188 | 4.18k | memcpy (&localname[len], ".debug", sizeof ".debug"); |
189 | 4.18k | debuglink_file = localname; |
190 | 4.18k | cancheck = false; |
191 | 4.18k | } |
192 | | |
193 | | /* Look for a file named DEBUGLINK_FILE in the directories |
194 | | indicated by the debug directory path setting. */ |
195 | | |
196 | 4.27k | const Dwfl_Callbacks *const cb = mod->dwfl->callbacks; |
197 | 4.27k | char *localpath = strdup ((cb->debuginfo_path ? *cb->debuginfo_path : NULL) |
198 | 4.27k | ?: DEFAULT_DEBUGINFO_PATH); |
199 | 4.27k | if (unlikely (localpath == NULL)) |
200 | 0 | { |
201 | 0 | free (localname); |
202 | 0 | return -1; |
203 | 0 | } |
204 | | |
205 | | /* A leading - or + in the whole path sets whether to check file CRCs. */ |
206 | 4.27k | bool defcheck = true; |
207 | 4.27k | char *path = localpath; |
208 | 4.27k | if (path[0] == '-' || path[0] == '+') |
209 | 0 | { |
210 | 0 | defcheck = path[0] == '+'; |
211 | 0 | ++path; |
212 | 0 | } |
213 | | |
214 | | /* XXX dev/ino should be cached in struct dwfl_file. */ |
215 | 4.27k | struct stat main_stat; |
216 | 4.27k | if (unlikely ((mod->main.fd != -1 ? fstat (mod->main.fd, &main_stat) |
217 | 4.27k | : file_name != NULL ? stat (file_name, &main_stat) |
218 | 4.27k | : -1) < 0)) |
219 | 457 | { |
220 | 457 | main_stat.st_dev = 0; |
221 | 457 | main_stat.st_ino = 0; |
222 | 457 | } |
223 | | |
224 | 4.27k | char *file_dirname = (file_basename == file_name ? NULL |
225 | 4.27k | : strndup (file_name, file_basename - 1 - file_name)); |
226 | 4.27k | if (file_basename != file_name && file_dirname == NULL) |
227 | 0 | { |
228 | 0 | free (localpath); |
229 | 0 | free (localname); |
230 | 0 | return -1; |
231 | 0 | } |
232 | 4.27k | char *p; |
233 | 8.52k | while ((p = strsep (&path, ":")) != NULL) |
234 | 4.27k | { |
235 | | /* A leading - or + says whether to check file CRCs for this element. */ |
236 | 4.27k | bool check = defcheck; |
237 | 4.27k | if (*p == '+' || *p == '-') |
238 | 0 | check = *p++ == '+'; |
239 | 4.27k | check = check && cancheck; |
240 | | |
241 | | /* Try the basename too, if we made up the debuglink name and this |
242 | | is not the main directory. */ |
243 | 4.27k | bool try_file_basename; |
244 | | |
245 | 4.27k | const char *dir, *subdir, *file; |
246 | 4.27k | switch (p[0]) |
247 | 4.27k | { |
248 | 4.27k | case '\0': |
249 | | /* An empty entry says to try the main file's directory. */ |
250 | 4.27k | dir = file_dirname; |
251 | 4.27k | subdir = NULL; |
252 | 4.27k | file = debuglink_file; |
253 | 4.27k | try_file_basename = false; |
254 | 4.27k | break; |
255 | 0 | case '/': |
256 | | /* An absolute path says to look there for a subdirectory |
257 | | named by the main file's absolute directory. This cannot |
258 | | be applied to a relative file name. For alt debug files |
259 | | it means to look for the basename file in that dir or the |
260 | | .dwz subdir (see below). */ |
261 | 0 | if (mod->dw == NULL |
262 | 0 | && (file_dirname == NULL || file_dirname[0] != '/')) |
263 | 0 | continue; |
264 | 0 | dir = p; |
265 | 0 | if (mod->dw == NULL) |
266 | 0 | { |
267 | 0 | subdir = file_dirname; |
268 | | /* We want to explore all sub-subdirs. Chop off one slash |
269 | | at a time. */ |
270 | 0 | explore_dir: |
271 | 0 | subdir = strchr (subdir, '/'); |
272 | 0 | if (subdir != NULL) |
273 | 0 | subdir = subdir + 1; |
274 | 0 | if (subdir && *subdir == 0) |
275 | 0 | continue; |
276 | 0 | file = debuglink_file; |
277 | 0 | } |
278 | 0 | else |
279 | 0 | { |
280 | 0 | subdir = NULL; |
281 | 0 | file = xbasename (debuglink_file); |
282 | 0 | } |
283 | 0 | try_file_basename = debuglink_null; |
284 | 0 | break; |
285 | 0 | default: |
286 | | /* A relative path says to try a subdirectory of that name |
287 | | in the main file's directory. */ |
288 | 0 | dir = file_dirname; |
289 | 0 | subdir = p; |
290 | 0 | file = debuglink_file; |
291 | 0 | try_file_basename = debuglink_null; |
292 | 0 | break; |
293 | 4.27k | } |
294 | | |
295 | 4.27k | char *fname = NULL; |
296 | 4.27k | int fd = try_open (&main_stat, dir, subdir, file, &fname); |
297 | 4.27k | if (fd < 0 && try_file_basename) |
298 | 0 | fd = try_open (&main_stat, dir, subdir, file_basename, &fname); |
299 | 4.27k | if (fd < 0) |
300 | 4.21k | switch (errno) |
301 | 4.21k | { |
302 | 4.19k | case ENOENT: |
303 | 4.19k | case ENOTDIR: |
304 | | /* If we are looking for the alt file also try the .dwz subdir. |
305 | | But only if this is the empty or absolute path. */ |
306 | 4.19k | if (mod->dw != NULL && (p[0] == '\0' || p[0] == '/')) |
307 | 4 | { |
308 | 4 | fd = try_open (&main_stat, dir, ".dwz", |
309 | 4 | xbasename (file), &fname); |
310 | 4 | if (fd < 0) |
311 | 4 | { |
312 | 4 | if (errno != ENOENT && errno != ENOTDIR) |
313 | 0 | goto fail_free; |
314 | 4 | else |
315 | 4 | continue; |
316 | 4 | } |
317 | 0 | break; |
318 | 4 | } |
319 | | /* If possible try again with a sub-subdir. */ |
320 | 4.19k | if (mod->dw == NULL && subdir) |
321 | 0 | goto explore_dir; |
322 | 4.19k | continue; |
323 | 4.19k | default: |
324 | 15 | goto fail_free; |
325 | 4.21k | } |
326 | 61 | if (validate (mod, fd, check, debuglink_crc)) |
327 | 10 | { |
328 | 10 | free (localpath); |
329 | 10 | free (localname); |
330 | 10 | free (file_dirname); |
331 | 10 | *debuginfo_file_name = fname; |
332 | 10 | return fd; |
333 | 10 | } |
334 | 51 | free (fname); |
335 | 51 | close (fd); |
336 | 51 | } |
337 | | |
338 | | /* No dice. */ |
339 | 4.27k | errno = 0; |
340 | 4.26k | fail_free: |
341 | 4.26k | free (localpath); |
342 | 4.26k | free (localname); |
343 | 4.26k | free (file_dirname); |
344 | 4.26k | return -1; |
345 | 4.24k | } |
346 | | |
347 | | int |
348 | | dwfl_standard_find_debuginfo (Dwfl_Module *mod, |
349 | | void **userdata __attribute__ ((unused)), |
350 | | const char *modname __attribute__ ((unused)), |
351 | | GElf_Addr base __attribute__ ((unused)), |
352 | | const char *file_name, |
353 | | const char *debuglink_file, |
354 | | GElf_Word debuglink_crc, |
355 | | char **debuginfo_file_name) |
356 | 4.27k | { |
357 | 4.27k | if (mod == NULL) |
358 | 0 | return -1; |
359 | | |
360 | | /* First try by build ID if we have one. If that succeeds or fails |
361 | | other than just by finding nothing, that's all we do. */ |
362 | 4.27k | const unsigned char *bits = NULL; |
363 | 4.27k | GElf_Addr vaddr; |
364 | 4.27k | int bits_len; |
365 | 4.27k | if ((bits_len = INTUSE(dwfl_module_build_id) (mod, &bits, &vaddr)) > 0) |
366 | 112 | { |
367 | | /* Dropping most arguments means we cannot rely on them in |
368 | | dwfl_build_id_find_debuginfo. But leave it that way since |
369 | | some user code out there also does this, so we'll have to |
370 | | handle it anyway. */ |
371 | 112 | int fd = INTUSE(dwfl_build_id_find_debuginfo) (mod, |
372 | 112 | NULL, NULL, 0, |
373 | 112 | NULL, NULL, 0, |
374 | 112 | debuginfo_file_name); |
375 | | |
376 | | /* Did the build_id callback find something or report an error? |
377 | | Then we are done. Otherwise fallback on path based search. */ |
378 | 112 | if (fd >= 0 |
379 | 112 | || (mod->dw == NULL && mod->debug.elf != NULL) |
380 | 112 | || (mod->dw != NULL && mod->alt_elf != NULL) |
381 | 112 | || errno != 0) |
382 | 0 | return fd; |
383 | 112 | } |
384 | | |
385 | | /* Failing that, search the path by name. */ |
386 | 4.27k | int fd = find_debuginfo_in_path (mod, file_name, |
387 | 4.27k | debuglink_file, debuglink_crc, |
388 | 4.27k | debuginfo_file_name); |
389 | | |
390 | 4.27k | if (fd < 0 && errno == 0 && file_name != NULL) |
391 | 4.24k | { |
392 | | /* If FILE_NAME is a symlink, the debug file might be associated |
393 | | with the symlink target name instead. */ |
394 | | |
395 | 4.24k | char *canon = realpath (file_name, NULL); |
396 | 4.24k | if (canon != NULL && strcmp (file_name, canon)) |
397 | 0 | fd = find_debuginfo_in_path (mod, canon, |
398 | 0 | debuglink_file, debuglink_crc, |
399 | 0 | debuginfo_file_name); |
400 | 4.24k | free (canon); |
401 | 4.24k | } |
402 | | |
403 | | #ifdef ENABLE_LIBDEBUGINFOD |
404 | | /* Still nothing? Try if we can use the debuginfod client. |
405 | | But note that we might be looking for the alt file. |
406 | | We use the same trick as dwfl_build_id_find_debuginfo. |
407 | | If the debug file (dw) is already set, then we must be |
408 | | looking for the altfile. But we cannot use the actual |
409 | | file/path name given as hint. We'll have to lookup the |
410 | | alt file "build-id". Because the debuginfod client only |
411 | | handles build-ids. */ |
412 | | if (fd < 0) |
413 | | { |
414 | | if (mod->dw != NULL) |
415 | | { |
416 | | const char *altname; |
417 | | bits_len = INTUSE(dwelf_dwarf_gnu_debugaltlink) (mod->dw, &altname, |
418 | | (const void **) |
419 | | &bits); |
420 | | } |
421 | | |
422 | | if (bits_len > 0) |
423 | | fd = __libdwfl_debuginfod_find_debuginfo (mod->dwfl, bits, bits_len); |
424 | | } |
425 | | #endif |
426 | | |
427 | 4.27k | return fd; |
428 | 4.27k | } |
429 | | INTDEF (dwfl_standard_find_debuginfo) |