• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Standard find_debuginfo callback for libdwfl.
2    Copyright (C) 2005-2010, 2014, 2015, 2019 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 #ifdef HAVE_CONFIG_H
30 # include <config.h>
31 #endif
32 
33 #include "libdwflP.h"
34 #include <stdio.h>
35 #include <fcntl.h>
36 #include <sys/stat.h>
37 #include "system.h"
38 
39 
40 /* Try to open [DIR/][SUBDIR/]DEBUGLINK, return file descriptor or -1.
41    On success, *DEBUGINFO_FILE_NAME has the malloc'd name of the open file.  */
42 static int
try_open(const struct stat * main_stat,const char * dir,const char * subdir,const char * debuglink,char ** debuginfo_file_name)43 try_open (const struct stat *main_stat,
44 	  const char *dir, const char *subdir, const char *debuglink,
45 	  char **debuginfo_file_name)
46 {
47   char *fname;
48   if (dir == NULL && subdir == NULL)
49     {
50       fname = strdup (debuglink);
51       if (unlikely (fname == NULL))
52 	return -1;
53     }
54   else if ((subdir == NULL ? asprintf (&fname, "%s/%s", dir, debuglink)
55 	    : dir == NULL ? asprintf (&fname, "%s/%s", subdir, debuglink)
56 	    : asprintf (&fname, "%s/%s/%s", dir, subdir, debuglink)) < 0)
57     return -1;
58 
59   struct stat st;
60   int fd = TEMP_FAILURE_RETRY (open (fname, O_RDONLY));
61   if (fd < 0)
62     free (fname);
63   else if (fstat (fd, &st) == 0
64 	   && st.st_ino == main_stat->st_ino
65 	   && st.st_dev == main_stat->st_dev)
66     {
67       /* This is the main file by another name.  Don't look at it again.  */
68       free (fname);
69       close (fd);
70       errno = ENOENT;
71       fd = -1;
72     }
73   else
74     *debuginfo_file_name = fname;
75 
76   return fd;
77 }
78 
79 /* Return true iff the FD's contents CRC matches DEBUGLINK_CRC.  */
80 static inline bool
check_crc(int fd,GElf_Word debuglink_crc)81 check_crc (int fd, GElf_Word debuglink_crc)
82 {
83   uint32_t file_crc;
84   return (__libdwfl_crc32_file (fd, &file_crc) == 0
85 	  && file_crc == debuglink_crc);
86 }
87 
88 static bool
validate(Dwfl_Module * mod,int fd,bool check,GElf_Word debuglink_crc)89 validate (Dwfl_Module *mod, int fd, bool check, GElf_Word debuglink_crc)
90 {
91   /* For alt debug files always check the build-id from the Dwarf and alt.  */
92   if (mod->dw != NULL)
93     {
94       bool valid = false;
95       const void *build_id;
96       const char *altname;
97       ssize_t build_id_len = INTUSE(dwelf_dwarf_gnu_debugaltlink) (mod->dw,
98 								   &altname,
99 								   &build_id);
100       if (build_id_len > 0)
101 	{
102 	  /* We need to open an Elf handle on the file so we can check its
103 	     build ID note for validation.  Backdoor the handle into the
104 	     module data structure since we had to open it early anyway.  */
105 	  Dwfl_Error error = __libdw_open_file (&fd, &mod->alt_elf,
106 						false, false);
107 	  if (error != DWFL_E_NOERROR)
108 	    __libdwfl_seterrno (error);
109 	  else
110 	    {
111 	      const void *alt_build_id;
112 	      ssize_t alt_len = INTUSE(dwelf_elf_gnu_build_id) (mod->alt_elf,
113 								&alt_build_id);
114 	      if (alt_len > 0 && alt_len == build_id_len
115 		  && memcmp (build_id, alt_build_id, alt_len) == 0)
116 		valid = true;
117 	      else
118 		{
119 		  /* A mismatch!  */
120 		  elf_end (mod->alt_elf);
121 		  mod->alt_elf = NULL;
122 		  close (fd);
123 		  fd = -1;
124 		}
125 	    }
126 	}
127       return valid;
128     }
129 
130   /* If we have a build ID, check only that.  */
131   if (mod->build_id_len > 0)
132     {
133       /* We need to open an Elf handle on the file so we can check its
134 	 build ID note for validation.  Backdoor the handle into the
135 	 module data structure since we had to open it early anyway.  */
136 
137       mod->debug.valid = false;
138       Dwfl_Error error = __libdw_open_file (&fd, &mod->debug.elf, false, false);
139       if (error != DWFL_E_NOERROR)
140 	__libdwfl_seterrno (error);
141       else if (likely (__libdwfl_find_build_id (mod, false,
142 						mod->debug.elf) == 2))
143 	/* Also backdoor the gratuitous flag.  */
144 	mod->debug.valid = true;
145       else
146 	{
147 	  /* A mismatch!  */
148 	  elf_end (mod->debug.elf);
149 	  mod->debug.elf = NULL;
150 	  close (fd);
151 	  fd = -1;
152 	}
153 
154       return mod->debug.valid;
155     }
156 
157   return !check || check_crc (fd, debuglink_crc);
158 }
159 
160 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)161 find_debuginfo_in_path (Dwfl_Module *mod, const char *file_name,
162 			const char *debuglink_file, GElf_Word debuglink_crc,
163 			char **debuginfo_file_name)
164 {
165   bool cancheck = debuglink_crc != (GElf_Word) 0;
166 
167   const char *file_basename = file_name == NULL ? NULL : basename (file_name);
168   char *localname = NULL;
169 
170   /* We invent a debuglink .debug name if NULL, but then want to try the
171      basename too.  */
172   bool debuglink_null = debuglink_file == NULL;
173   if (debuglink_null)
174     {
175       /* For a alt debug multi file we need a name, for a separate debug
176 	 name we may be able to fall back on file_basename.debug.  */
177       if (file_basename == NULL || mod->dw != NULL)
178 	{
179 	  errno = 0;
180 	  return -1;
181 	}
182 
183       size_t len = strlen (file_basename);
184       localname = malloc (len + sizeof ".debug");
185       if (unlikely (localname == NULL))
186 	return -1;
187       memcpy (localname, file_basename, len);
188       memcpy (&localname[len], ".debug", sizeof ".debug");
189       debuglink_file = localname;
190       cancheck = false;
191     }
192 
193   /* Look for a file named DEBUGLINK_FILE in the directories
194      indicated by the debug directory path setting.  */
195 
196   const Dwfl_Callbacks *const cb = mod->dwfl->callbacks;
197   char *localpath = strdup ((cb->debuginfo_path ? *cb->debuginfo_path : NULL)
198 			    ?: DEFAULT_DEBUGINFO_PATH);
199   if (unlikely (localpath == NULL))
200     {
201       free (localname);
202       return -1;
203     }
204 
205   /* A leading - or + in the whole path sets whether to check file CRCs.  */
206   bool defcheck = true;
207   char *path = localpath;
208   if (path[0] == '-' || path[0] == '+')
209     {
210       defcheck = path[0] == '+';
211       ++path;
212     }
213 
214   /* XXX dev/ino should be cached in struct dwfl_file.  */
215   struct stat main_stat;
216   if (unlikely ((mod->main.fd != -1 ? fstat (mod->main.fd, &main_stat)
217 		 : file_name != NULL ? stat (file_name, &main_stat)
218 		 : -1) < 0))
219     {
220       main_stat.st_dev = 0;
221       main_stat.st_ino = 0;
222     }
223 
224   char *file_dirname = (file_basename == file_name ? NULL
225 			: strndup (file_name, file_basename - 1 - file_name));
226   if (file_basename != file_name && file_dirname == NULL)
227     {
228       free (localpath);
229       free (localname);
230       return -1;
231     }
232   char *p;
233   while ((p = strsep (&path, ":")) != NULL)
234     {
235       /* A leading - or + says whether to check file CRCs for this element.  */
236       bool check = defcheck;
237       if (*p == '+' || *p == '-')
238 	check = *p++ == '+';
239       check = check && cancheck;
240 
241       /* Try the basename too, if we made up the debuglink name and this
242 	 is not the main directory.  */
243       bool try_file_basename;
244 
245       const char *dir, *subdir, *file;
246       switch (p[0])
247 	{
248 	case '\0':
249 	  /* An empty entry says to try the main file's directory.  */
250 	  dir = file_dirname;
251 	  subdir = NULL;
252 	  file = debuglink_file;
253 	  try_file_basename = false;
254 	  break;
255 	case '/':
256 	  /* An absolute path says to look there for a subdirectory
257 	     named by the main file's absolute directory.  This cannot
258 	     be applied to a relative file name.  For alt debug files
259 	     it means to look for the basename file in that dir or the
260 	     .dwz subdir (see below).  */
261 	  if (mod->dw == NULL
262 	      && (file_dirname == NULL || file_dirname[0] != '/'))
263 	    continue;
264 	  dir = p;
265 	  if (mod->dw == NULL)
266 	    {
267 	      subdir = file_dirname;
268 	      /* We want to explore all sub-subdirs.  Chop off one slash
269 		 at a time.  */
270 	    explore_dir:
271 	      subdir = strchr (subdir, '/');
272 	      if (subdir != NULL)
273 		subdir = subdir + 1;
274 	      if (subdir && *subdir == 0)
275 		continue;
276 	      file = debuglink_file;
277 	    }
278 	  else
279 	    {
280 	      subdir = NULL;
281 	      file = basename (debuglink_file);
282 	    }
283 	  try_file_basename = debuglink_null;
284 	  break;
285 	default:
286 	  /* A relative path says to try a subdirectory of that name
287 	     in the main file's directory.  */
288 	  dir = file_dirname;
289 	  subdir = p;
290 	  file = debuglink_file;
291 	  try_file_basename = debuglink_null;
292 	  break;
293 	}
294 
295       char *fname = NULL;
296       int fd = try_open (&main_stat, dir, subdir, file, &fname);
297       if (fd < 0 && try_file_basename)
298 	fd = try_open (&main_stat, dir, subdir, file_basename, &fname);
299       if (fd < 0)
300 	switch (errno)
301 	  {
302 	  case ENOENT:
303 	  case ENOTDIR:
304 	    /* If we are looking for the alt file also try the .dwz subdir.
305 	       But only if this is the empty or absolute path.  */
306 	    if (mod->dw != NULL && (p[0] == '\0' || p[0] == '/'))
307 	      {
308 		fd = try_open (&main_stat, dir, ".dwz",
309 			       basename (file), &fname);
310 		if (fd < 0)
311 		  {
312 		    if (errno != ENOENT && errno != ENOTDIR)
313 		      goto fail_free;
314 		    else
315 		      continue;
316 		  }
317 		break;
318 	      }
319 	    /* If possible try again with a sub-subdir.  */
320 	    if (mod->dw == NULL && subdir)
321 	      goto explore_dir;
322 	    continue;
323 	  default:
324 	    goto fail_free;
325 	  }
326       if (validate (mod, fd, check, debuglink_crc))
327 	{
328 	  free (localpath);
329 	  free (localname);
330 	  free (file_dirname);
331 	  *debuginfo_file_name = fname;
332 	  return fd;
333 	}
334       free (fname);
335       close (fd);
336     }
337 
338   /* No dice.  */
339   errno = 0;
340 fail_free:
341   free (localpath);
342   free (localname);
343   free (file_dirname);
344   return -1;
345 }
346 
347 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)348 dwfl_standard_find_debuginfo (Dwfl_Module *mod,
349 			      void **userdata __attribute__ ((unused)),
350 			      const char *modname __attribute__ ((unused)),
351 			      GElf_Addr base __attribute__ ((unused)),
352 			      const char *file_name,
353 			      const char *debuglink_file,
354 			      GElf_Word debuglink_crc,
355 			      char **debuginfo_file_name)
356 {
357   if (mod == NULL)
358     return -1;
359 
360   /* First try by build ID if we have one.  If that succeeds or fails
361      other than just by finding nothing, that's all we do.  */
362   const unsigned char *bits = NULL;
363   GElf_Addr vaddr;
364   int bits_len;
365   if ((bits_len = INTUSE(dwfl_module_build_id) (mod, &bits, &vaddr)) > 0)
366     {
367       /* Dropping most arguments means we cannot rely on them in
368 	 dwfl_build_id_find_debuginfo.  But leave it that way since
369 	 some user code out there also does this, so we'll have to
370 	 handle it anyway.  */
371       int fd = INTUSE(dwfl_build_id_find_debuginfo) (mod,
372 						     NULL, NULL, 0,
373 						     NULL, NULL, 0,
374 						     debuginfo_file_name);
375 
376       /* Did the build_id callback find something or report an error?
377          Then we are done.  Otherwise fallback on path based search.  */
378       if (fd >= 0
379 	  || (mod->dw == NULL && mod->debug.elf != NULL)
380 	  || (mod->dw != NULL && mod->alt_elf != NULL)
381 	  || errno != 0)
382 	return fd;
383     }
384 
385   /* Failing that, search the path by name.  */
386   int fd = find_debuginfo_in_path (mod, file_name,
387 				   debuglink_file, debuglink_crc,
388 				   debuginfo_file_name);
389 
390   if (fd < 0 && errno == 0 && file_name != NULL)
391     {
392       /* If FILE_NAME is a symlink, the debug file might be associated
393 	 with the symlink target name instead.  */
394 
395       char *canon = realpath (file_name, NULL);
396       if (canon != NULL && strcmp (file_name, canon))
397 	fd = find_debuginfo_in_path (mod, canon,
398 				     debuglink_file, debuglink_crc,
399 				     debuginfo_file_name);
400       free (canon);
401     }
402 
403 #ifdef ENABLE_LIBDEBUGINFOD
404   /* Still nothing? Try if we can use the debuginfod client.
405      But note that we might be looking for the alt file.
406      We use the same trick as dwfl_build_id_find_debuginfo.
407      If the debug file (dw) is already set, then we must be
408      looking for the altfile. But we cannot use the actual
409      file/path name given as hint. We'll have to lookup the
410      alt file "build-id". Because the debuginfod client only
411      handles build-ids.  */
412   if (fd < 0)
413     {
414       if (mod->dw != NULL)
415 	{
416 	  const char *altname;
417 	  bits_len = INTUSE(dwelf_dwarf_gnu_debugaltlink) (mod->dw, &altname,
418 							   (const void **)
419 							   &bits);
420 	}
421 
422       if (bits_len > 0)
423 	fd = __libdwfl_debuginfod_find_debuginfo (mod->dwfl, bits, bits_len);
424     }
425 #endif
426 
427   return fd;
428 }
429 INTDEF (dwfl_standard_find_debuginfo)
430