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