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 <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 : 1286 : try_open (const struct stat *main_stat,
44 : : const char *dir, const char *subdir, const char *debuglink,
45 : : char **debuginfo_file_name)
46 : : {
47 : 1286 : char *fname;
48 [ + + ]: 1286 : if (dir == NULL && subdir == NULL)
49 : : {
50 : 104 : fname = strdup (debuglink);
51 [ + - ]: 104 : if (unlikely (fname == NULL))
52 : : return -1;
53 : : }
54 [ + - ]: 1182 : else if ((subdir == NULL ? asprintf (&fname, "%s/%s", dir, debuglink)
55 : 56 : : dir == NULL ? asprintf (&fname, "%s/%s", subdir, debuglink)
56 [ + + + + ]: 2364 : : asprintf (&fname, "%s/%s/%s", dir, subdir, debuglink)) < 0)
57 : : return -1;
58 : :
59 : 1286 : struct stat st;
60 [ + + - + ]: 1286 : int fd = TEMP_FAILURE_RETRY (open (fname, O_RDONLY));
61 [ + + ]: 1286 : if (fd < 0)
62 : 1167 : free (fname);
63 [ + - ]: 119 : else if (fstat (fd, &st) == 0
64 [ + + ]: 119 : && st.st_ino == main_stat->st_ino
65 [ + - ]: 72 : && st.st_dev == main_stat->st_dev)
66 : : {
67 : : /* This is the main file by another name. Don't look at it again. */
68 : 72 : free (fname);
69 : 72 : close (fd);
70 : 72 : errno = ENOENT;
71 : 72 : fd = -1;
72 : : }
73 : : else
74 : 47 : *debuginfo_file_name = fname;
75 : :
76 : : return fd;
77 : : }
78 : :
79 : : /* Return true iff the FD's contents CRC matches DEBUGLINK_CRC. */
80 : : static inline bool
81 : 1 : check_crc (int fd, GElf_Word debuglink_crc)
82 : : {
83 : 1 : uint32_t file_crc;
84 : 1 : return (__libdwfl_crc32_file (fd, &file_crc) == 0
85 [ + - - + ]: 1 : && file_crc == debuglink_crc);
86 : : }
87 : :
88 : : static bool
89 : 47 : validate (Dwfl_Module *mod, int fd, bool check, GElf_Word debuglink_crc)
90 : : {
91 : : /* For alt debug files always check the build-id from the Dwarf and alt. */
92 [ + + ]: 47 : if (mod->dw != NULL)
93 : : {
94 : 13 : bool valid = false;
95 : 13 : const void *build_id;
96 : 13 : const char *altname;
97 : 13 : ssize_t build_id_len = INTUSE(dwelf_dwarf_gnu_debugaltlink) (mod->dw,
98 : : &altname,
99 : : &build_id);
100 [ + - ]: 13 : if (build_id_len > 0)
101 : : {
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 : 13 : Dwfl_Error error = __libdw_open_file (&fd, &mod->alt_elf,
106 : : false, false);
107 [ - + ]: 13 : if (error != DWFL_E_NOERROR)
108 : 0 : __libdwfl_seterrno (error);
109 : : else
110 : : {
111 : 13 : const void *alt_build_id;
112 : 13 : ssize_t alt_len = INTUSE(dwelf_elf_gnu_build_id) (mod->alt_elf,
113 : : &alt_build_id);
114 [ + - ]: 13 : if (alt_len > 0 && alt_len == build_id_len
115 [ - + ]: 13 : && memcmp (build_id, alt_build_id, alt_len) == 0)
116 : : valid = true;
117 : : else
118 : : {
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 : : }
125 : : }
126 : : }
127 : 13 : return valid;
128 : : }
129 : :
130 : : /* If we have a build ID, check only that. */
131 [ + + ]: 34 : if (mod->build_id_len > 0)
132 : : {
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 : 33 : mod->debug.valid = false;
138 : 33 : Dwfl_Error error = __libdw_open_file (&fd, &mod->debug.elf, false, false);
139 [ - + ]: 33 : if (error != DWFL_E_NOERROR)
140 : 0 : __libdwfl_seterrno (error);
141 [ + - ]: 33 : else if (likely (__libdwfl_find_build_id (mod, false,
142 : : mod->debug.elf) == 2))
143 : : /* Also backdoor the gratuitous flag. */
144 : 33 : mod->debug.valid = true;
145 : : else
146 : : {
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 : : }
153 : :
154 : 33 : return mod->debug.valid;
155 : : }
156 : :
157 [ + - + - ]: 2 : return !check || check_crc (fd, debuglink_crc);
158 : : }
159 : :
160 : : static int
161 : 159 : 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 : : {
165 : 159 : bool cancheck = debuglink_crc != (GElf_Word) 0;
166 : :
167 [ + + ]: 159 : const char *file_basename = file_name == NULL ? NULL : basename (file_name);
168 : 159 : char *localname = NULL;
169 : :
170 : : /* We invent a debuglink .debug name if NULL, but then want to try the
171 : : basename too. */
172 : 159 : bool debuglink_null = debuglink_file == NULL;
173 [ + + ]: 159 : if (debuglink_null)
174 : : {
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 [ + + - + ]: 95 : if (file_basename == NULL || mod->dw != NULL)
178 : : {
179 : 21 : errno = 0;
180 : 21 : return -1;
181 : : }
182 : :
183 : 74 : size_t len = strlen (file_basename);
184 : 74 : localname = malloc (len + sizeof ".debug");
185 [ + - ]: 74 : if (unlikely (localname == NULL))
186 : : return -1;
187 : 74 : memcpy (localname, file_basename, len);
188 : 74 : memcpy (&localname[len], ".debug", sizeof ".debug");
189 : 74 : debuglink_file = localname;
190 : 74 : cancheck = false;
191 : : }
192 : :
193 : : /* Look for a file named DEBUGLINK_FILE in the directories
194 : : indicated by the debug directory path setting. */
195 : :
196 : 138 : const Dwfl_Callbacks *const cb = mod->dwfl->callbacks;
197 [ + + + + ]: 138 : char *localpath = strdup ((cb->debuginfo_path ? *cb->debuginfo_path : NULL)
198 : : ?: DEFAULT_DEBUGINFO_PATH);
199 [ - + ]: 138 : if (unlikely (localpath == NULL))
200 : : {
201 : 0 : free (localname);
202 : 0 : return -1;
203 : : }
204 : :
205 : : /* A leading - or + in the whole path sets whether to check file CRCs. */
206 : 138 : bool defcheck = true;
207 : 138 : char *path = localpath;
208 [ - + ]: 138 : if (path[0] == '-' || path[0] == '+')
209 : : {
210 : 0 : defcheck = path[0] == '+';
211 : 0 : ++path;
212 : : }
213 : :
214 : : /* XXX dev/ino should be cached in struct dwfl_file. */
215 : 138 : struct stat main_stat;
216 [ + + + - : 238 : if (unlikely ((mod->main.fd != -1 ? fstat (mod->main.fd, &main_stat)
- + - + ]
217 : : : file_name != NULL ? stat (file_name, &main_stat)
218 : : : -1) < 0))
219 : : {
220 : 0 : main_stat.st_dev = 0;
221 : 0 : main_stat.st_ino = 0;
222 : : }
223 : :
224 : 276 : char *file_dirname = (file_basename == file_name ? NULL
225 [ + + ]: 138 : : strndup (file_name, file_basename - 1 - file_name));
226 [ - + ]: 138 : if (file_basename != file_name && file_dirname == NULL)
227 : : {
228 : 0 : free (localpath);
229 : 0 : free (localname);
230 : 0 : return -1;
231 : : }
232 : : char *p;
233 [ + + ]: 411 : while ((p = strsep (&path, ":")) != NULL)
234 : : {
235 : : /* A leading - or + says whether to check file CRCs for this element. */
236 : 320 : bool check = defcheck;
237 [ - + ]: 320 : if (*p == '+' || *p == '-')
238 : 0 : check = *p++ == '+';
239 : 320 : 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 : 320 : bool try_file_basename;
244 : :
245 : 320 : const char *dir, *subdir, *file;
246 [ + + + ]: 320 : switch (p[0])
247 : : {
248 : : case '\0':
249 : : /* An empty entry says to try the main file's directory. */
250 : : dir = file_dirname;
251 : : subdir = NULL;
252 : : file = debuglink_file;
253 : : try_file_basename = false;
254 : : break;
255 : 92 : 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 [ + + ]: 92 : if (mod->dw == NULL
262 [ + + + + ]: 79 : && (file_dirname == NULL || file_dirname[0] != '/'))
263 : 273 : continue;
264 : 56 : dir = p;
265 [ + + ]: 56 : if (mod->dw == NULL)
266 : : {
267 : : subdir = file_dirname;
268 : : /* We want to explore all sub-subdirs. Chop off one slash
269 : : at a time. */
270 : 493 : explore_dir:
271 : 493 : subdir = strchr (subdir, '/');
272 [ + + ]: 493 : if (subdir != NULL)
273 : 373 : subdir = subdir + 1;
274 [ + + - + ]: 493 : if (subdir && *subdir == 0)
275 : 0 : continue;
276 : : file = debuglink_file;
277 : : }
278 : : else
279 : : {
280 : 13 : subdir = NULL;
281 : 13 : file = basename (debuglink_file);
282 : : }
283 : : try_file_basename = debuglink_null;
284 : : break;
285 : 91 : default:
286 : : /* A relative path says to try a subdirectory of that name
287 : : in the main file's directory. */
288 : 91 : dir = file_dirname;
289 : 91 : subdir = p;
290 : 91 : file = debuglink_file;
291 : 91 : try_file_basename = debuglink_null;
292 : 91 : break;
293 : : }
294 : :
295 : 734 : char *fname = NULL;
296 : 734 : int fd = try_open (&main_stat, dir, subdir, file, &fname);
297 [ + + ]: 734 : if (fd < 0 && try_file_basename)
298 : 524 : fd = try_open (&main_stat, dir, subdir, file_basename, &fname);
299 [ + + ]: 734 : if (fd < 0)
300 [ + - ]: 689 : switch (errno)
301 : : {
302 : 689 : case ENOENT:
303 : : 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 [ + + + + ]: 689 : if (mod->dw != NULL && (p[0] == '\0' || p[0] == '/'))
307 : : {
308 : 28 : fd = try_open (&main_stat, dir, ".dwz",
309 : 28 : basename (file), &fname);
310 [ + + ]: 28 : if (fd < 0)
311 : : {
312 [ - + ]: 26 : if (errno != ENOENT && errno != ENOTDIR)
313 : 0 : goto fail_free;
314 : : else
315 : 26 : continue;
316 : : }
317 : : break;
318 : : }
319 : : /* If possible try again with a sub-subdir. */
320 [ + + + + ]: 661 : if (mod->dw == NULL && subdir)
321 : 450 : goto explore_dir;
322 : 211 : continue;
323 : 0 : default:
324 : 0 : goto fail_free;
325 : : }
326 [ + - ]: 47 : if (validate (mod, fd, check, debuglink_crc))
327 : : {
328 : 47 : free (localpath);
329 : 47 : free (localname);
330 : 47 : free (file_dirname);
331 : 47 : *debuginfo_file_name = fname;
332 : 47 : return fd;
333 : : }
334 : 0 : free (fname);
335 : 0 : close (fd);
336 : : }
337 : :
338 : : /* No dice. */
339 : 91 : errno = 0;
340 : 91 : fail_free:
341 : 91 : free (localpath);
342 : 91 : free (localname);
343 : 91 : free (file_dirname);
344 : 91 : return -1;
345 : : }
346 : :
347 : : int
348 : 127 : 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 : : {
357 [ + - ]: 127 : if (mod == NULL)
358 : : 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 : 127 : const unsigned char *bits = NULL;
363 : 127 : GElf_Addr vaddr;
364 : 127 : int bits_len;
365 [ + + ]: 127 : if ((bits_len = INTUSE(dwfl_module_build_id) (mod, &bits, &vaddr)) > 0)
366 : : {
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 : 116 : int fd = INTUSE(dwfl_build_id_find_debuginfo) (mod,
372 : : NULL, NULL, 0,
373 : : NULL, NULL, 0,
374 : : 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 [ + + ]: 116 : if (fd >= 0
379 [ + + + - ]: 108 : || (mod->dw == NULL && mod->debug.elf != NULL)
380 [ + + + - ]: 108 : || (mod->dw != NULL && mod->alt_elf != NULL)
381 [ + - ]: 108 : || errno != 0)
382 : : return fd;
383 : : }
384 : :
385 : : /* Failing that, search the path by name. */
386 : 119 : int fd = find_debuginfo_in_path (mod, file_name,
387 : : debuglink_file, debuglink_crc,
388 : : debuginfo_file_name);
389 : :
390 [ + + + - : 119 : if (fd < 0 && errno == 0 && file_name != NULL)
+ + ]
391 : : {
392 : : /* If FILE_NAME is a symlink, the debug file might be associated
393 : : with the symlink target name instead. */
394 : :
395 [ - + ]: 55 : char *canon = realpath (file_name, NULL);
396 [ + - + + ]: 55 : if (canon != NULL && strcmp (file_name, canon))
397 : 40 : fd = find_debuginfo_in_path (mod, canon,
398 : : debuglink_file, debuglink_crc,
399 : : debuginfo_file_name);
400 : 55 : free (canon);
401 : : }
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 [ + + ]: 119 : if (fd < 0)
413 : : {
414 [ + + ]: 72 : if (mod->dw != NULL)
415 : : {
416 : 9 : const char *altname;
417 : 9 : bits_len = INTUSE(dwelf_dwarf_gnu_debugaltlink) (mod->dw, &altname,
418 : : (const void **)
419 : : &bits);
420 : : }
421 : :
422 [ + + ]: 72 : if (bits_len > 0)
423 : 62 : fd = __libdwfl_debuginfod_find_debuginfo (mod->dwfl, bits, bits_len);
424 : : }
425 : : #endif
426 : :
427 : : return fd;
428 : : }
429 : : INTDEF (dwfl_standard_find_debuginfo)
|