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