f6f802e25d1aaa49e155c9be80f984ede7ec771a
[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 Red Hat elfutils.
4
5    Red Hat elfutils is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by the
7    Free Software Foundation; version 2 of the License.
8
9    Red Hat elfutils is distributed in the hope that it will be useful, but
10    WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    General Public License for more details.
13
14    You should have received a copy of the GNU General Public License along
15    with Red Hat elfutils; if not, write to the Free Software Foundation,
16    Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
17
18    In addition, as a special exception, Red Hat, Inc. gives You the
19    additional right to link the code of Red Hat elfutils with code licensed
20    under any Open Source Initiative certified open source license
21    (http://www.opensource.org/licenses/index.php) which requires the
22    distribution of source code with any binary distribution and to
23    distribute linked combinations of the two.  Non-GPL Code permitted under
24    this exception must only link to the code of Red Hat elfutils through
25    those well defined interfaces identified in the file named EXCEPTION
26    found in the source code files (the "Approved Interfaces").  The files
27    of Non-GPL Code may instantiate templates or use macros or inline
28    functions from the Approved Interfaces without causing the resulting
29    work to be covered by the GNU General Public License.  Only Red Hat,
30    Inc. may make changes or additions to the list of Approved Interfaces.
31    Red Hat's grant of this exception is conditioned upon your not adding
32    any new exceptions.  If you wish to add a new Approved Interface or
33    exception, please contact Red Hat.  You must obey the GNU General Public
34    License in all respects for all of the Red Hat elfutils code and other
35    code used in conjunction with Red Hat elfutils except the Non-GPL Code
36    covered by this exception.  If you modify this file, you may extend this
37    exception to your version of the file, but you are not obligated to do
38    so.  If you do not wish to provide this exception without modification,
39    you must delete this exception statement from your version and license
40    this file solely under the GPL without exception.
41
42    Red Hat elfutils is an included package of the Open Invention Network.
43    An included package of the Open Invention Network is a package for which
44    Open Invention Network licensees cross-license their patents.  No patent
45    license is granted, either expressly or impliedly, by designation as an
46    included package.  Should you wish to participate in the Open Invention
47    Network licensing program, please visit www.openinventionnetwork.com
48    <http://www.openinventionnetwork.com>.  */
49
50 #include "libdwflP.h"
51 #include <stdio.h>
52 #include <fcntl.h>
53 #include <unistd.h>
54 #include <sys/stat.h>
55 #include "system.h"
56
57
58 /* Try to open64 [DIR/][SUBDIR/]DEBUGLINK, return file descriptor or -1.
59    On success, *DEBUGINFO_FILE_NAME has the malloc'd name of the open file.  */
60 static int
61 try_open (const struct stat64 *main_stat,
62           const char *dir, const char *subdir, const char *debuglink,
63           char **debuginfo_file_name)
64 {
65   char *fname;
66   if (dir == NULL && subdir == NULL)
67     {
68       fname = strdup (debuglink);
69       if (fname == NULL)
70         return -1;
71     }
72   else if ((subdir == NULL ? asprintf (&fname, "%s/%s", dir, debuglink)
73             : dir == NULL ? asprintf (&fname, "%s/%s", subdir, debuglink)
74             : asprintf (&fname, "%s/%s/%s", dir, subdir, debuglink)) < 0)
75     return -1;
76
77   struct stat64 st;
78   int fd = TEMP_FAILURE_RETRY (open64 (fname, O_RDONLY));
79   if (fd < 0)
80     free (fname);
81   else if (fstat64 (fd, &st) == 0
82            && st.st_ino == main_stat->st_ino
83            && st.st_dev == main_stat->st_dev)
84     {
85       /* This is the main file by another name.  Don't look at it again.  */
86       close (fd);
87       errno = ENOENT;
88       fd = -1;
89     }
90   else
91     *debuginfo_file_name = fname;
92
93   return fd;
94 }
95
96 /* Return true iff the FD's contents CRC matches DEBUGLINK_CRC.  */
97 static inline bool
98 check_crc (int fd, GElf_Word debuglink_crc)
99 {
100   uint32_t file_crc;
101   return (__libdwfl_crc32_file (fd, &file_crc) == 0
102           && file_crc == debuglink_crc);
103 }
104
105 static bool
106 validate (Dwfl_Module *mod, int fd, bool check, GElf_Word debuglink_crc)
107 {
108   /* If we have a build ID, check only that.  */
109   if (mod->build_id_len > 0)
110     {
111       /* We need to open an Elf handle on the file so we can check its
112          build ID note for validation.  Backdoor the handle into the
113          module data structure since we had to open it early anyway.  */
114
115       mod->debug.valid = false;
116       Dwfl_Error error = __libdw_open_file (&fd, &mod->debug.elf, false, false);
117       if (error != DWFL_E_NOERROR)
118         __libdwfl_seterrno (error);
119       else if (likely (__libdwfl_find_build_id (mod, false,
120                                                 mod->debug.elf) == 2))
121         /* Also backdoor the gratuitous flag.  */
122         mod->debug.valid = true;
123       else
124         {
125           /* A mismatch!  */
126           elf_end (mod->debug.elf);
127           mod->debug.elf = NULL;
128           close (fd);
129           fd = -1;
130         }
131
132       return mod->debug.valid;
133     }
134
135   return !check || check_crc (fd, debuglink_crc);
136 }
137
138 static int
139 find_debuginfo_in_path (Dwfl_Module *mod, const char *file_name,
140                         const char *debuglink_file, GElf_Word debuglink_crc,
141                         char **debuginfo_file_name)
142 {
143   bool cancheck = debuglink_crc != (GElf_Word) 0;
144
145   const char *file_basename = file_name == NULL ? NULL : basename (file_name);
146   if (debuglink_file == NULL)
147     {
148       if (file_basename == NULL)
149         {
150           errno = 0;
151           return -1;
152         }
153
154       size_t len = strlen (file_basename);
155       char *localname = alloca (len + sizeof ".debug");
156       memcpy (localname, file_basename, len);
157       memcpy (&localname[len], ".debug", sizeof ".debug");
158       debuglink_file = localname;
159       cancheck = false;
160     }
161
162   /* Look for a file named DEBUGLINK_FILE in the directories
163      indicated by the debug directory path setting.  */
164
165   const Dwfl_Callbacks *const cb = mod->dwfl->callbacks;
166   char *path = strdupa ((cb->debuginfo_path ? *cb->debuginfo_path : NULL)
167                         ?: DEFAULT_DEBUGINFO_PATH);
168
169   /* A leading - or + in the whole path sets whether to check file CRCs.  */
170   bool defcheck = true;
171   if (path[0] == '-' || path[0] == '+')
172     {
173       defcheck = path[0] == '+';
174       ++path;
175     }
176
177   /* XXX dev/ino should be cached in struct dwfl_file.  */
178   struct stat64 main_stat;
179   if (unlikely ((mod->main.fd != -1 ? fstat64 (mod->main.fd, &main_stat)
180                  : file_name != NULL ? stat64 (file_name, &main_stat)
181                  : -1) < 0))
182     {
183       main_stat.st_dev = 0;
184       main_stat.st_ino = 0;
185     }
186
187   char *file_dirname = (file_basename == file_name ? NULL
188                         : strndupa (file_name, file_basename - 1 - file_name));
189   char *p;
190   while ((p = strsep (&path, ":")) != NULL)
191     {
192       /* A leading - or + says whether to check file CRCs for this element.  */
193       bool check = defcheck;
194       if (*p == '+' || *p == '-')
195         check = *p++ == '+';
196       check = check && cancheck;
197
198       const char *dir, *subdir;
199       switch (p[0])
200         {
201         case '\0':
202           /* An empty entry says to try the main file's directory.  */
203           dir = file_dirname;
204           subdir = NULL;
205           break;
206         case '/':
207           /* An absolute path says to look there for a subdirectory
208              named by the main file's absolute directory.
209              This cannot be applied to a relative file name.  */
210           if (file_dirname == NULL || file_dirname[0] != '/')
211             continue;
212           dir = p;
213           subdir = file_dirname + 1;
214           break;
215         default:
216           /* A relative path says to try a subdirectory of that name
217              in the main file's directory.  */
218           dir = file_dirname;
219           subdir = p;
220           break;
221         }
222
223       char *fname = NULL;
224       int fd = try_open (&main_stat, dir, subdir, debuglink_file, &fname);
225       if (fd < 0)
226         switch (errno)
227           {
228           case ENOENT:
229           case ENOTDIR:
230             continue;
231           default:
232             return -1;
233           }
234       if (validate (mod, fd, check, debuglink_crc))
235         {
236           *debuginfo_file_name = fname;
237           return fd;
238         }
239       free (fname);
240       close (fd);
241     }
242
243   /* No dice.  */
244   errno = 0;
245   return -1;
246 }
247
248 int
249 dwfl_standard_find_debuginfo (Dwfl_Module *mod,
250                               void **userdata __attribute__ ((unused)),
251                               const char *modname __attribute__ ((unused)),
252                               GElf_Addr base __attribute__ ((unused)),
253                               const char *file_name,
254                               const char *debuglink_file,
255                               GElf_Word debuglink_crc,
256                               char **debuginfo_file_name)
257 {
258   /* First try by build ID if we have one.  If that succeeds or fails
259      other than just by finding nothing, that's all we do.  */
260   const unsigned char *bits;
261   GElf_Addr vaddr;
262   if (INTUSE(dwfl_module_build_id) (mod, &bits, &vaddr) > 0)
263     {
264       int fd = INTUSE(dwfl_build_id_find_debuginfo) (mod,
265                                                      NULL, NULL, 0,
266                                                      NULL, NULL, 0,
267                                                      debuginfo_file_name);
268       if (fd >= 0 || mod->debug.elf != NULL || errno != 0)
269         return fd;
270     }
271
272   /* Failing that, search the path by name.  */
273   int fd = find_debuginfo_in_path (mod, file_name,
274                                    debuglink_file, debuglink_crc,
275                                    debuginfo_file_name);
276
277   if (fd < 0 && errno == 0)
278     {
279       /* If FILE_NAME is a symlink, the debug file might be associated
280          with the symlink target name instead.  */
281
282       char *canon = canonicalize_file_name (file_name);
283       if (canon != NULL && strcmp (file_name, canon))
284         fd = find_debuginfo_in_path (mod, canon,
285                                      debuglink_file, debuglink_crc,
286                                      debuginfo_file_name);
287       free (canon);
288     }
289
290   return fd;
291 }
292 INTDEF (dwfl_standard_find_debuginfo)