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
try_open(const struct stat * main_stat,const char * dir,const char * subdir,const char * debuglink,char ** debuginfo_file_name)43 try_open (const struct stat *main_stat,
44 const char *dir, const char *subdir, const char *debuglink,
45 char **debuginfo_file_name)
46 {
47 char *fname;
48 if (dir == NULL && subdir == NULL)
49 {
50 fname = strdup (debuglink);
51 if (unlikely (fname == NULL))
52 return -1;
53 }
54 else if ((subdir == NULL ? asprintf (&fname, "%s/%s", dir, debuglink)
55 : dir == NULL ? asprintf (&fname, "%s/%s", subdir, debuglink)
56 : asprintf (&fname, "%s/%s/%s", dir, subdir, debuglink)) < 0)
57 return -1;
58
59 struct stat st;
60 int fd = TEMP_FAILURE_RETRY (open (fname, O_RDONLY));
61 if (fd < 0)
62 free (fname);
63 else if (fstat (fd, &st) == 0
64 && st.st_ino == main_stat->st_ino
65 && st.st_dev == main_stat->st_dev)
66 {
67 /* This is the main file by another name. Don't look at it again. */
68 free (fname);
69 close (fd);
70 errno = ENOENT;
71 fd = -1;
72 }
73 else
74 *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
check_crc(int fd,GElf_Word debuglink_crc)81 check_crc (int fd, GElf_Word debuglink_crc)
82 {
83 uint32_t file_crc;
84 return (__libdwfl_crc32_file (fd, &file_crc) == 0
85 && file_crc == debuglink_crc);
86 }
87
88 static bool
validate(Dwfl_Module * mod,int fd,bool check,GElf_Word debuglink_crc)89 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 if (mod->dw != NULL)
93 {
94 bool valid = false;
95 const void *build_id;
96 const char *altname;
97 ssize_t build_id_len = INTUSE(dwelf_dwarf_gnu_debugaltlink) (mod->dw,
98 &altname,
99 &build_id);
100 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 Dwfl_Error error = __libdw_open_file (&fd, &mod->alt_elf,
106 false, false);
107 if (error != DWFL_E_NOERROR)
108 __libdwfl_seterrno (error);
109 else
110 {
111 const void *alt_build_id;
112 ssize_t alt_len = INTUSE(dwelf_elf_gnu_build_id) (mod->alt_elf,
113 &alt_build_id);
114 if (alt_len > 0 && alt_len == build_id_len
115 && memcmp (build_id, alt_build_id, alt_len) == 0)
116 valid = true;
117 else
118 {
119 /* A mismatch! */
120 elf_end (mod->alt_elf);
121 mod->alt_elf = NULL;
122 close (fd);
123 fd = -1;
124 }
125 }
126 }
127 return valid;
128 }
129
130 /* If we have a build ID, check only that. */
131 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 mod->debug.valid = false;
138 Dwfl_Error error = __libdw_open_file (&fd, &mod->debug.elf, false, false);
139 if (error != DWFL_E_NOERROR)
140 __libdwfl_seterrno (error);
141 else if (likely (__libdwfl_find_build_id (mod, false,
142 mod->debug.elf) == 2))
143 /* Also backdoor the gratuitous flag. */
144 mod->debug.valid = true;
145 else
146 {
147 /* A mismatch! */
148 elf_end (mod->debug.elf);
149 mod->debug.elf = NULL;
150 close (fd);
151 fd = -1;
152 }
153
154 return mod->debug.valid;
155 }
156
157 return !check || check_crc (fd, debuglink_crc);
158 }
159
160 static int
find_debuginfo_in_path(Dwfl_Module * mod,const char * file_name,const char * debuglink_file,GElf_Word debuglink_crc,char ** debuginfo_file_name)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 {
165 bool cancheck = debuglink_crc != (GElf_Word) 0;
166
167 const char *file_basename = file_name == NULL ? NULL : basename (file_name);
168 char *localname = NULL;
169
170 /* We invent a debuglink .debug name if NULL, but then want to try the
171 basename too. */
172 bool debuglink_null = debuglink_file == NULL;
173 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 if (file_basename == NULL || mod->dw != NULL)
178 {
179 errno = 0;
180 return -1;
181 }
182
183 size_t len = strlen (file_basename);
184 localname = malloc (len + sizeof ".debug");
185 if (unlikely (localname == NULL))
186 return -1;
187 memcpy (localname, file_basename, len);
188 memcpy (&localname[len], ".debug", sizeof ".debug");
189 debuglink_file = localname;
190 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 const Dwfl_Callbacks *const cb = mod->dwfl->callbacks;
197 char *localpath = strdup ((cb->debuginfo_path ? *cb->debuginfo_path : NULL)
198 ?: DEFAULT_DEBUGINFO_PATH);
199 if (unlikely (localpath == NULL))
200 {
201 free (localname);
202 return -1;
203 }
204
205 /* A leading - or + in the whole path sets whether to check file CRCs. */
206 bool defcheck = true;
207 char *path = localpath;
208 if (path[0] == '-' || path[0] == '+')
209 {
210 defcheck = path[0] == '+';
211 ++path;
212 }
213
214 /* XXX dev/ino should be cached in struct dwfl_file. */
215 struct stat main_stat;
216 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 main_stat.st_dev = 0;
221 main_stat.st_ino = 0;
222 }
223
224 char *file_dirname = (file_basename == file_name ? NULL
225 : strndup (file_name, file_basename - 1 - file_name));
226 if (file_basename != file_name && file_dirname == NULL)
227 {
228 free (localpath);
229 free (localname);
230 return -1;
231 }
232 char *p;
233 while ((p = strsep (&path, ":")) != NULL)
234 {
235 /* A leading - or + says whether to check file CRCs for this element. */
236 bool check = defcheck;
237 if (*p == '+' || *p == '-')
238 check = *p++ == '+';
239 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 bool try_file_basename;
244
245 const char *dir, *subdir, *file;
246 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 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 if (mod->dw == NULL
262 && (file_dirname == NULL || file_dirname[0] != '/'))
263 continue;
264 dir = p;
265 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 explore_dir:
271 subdir = strchr (subdir, '/');
272 if (subdir != NULL)
273 subdir = subdir + 1;
274 if (subdir && *subdir == 0)
275 continue;
276 file = debuglink_file;
277 }
278 else
279 {
280 subdir = NULL;
281 file = basename (debuglink_file);
282 }
283 try_file_basename = debuglink_null;
284 break;
285 default:
286 /* A relative path says to try a subdirectory of that name
287 in the main file's directory. */
288 dir = file_dirname;
289 subdir = p;
290 file = debuglink_file;
291 try_file_basename = debuglink_null;
292 break;
293 }
294
295 char *fname = NULL;
296 int fd = try_open (&main_stat, dir, subdir, file, &fname);
297 if (fd < 0 && try_file_basename)
298 fd = try_open (&main_stat, dir, subdir, file_basename, &fname);
299 if (fd < 0)
300 switch (errno)
301 {
302 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 if (mod->dw != NULL && (p[0] == '\0' || p[0] == '/'))
307 {
308 fd = try_open (&main_stat, dir, ".dwz",
309 basename (file), &fname);
310 if (fd < 0)
311 {
312 if (errno != ENOENT && errno != ENOTDIR)
313 goto fail_free;
314 else
315 continue;
316 }
317 break;
318 }
319 /* If possible try again with a sub-subdir. */
320 if (mod->dw == NULL && subdir)
321 goto explore_dir;
322 continue;
323 default:
324 goto fail_free;
325 }
326 if (validate (mod, fd, check, debuglink_crc))
327 {
328 free (localpath);
329 free (localname);
330 free (file_dirname);
331 *debuginfo_file_name = fname;
332 return fd;
333 }
334 free (fname);
335 close (fd);
336 }
337
338 /* No dice. */
339 errno = 0;
340 fail_free:
341 free (localpath);
342 free (localname);
343 free (file_dirname);
344 return -1;
345 }
346
347 int
dwfl_standard_find_debuginfo(Dwfl_Module * mod,void ** userdata,const char * modname,GElf_Addr base,const char * file_name,const char * debuglink_file,GElf_Word debuglink_crc,char ** debuginfo_file_name)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 {
357 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 const unsigned char *bits = NULL;
363 GElf_Addr vaddr;
364 int bits_len;
365 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 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 if (fd >= 0
379 || (mod->dw == NULL && mod->debug.elf != NULL)
380 || (mod->dw != NULL && mod->alt_elf != NULL)
381 || errno != 0)
382 return fd;
383 }
384
385 /* Failing that, search the path by name. */
386 int fd = find_debuginfo_in_path (mod, file_name,
387 debuglink_file, debuglink_crc,
388 debuginfo_file_name);
389
390 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 char *canon = realpath (file_name, NULL);
396 if (canon != NULL && strcmp (file_name, canon))
397 fd = find_debuginfo_in_path (mod, canon,
398 debuglink_file, debuglink_crc,
399 debuginfo_file_name);
400 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 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 return fd;
428 }
429 INTDEF (dwfl_standard_find_debuginfo)
430