• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Standard find_debuginfo callback for libdwfl.
2    Copyright (C) 2005, 2006, 2007, 2008 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 "system.h"
55 
56 /* Try to open64 [DIR/][SUBDIR/]DEBUGLINK, return file descriptor or -1.
57    On success, *DEBUGINFO_FILE_NAME has the malloc'd name of the open file.  */
58 static int
try_open(const char * dir,const char * subdir,const char * debuglink,char ** debuginfo_file_name)59 try_open (const char *dir, const char *subdir, const char *debuglink,
60 	  char **debuginfo_file_name)
61 {
62   char *fname;
63   if (dir == NULL && subdir == NULL)
64     {
65       fname = strdup (debuglink);
66       if (fname == NULL)
67 	return -1;
68     }
69   else if ((subdir == NULL ? asprintf (&fname, "%s/%s", dir, debuglink)
70 	    : dir == NULL ? asprintf (&fname, "%s/%s", subdir, debuglink)
71 	    : asprintf (&fname, "%s/%s/%s", dir, subdir, debuglink)) < 0)
72     return -1;
73 
74   int fd = TEMP_FAILURE_RETRY (open64 (fname, O_RDONLY));
75   if (fd < 0)
76     free (fname);
77   else
78     *debuginfo_file_name = fname;
79 
80   return fd;
81 }
82 
83 /* Return true iff the FD's contents CRC matches DEBUGLINK_CRC.  */
84 static inline bool
check_crc(int fd,GElf_Word debuglink_crc)85 check_crc (int fd, GElf_Word debuglink_crc)
86 {
87   uint32_t file_crc;
88   return (__libdwfl_crc32_file (fd, &file_crc) == 0
89 	  && file_crc == debuglink_crc);
90 }
91 
92 static bool
validate(Dwfl_Module * mod,int fd,bool check,GElf_Word debuglink_crc)93 validate (Dwfl_Module *mod, int fd, bool check, GElf_Word debuglink_crc)
94 {
95   /* If we have a build ID, check only that.  */
96   if (mod->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       mod->debug.elf = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, NULL);
102       if (likely (__libdwfl_find_build_id (mod, false, mod->debug.elf) == 2))
103 	/* Also backdoor the gratuitous flag.  */
104 	mod->debug.valid = true;
105       else
106 	{
107 	  /* A mismatch!  */
108 	  elf_end (mod->debug.elf);
109 	  mod->debug.elf = NULL;
110 	  mod->debug.valid = false;
111 	}
112 
113       return mod->debug.valid;
114     }
115 
116   return !check || check_crc (fd, debuglink_crc);
117 }
118 
119 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)120 find_debuginfo_in_path (Dwfl_Module *mod, const char *file_name,
121 			const char *debuglink_file, GElf_Word debuglink_crc,
122 			char **debuginfo_file_name)
123 {
124   bool cancheck = debuglink_crc != (GElf_Word) 0;
125 
126   const char *file_basename = file_name == NULL ? NULL : basename (file_name);
127   if (debuglink_file == NULL)
128     {
129       if (file_basename == NULL)
130 	{
131 	  errno = 0;
132 	  return -1;
133 	}
134 
135       size_t len = strlen (file_basename);
136       char *localname = alloca (len + sizeof ".debug");
137       memcpy (localname, file_basename, len);
138       memcpy (&localname[len], ".debug", sizeof ".debug");
139       debuglink_file = localname;
140       cancheck = false;
141     }
142 
143   /* Look for a file named DEBUGLINK_FILE in the directories
144      indicated by the debug directory path setting.  */
145 
146   const Dwfl_Callbacks *const cb = mod->dwfl->callbacks;
147 /* ANDROID_CHANGE_BEGIN */
148 #if defined(__BIONIC__) || defined(__APPLE__)
149   char *path = strdup ((cb->debuginfo_path ? *cb->debuginfo_path : NULL)
150 			?: DEFAULT_DEBUGINFO_PATH);
151 #else
152   char *path = strdupa ((cb->debuginfo_path ? *cb->debuginfo_path : NULL)
153 			?: DEFAULT_DEBUGINFO_PATH);
154 #endif
155 /* ANDROID_CHANGE_END */
156 
157   /* A leading - or + in the whole path sets whether to check file CRCs.  */
158   bool defcheck = true;
159   if (path[0] == '-' || path[0] == '+')
160     {
161       defcheck = path[0] == '+';
162       ++path;
163     }
164 
165   /* ANDROID_CHANGE_BEGIN */
166 #if defined(__BIONIC__) || defined(__APPLE__)
167   char *file_dirname = (file_basename == file_name ? NULL
168 			: strndup (file_name, file_basename - 1 - file_name));
169 #else
170   char *file_dirname = (file_basename == file_name ? NULL
171 			: strndupa (file_name, file_basename - 1 - file_name));
172 #endif
173   /* ANDROID_CHANGE_END */
174   char *p;
175   while ((p = strsep (&path, ":")) != NULL)
176     {
177       /* A leading - or + says whether to check file CRCs for this element.  */
178       bool check = defcheck;
179       if (*p == '+' || *p == '-')
180 	check = *p++ == '+';
181       check = check && cancheck;
182 
183       const char *dir, *subdir;
184       switch (p[0])
185 	{
186 	case '\0':
187 	  /* An empty entry says to try the main file's directory.  */
188 	  dir = file_dirname;
189 	  subdir = NULL;
190 	  break;
191 	case '/':
192 	  /* An absolute path says to look there for a subdirectory
193 	     named by the main file's absolute directory.
194 	     This cannot be applied to a relative file name.  */
195 	  if (file_dirname == NULL || file_dirname[0] != '/')
196 	    continue;
197 	  dir = p;
198 	  subdir = file_dirname + 1;
199 	  break;
200 	default:
201 	  /* A relative path says to try a subdirectory of that name
202 	     in the main file's directory.  */
203 	  dir = file_dirname;
204 	  subdir = p;
205 	  break;
206 	}
207 
208 /* ANDROID_CHANGE_BEGIN */
209       char *fname = NULL;
210 /* ANDROID_CHANGE_END */
211       int fd = try_open (dir, subdir, debuglink_file, &fname);
212       if (fd < 0)
213 	switch (errno)
214 	  {
215 	  case ENOENT:
216 	  case ENOTDIR:
217 	    continue;
218 	  default:
219 /* ANDROID_CHANGE_BEGIN */
220 #ifdef __BIONIC__
221             free(path);
222             free(file_dirname);
223 #endif
224 /* ANDROID_CHANGE_END */
225 	    return -1;
226 	  }
227       if (validate (mod, fd, check, debuglink_crc))
228 	{
229 	  *debuginfo_file_name = fname;
230 /* ANDROID_CHANGE_BEGIN */
231 #ifdef __BIONIC__
232           free(path);
233           free(file_dirname);
234 #endif
235 /* ANDROID_CHANGE_END */
236 	  return fd;
237 	}
238       free (fname);
239       close (fd);
240     }
241 
242 /* ANDROID_CHANGE_BEGIN */
243 #ifdef __BIONIC__
244   free(path);
245   free(file_dirname);
246 #endif
247 /* ANDROID_CHANGE_END */
248 
249   /* No dice.  */
250   errno = 0;
251   return -1;
252 }
253 
254 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)255 dwfl_standard_find_debuginfo (Dwfl_Module *mod,
256 			      void **userdata __attribute__ ((unused)),
257 			      const char *modname __attribute__ ((unused)),
258 			      GElf_Addr base __attribute__ ((unused)),
259 			      const char *file_name,
260 			      const char *debuglink_file,
261 			      GElf_Word debuglink_crc,
262 			      char **debuginfo_file_name)
263 {
264   /* First try by build ID if we have one.  If that succeeds or fails
265      other than just by finding nothing, that's all we do.  */
266   const unsigned char *bits;
267   GElf_Addr vaddr;
268   if (INTUSE(dwfl_module_build_id) (mod, &bits, &vaddr) > 0)
269     {
270       int fd = INTUSE(dwfl_build_id_find_debuginfo) (mod,
271 						     NULL, NULL, 0,
272 						     NULL, NULL, 0,
273 						     debuginfo_file_name);
274       if (fd >= 0 || errno != 0)
275 	return fd;
276     }
277 
278   /* Failing that, search the path by name.  */
279   int fd = find_debuginfo_in_path (mod, file_name,
280 				   debuglink_file, debuglink_crc,
281 				   debuginfo_file_name);
282 
283   if (fd < 0 && errno == 0)
284     {
285       /* If FILE_NAME is a symlink, the debug file might be associated
286 	 with the symlink target name instead.  */
287 
288       char *canon = canonicalize_file_name (file_name);
289       if (canon != NULL && strcmp (file_name, canon))
290 	fd = find_debuginfo_in_path (mod, canon,
291 				     debuglink_file, debuglink_crc,
292 				     debuginfo_file_name);
293       free (canon);
294     }
295 
296   return fd;
297 }
298 INTDEF (dwfl_standard_find_debuginfo)
299