1 /* Standard find_debuginfo callback for libdwfl.
2 Copyright (C) 2005-2010, 2014, 2015 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
try_open(const struct stat * main_stat,const char * dir,const char * subdir,const char * debuglink,char ** debuginfo_file_name)44 try_open (const struct stat *main_stat,
45 const char *dir, const char *subdir, const char *debuglink,
46 char **debuginfo_file_name)
47 {
48 char *fname;
49 if (dir == NULL && subdir == NULL)
50 {
51 fname = strdup (debuglink);
52 if (unlikely (fname == NULL))
53 return -1;
54 }
55 else if ((subdir == NULL ? asprintf (&fname, "%s/%s", dir, debuglink)
56 : dir == NULL ? asprintf (&fname, "%s/%s", subdir, debuglink)
57 : asprintf (&fname, "%s/%s/%s", dir, subdir, debuglink)) < 0)
58 return -1;
59
60 struct stat st;
61 int fd = TEMP_FAILURE_RETRY (open (fname, O_RDONLY));
62 if (fd < 0)
63 free (fname);
64 else if (fstat (fd, &st) == 0
65 && st.st_ino == main_stat->st_ino
66 && st.st_dev == main_stat->st_dev)
67 {
68 /* This is the main file by another name. Don't look at it again. */
69 free (fname);
70 close (fd);
71 errno = ENOENT;
72 fd = -1;
73 }
74 else
75 *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
check_crc(int fd,GElf_Word debuglink_crc)82 check_crc (int fd, GElf_Word debuglink_crc)
83 {
84 uint32_t file_crc;
85 return (__libdwfl_crc32_file (fd, &file_crc) == 0
86 && file_crc == debuglink_crc);
87 }
88
89 static bool
validate(Dwfl_Module * mod,int fd,bool check,GElf_Word debuglink_crc)90 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 if (mod->dw != NULL)
94 {
95 bool valid = false;
96 const void *build_id;
97 const char *altname;
98 ssize_t build_id_len = INTUSE(dwelf_dwarf_gnu_debugaltlink) (mod->dw,
99 &altname,
100 &build_id);
101 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 Dwfl_Error error = __libdw_open_file (&fd, &mod->alt_elf,
107 false, false);
108 if (error != DWFL_E_NOERROR)
109 __libdwfl_seterrno (error);
110 else
111 {
112 const void *alt_build_id;
113 ssize_t alt_len = INTUSE(dwelf_elf_gnu_build_id) (mod->alt_elf,
114 &alt_build_id);
115 if (alt_len > 0 && alt_len == build_id_len
116 && memcmp (build_id, alt_build_id, alt_len) == 0)
117 valid = true;
118 else
119 {
120 /* A mismatch! */
121 elf_end (mod->alt_elf);
122 mod->alt_elf = NULL;
123 close (fd);
124 fd = -1;
125 }
126 }
127 }
128 return valid;
129 }
130
131 /* If we have a build ID, check only that. */
132 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 mod->debug.valid = false;
139 Dwfl_Error error = __libdw_open_file (&fd, &mod->debug.elf, false, false);
140 if (error != DWFL_E_NOERROR)
141 __libdwfl_seterrno (error);
142 else if (likely (__libdwfl_find_build_id (mod, false,
143 mod->debug.elf) == 2))
144 /* Also backdoor the gratuitous flag. */
145 mod->debug.valid = true;
146 else
147 {
148 /* A mismatch! */
149 elf_end (mod->debug.elf);
150 mod->debug.elf = NULL;
151 close (fd);
152 fd = -1;
153 }
154
155 return mod->debug.valid;
156 }
157
158 return !check || check_crc (fd, debuglink_crc);
159 }
160
161 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)162 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 bool cancheck = debuglink_crc != (GElf_Word) 0;
167
168 const char *file_basename = file_name == NULL ? NULL : basename (file_name);
169 char *localname = NULL;
170
171 /* We invent a debuglink .debug name if NULL, but then want to try the
172 basename too. */
173 bool debuglink_null = debuglink_file == NULL;
174 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 if (file_basename == NULL || mod->dw != NULL)
179 {
180 errno = 0;
181 return -1;
182 }
183
184 size_t len = strlen (file_basename);
185 localname = malloc (len + sizeof ".debug");
186 if (unlikely (localname == NULL))
187 return -1;
188 memcpy (localname, file_basename, len);
189 memcpy (&localname[len], ".debug", sizeof ".debug");
190 debuglink_file = localname;
191 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 const Dwfl_Callbacks *const cb = mod->dwfl->callbacks;
198 char *localpath = strdup ((cb->debuginfo_path ? *cb->debuginfo_path : NULL)
199 ?: DEFAULT_DEBUGINFO_PATH);
200 if (unlikely (localpath == NULL))
201 {
202 free (localname);
203 return -1;
204 }
205
206 /* A leading - or + in the whole path sets whether to check file CRCs. */
207 bool defcheck = true;
208 char *path = localpath;
209 if (path[0] == '-' || path[0] == '+')
210 {
211 defcheck = path[0] == '+';
212 ++path;
213 }
214
215 /* XXX dev/ino should be cached in struct dwfl_file. */
216 struct stat main_stat;
217 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 main_stat.st_dev = 0;
222 main_stat.st_ino = 0;
223 }
224
225 char *file_dirname = (file_basename == file_name ? NULL
226 : strndup (file_name, file_basename - 1 - file_name));
227 if (file_basename != file_name && file_dirname == NULL)
228 {
229 free (localpath);
230 free (localname);
231 return -1;
232 }
233 char *p;
234 while ((p = strsep (&path, ":")) != NULL)
235 {
236 /* A leading - or + says whether to check file CRCs for this element. */
237 bool check = defcheck;
238 if (*p == '+' || *p == '-')
239 check = *p++ == '+';
240 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 bool try_file_basename;
245
246 const char *dir, *subdir, *file;
247 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 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 if (mod->dw == NULL
263 && (file_dirname == NULL || file_dirname[0] != '/'))
264 continue;
265 dir = p;
266 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 explore_dir:
272 subdir = strchr (subdir, '/');
273 if (subdir != NULL)
274 subdir = subdir + 1;
275 if (subdir && *subdir == 0)
276 continue;
277 file = debuglink_file;
278 }
279 else
280 {
281 subdir = NULL;
282 file = basename (debuglink_file);
283 }
284 try_file_basename = debuglink_null;
285 break;
286 default:
287 /* A relative path says to try a subdirectory of that name
288 in the main file's directory. */
289 dir = file_dirname;
290 subdir = p;
291 file = debuglink_file;
292 try_file_basename = debuglink_null;
293 break;
294 }
295
296 char *fname = NULL;
297 int fd = try_open (&main_stat, dir, subdir, file, &fname);
298 if (fd < 0 && try_file_basename)
299 fd = try_open (&main_stat, dir, subdir, file_basename, &fname);
300 if (fd < 0)
301 switch (errno)
302 {
303 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 if (mod->dw != NULL && (p[0] == '\0' || p[0] == '/'))
308 {
309 fd = try_open (&main_stat, dir, ".dwz",
310 basename (file), &fname);
311 if (fd < 0)
312 {
313 if (errno != ENOENT && errno != ENOTDIR)
314 goto fail_free;
315 else
316 continue;
317 }
318 break;
319 }
320 /* If possible try again with a sub-subdir. */
321 if (mod->dw == NULL && subdir)
322 goto explore_dir;
323 continue;
324 default:
325 goto fail_free;
326 }
327 if (validate (mod, fd, check, debuglink_crc))
328 {
329 free (localpath);
330 free (localname);
331 free (file_dirname);
332 *debuginfo_file_name = fname;
333 return fd;
334 }
335 free (fname);
336 close (fd);
337 }
338
339 /* No dice. */
340 errno = 0;
341 fail_free:
342 free (localpath);
343 free (localname);
344 free (file_dirname);
345 return -1;
346 }
347
348 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)349 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 /* First try by build ID if we have one. If that succeeds or fails
359 other than just by finding nothing, that's all we do. */
360 const unsigned char *bits;
361 GElf_Addr vaddr;
362 if (INTUSE(dwfl_module_build_id) (mod, &bits, &vaddr) > 0)
363 {
364 /* Dropping most arguments means we cannot rely on them in
365 dwfl_build_id_find_debuginfo. But leave it that way since
366 some user code out there also does this, so we'll have to
367 handle it anyway. */
368 int fd = INTUSE(dwfl_build_id_find_debuginfo) (mod,
369 NULL, NULL, 0,
370 NULL, NULL, 0,
371 debuginfo_file_name);
372
373 /* Did the build_id callback find something or report an error?
374 Then we are done. Otherwise fallback on path based search. */
375 if (fd >= 0
376 || (mod->dw == NULL && mod->debug.elf != NULL)
377 || (mod->dw != NULL && mod->alt_elf != NULL)
378 || errno != 0)
379 return fd;
380 }
381
382 /* Failing that, search the path by name. */
383 int fd = find_debuginfo_in_path (mod, file_name,
384 debuglink_file, debuglink_crc,
385 debuginfo_file_name);
386
387 if (fd < 0 && errno == 0 && file_name != NULL)
388 {
389 /* If FILE_NAME is a symlink, the debug file might be associated
390 with the symlink target name instead. */
391
392 char *canon = realpath (file_name, NULL);
393 if (canon != NULL && strcmp (file_name, canon))
394 fd = find_debuginfo_in_path (mod, canon,
395 debuglink_file, debuglink_crc,
396 debuginfo_file_name);
397 free (canon);
398 }
399
400 return fd;
401 }
402 INTDEF (dwfl_standard_find_debuginfo)
403