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