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