21db91a406a3c384ea113b72abe62fd897c843ca
[platform/upstream/elfutils.git] / libdwfl / find-debuginfo.c
1 /* Standard find_debuginfo callback for libdwfl.
2    Copyright (C) 2005-2010 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   /* If we have a build ID, check only that.  */
88   if (mod->build_id_len > 0)
89     {
90       /* We need to open an Elf handle on the file so we can check its
91          build ID note for validation.  Backdoor the handle into the
92          module data structure since we had to open it early anyway.  */
93
94       mod->debug.valid = false;
95       Dwfl_Error error = __libdw_open_file (&fd, &mod->debug.elf, false, false);
96       if (error != DWFL_E_NOERROR)
97         __libdwfl_seterrno (error);
98       else if (likely (__libdwfl_find_build_id (mod, false,
99                                                 mod->debug.elf) == 2))
100         /* Also backdoor the gratuitous flag.  */
101         mod->debug.valid = true;
102       else
103         {
104           /* A mismatch!  */
105           elf_end (mod->debug.elf);
106           mod->debug.elf = NULL;
107           close (fd);
108           fd = -1;
109         }
110
111       return mod->debug.valid;
112     }
113
114   return !check || check_crc (fd, debuglink_crc);
115 }
116
117 static int
118 find_debuginfo_in_path (Dwfl_Module *mod, const char *file_name,
119                         const char *debuglink_file, GElf_Word debuglink_crc,
120                         char **debuginfo_file_name)
121 {
122   bool cancheck = debuglink_crc != (GElf_Word) 0;
123
124   const char *file_basename = file_name == NULL ? NULL : basename (file_name);
125   if (debuglink_file == NULL)
126     {
127       if (file_basename == NULL)
128         {
129           errno = 0;
130           return -1;
131         }
132
133       size_t len = strlen (file_basename);
134       char *localname = alloca (len + sizeof ".debug");
135       memcpy (localname, file_basename, len);
136       memcpy (&localname[len], ".debug", sizeof ".debug");
137       debuglink_file = localname;
138       cancheck = false;
139     }
140
141   /* Look for a file named DEBUGLINK_FILE in the directories
142      indicated by the debug directory path setting.  */
143
144   const Dwfl_Callbacks *const cb = mod->dwfl->callbacks;
145   char *path = strdupa ((cb->debuginfo_path ? *cb->debuginfo_path : NULL)
146                         ?: DEFAULT_DEBUGINFO_PATH);
147
148   /* A leading - or + in the whole path sets whether to check file CRCs.  */
149   bool defcheck = true;
150   if (path[0] == '-' || path[0] == '+')
151     {
152       defcheck = path[0] == '+';
153       ++path;
154     }
155
156   /* XXX dev/ino should be cached in struct dwfl_file.  */
157   struct stat64 main_stat;
158   if (unlikely ((mod->main.fd != -1 ? fstat64 (mod->main.fd, &main_stat)
159                  : file_name != NULL ? stat64 (file_name, &main_stat)
160                  : -1) < 0))
161     {
162       main_stat.st_dev = 0;
163       main_stat.st_ino = 0;
164     }
165
166   char *file_dirname = (file_basename == file_name ? NULL
167                         : strndupa (file_name, file_basename - 1 - file_name));
168   char *p;
169   while ((p = strsep (&path, ":")) != NULL)
170     {
171       /* A leading - or + says whether to check file CRCs for this element.  */
172       bool check = defcheck;
173       if (*p == '+' || *p == '-')
174         check = *p++ == '+';
175       check = check && cancheck;
176
177       const char *dir, *subdir;
178       switch (p[0])
179         {
180         case '\0':
181           /* An empty entry says to try the main file's directory.  */
182           dir = file_dirname;
183           subdir = NULL;
184           break;
185         case '/':
186           /* An absolute path says to look there for a subdirectory
187              named by the main file's absolute directory.
188              This cannot be applied to a relative file name.  */
189           if (file_dirname == NULL || file_dirname[0] != '/')
190             continue;
191           dir = p;
192           subdir = file_dirname + 1;
193           break;
194         default:
195           /* A relative path says to try a subdirectory of that name
196              in the main file's directory.  */
197           dir = file_dirname;
198           subdir = p;
199           break;
200         }
201
202       char *fname = NULL;
203       int fd = try_open (&main_stat, dir, subdir, debuglink_file, &fname);
204       if (fd < 0)
205         switch (errno)
206           {
207           case ENOENT:
208           case ENOTDIR:
209             continue;
210           default:
211             return -1;
212           }
213       if (validate (mod, fd, check, debuglink_crc))
214         {
215           *debuginfo_file_name = fname;
216           return fd;
217         }
218       free (fname);
219       close (fd);
220     }
221
222   /* No dice.  */
223   errno = 0;
224   return -1;
225 }
226
227 int
228 dwfl_standard_find_debuginfo (Dwfl_Module *mod,
229                               void **userdata __attribute__ ((unused)),
230                               const char *modname __attribute__ ((unused)),
231                               GElf_Addr base __attribute__ ((unused)),
232                               const char *file_name,
233                               const char *debuglink_file,
234                               GElf_Word debuglink_crc,
235                               char **debuginfo_file_name)
236 {
237   /* First try by build ID if we have one.  If that succeeds or fails
238      other than just by finding nothing, that's all we do.  */
239   const unsigned char *bits;
240   GElf_Addr vaddr;
241   if (INTUSE(dwfl_module_build_id) (mod, &bits, &vaddr) > 0)
242     {
243       int fd = INTUSE(dwfl_build_id_find_debuginfo) (mod,
244                                                      NULL, NULL, 0,
245                                                      NULL, NULL, 0,
246                                                      debuginfo_file_name);
247       if (fd >= 0 || mod->debug.elf != NULL || errno != 0)
248         return fd;
249     }
250
251   /* Failing that, search the path by name.  */
252   int fd = find_debuginfo_in_path (mod, file_name,
253                                    debuglink_file, debuglink_crc,
254                                    debuginfo_file_name);
255
256   if (fd < 0 && errno == 0)
257     {
258       /* If FILE_NAME is a symlink, the debug file might be associated
259          with the symlink target name instead.  */
260
261       char *canon = canonicalize_file_name (file_name);
262       if (canon != NULL && strcmp (file_name, canon))
263         fd = find_debuginfo_in_path (mod, canon,
264                                      debuglink_file, debuglink_crc,
265                                      debuginfo_file_name);
266       free (canon);
267     }
268
269   return fd;
270 }
271 INTDEF (dwfl_standard_find_debuginfo)