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.  */
39static int
40try_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.  */
77static inline bool
78check_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
85static bool
86validate (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
157static int
158find_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;
324fail_free:
325  free (localpath);
326  free (localname);
327  free (file_dirname);
328  return -1;
329}
330
331int
332dwfl_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}
385INTDEF (dwfl_standard_find_debuginfo)
386