1/* Standard find_debuginfo callback for libdwfl.
2   Copyright (C) 2005-2010, 2014 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 open64 [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 stat64 *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 (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 stat64 st;
57  int fd = TEMP_FAILURE_RETRY (open64 (fname, O_RDONLY));
58  if (fd < 0)
59    free (fname);
60  else if (fstat64 (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      close (fd);
66      errno = ENOENT;
67      fd = -1;
68    }
69  else
70    *debuginfo_file_name = fname;
71
72  return fd;
73}
74
75/* Return true iff the FD's contents CRC matches DEBUGLINK_CRC.  */
76static inline bool
77check_crc (int fd, GElf_Word debuglink_crc)
78{
79  uint32_t file_crc;
80  return (__libdwfl_crc32_file (fd, &file_crc) == 0
81	  && file_crc == debuglink_crc);
82}
83
84static bool
85validate (Dwfl_Module *mod, int fd, bool check, GElf_Word debuglink_crc)
86{
87  /* For alt debug files always check the build-id from the Dwarf and alt.  */
88  if (mod->dw != NULL)
89    {
90      bool valid = false;
91      const void *build_id;
92      const char *altname;
93      ssize_t build_id_len = INTUSE(dwelf_dwarf_gnu_debugaltlink) (mod->dw,
94								   &altname,
95								   &build_id);
96      if (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	  Dwfl_Error error = __libdw_open_file (&fd, &mod->alt_elf,
102						false, false);
103	  if (error != DWFL_E_NOERROR)
104	    __libdwfl_seterrno (error);
105	  else
106	    {
107	      const void *alt_build_id;
108	      ssize_t alt_len = INTUSE(dwelf_elf_gnu_build_id) (mod->alt_elf,
109								&alt_build_id);
110	      if (alt_len > 0 && alt_len == build_id_len
111		  && memcmp (build_id, alt_build_id, alt_len) == 0)
112		valid = true;
113	      else
114		{
115		  /* A mismatch!  */
116		  elf_end (mod->alt_elf);
117		  mod->alt_elf = NULL;
118		  close (fd);
119		  fd = -1;
120		}
121	    }
122	}
123      return valid;
124    }
125
126  /* If we have a build ID, check only that.  */
127  if (mod->build_id_len > 0)
128    {
129      /* We need to open an Elf handle on the file so we can check its
130	 build ID note for validation.  Backdoor the handle into the
131	 module data structure since we had to open it early anyway.  */
132
133      mod->debug.valid = false;
134      Dwfl_Error error = __libdw_open_file (&fd, &mod->debug.elf, false, false);
135      if (error != DWFL_E_NOERROR)
136	__libdwfl_seterrno (error);
137      else if (likely (__libdwfl_find_build_id (mod, false,
138						mod->debug.elf) == 2))
139	/* Also backdoor the gratuitous flag.  */
140	mod->debug.valid = true;
141      else
142	{
143	  /* A mismatch!  */
144	  elf_end (mod->debug.elf);
145	  mod->debug.elf = NULL;
146	  close (fd);
147	  fd = -1;
148	}
149
150      return mod->debug.valid;
151    }
152
153  return !check || check_crc (fd, debuglink_crc);
154}
155
156static int
157find_debuginfo_in_path (Dwfl_Module *mod, const char *file_name,
158			const char *debuglink_file, GElf_Word debuglink_crc,
159			char **debuginfo_file_name)
160{
161  bool cancheck = debuglink_crc != (GElf_Word) 0;
162
163  const char *file_basename = file_name == NULL ? NULL : basename (file_name);
164  if (debuglink_file == NULL)
165    {
166      /* For a alt debug multi file we need a name, for a separate debug
167	 name we may be able to fall back on file_basename.debug.  */
168      if (file_basename == NULL || mod->dw != NULL)
169	{
170	  errno = 0;
171	  return -1;
172	}
173
174      size_t len = strlen (file_basename);
175      char *localname = alloca (len + sizeof ".debug");
176      memcpy (localname, file_basename, len);
177      memcpy (&localname[len], ".debug", sizeof ".debug");
178      debuglink_file = localname;
179      cancheck = false;
180    }
181
182  /* Look for a file named DEBUGLINK_FILE in the directories
183     indicated by the debug directory path setting.  */
184
185  const Dwfl_Callbacks *const cb = mod->dwfl->callbacks;
186  char *path = strdupa ((cb->debuginfo_path ? *cb->debuginfo_path : NULL)
187			?: DEFAULT_DEBUGINFO_PATH);
188
189  /* A leading - or + in the whole path sets whether to check file CRCs.  */
190  bool defcheck = true;
191  if (path[0] == '-' || path[0] == '+')
192    {
193      defcheck = path[0] == '+';
194      ++path;
195    }
196
197  /* XXX dev/ino should be cached in struct dwfl_file.  */
198  struct stat64 main_stat;
199  if (unlikely ((mod->main.fd != -1 ? fstat64 (mod->main.fd, &main_stat)
200		 : file_name != NULL ? stat64 (file_name, &main_stat)
201		 : -1) < 0))
202    {
203      main_stat.st_dev = 0;
204      main_stat.st_ino = 0;
205    }
206
207  char *file_dirname = (file_basename == file_name ? NULL
208			: strndupa (file_name, file_basename - 1 - file_name));
209  char *p;
210  while ((p = strsep (&path, ":")) != NULL)
211    {
212      /* A leading - or + says whether to check file CRCs for this element.  */
213      bool check = defcheck;
214      if (*p == '+' || *p == '-')
215	check = *p++ == '+';
216      check = check && cancheck;
217
218      const char *dir, *subdir, *file;
219      switch (p[0])
220	{
221	case '\0':
222	  /* An empty entry says to try the main file's directory.  */
223	  dir = file_dirname;
224	  subdir = NULL;
225	  file = debuglink_file;
226	  break;
227	case '/':
228	  /* An absolute path says to look there for a subdirectory
229	     named by the main file's absolute directory.  This cannot
230	     be applied to a relative file name.  For alt debug files
231	     it means to look for the basename file in that dir or the
232	     .dwz subdir (see below).  */
233	  if (mod->dw == NULL
234	      && (file_dirname == NULL || file_dirname[0] != '/'))
235	    continue;
236	  dir = p;
237	  if (mod->dw == NULL)
238	    {
239	      subdir = file_dirname + 1;
240	      file = debuglink_file;
241	    }
242	  else
243	    {
244	      subdir = NULL;
245	      file = basename (debuglink_file);
246	    }
247	  break;
248	default:
249	  /* A relative path says to try a subdirectory of that name
250	     in the main file's directory.  */
251	  dir = file_dirname;
252	  subdir = p;
253	  file = debuglink_file;
254	  break;
255	}
256
257      char *fname = NULL;
258      int fd = try_open (&main_stat, dir, subdir, file, &fname);
259      if (fd < 0)
260	switch (errno)
261	  {
262	  case ENOENT:
263	  case ENOTDIR:
264	    /* If we are looking for the alt file also try the .dwz subdir.
265	       But only if this is the empty or absolute path.  */
266	    if (mod->dw != NULL && (p[0] == '\0' || p[0] == '/'))
267	      {
268		fd = try_open (&main_stat, dir, ".dwz",
269			       basename (file), &fname);
270		if (fd < 0)
271		  {
272		    if (errno != ENOENT && errno != ENOTDIR)
273		      return -1;
274		    else
275		      continue;
276		  }
277		break;
278	      }
279	    continue;
280	  default:
281	    return -1;
282	  }
283      if (validate (mod, fd, check, debuglink_crc))
284	{
285	  *debuginfo_file_name = fname;
286	  return fd;
287	}
288      free (fname);
289      close (fd);
290    }
291
292  /* No dice.  */
293  errno = 0;
294  return -1;
295}
296
297int
298dwfl_standard_find_debuginfo (Dwfl_Module *mod,
299			      void **userdata __attribute__ ((unused)),
300			      const char *modname __attribute__ ((unused)),
301			      GElf_Addr base __attribute__ ((unused)),
302			      const char *file_name,
303			      const char *debuglink_file,
304			      GElf_Word debuglink_crc,
305			      char **debuginfo_file_name)
306{
307  /* First try by build ID if we have one.  If that succeeds or fails
308     other than just by finding nothing, that's all we do.  */
309  const unsigned char *bits;
310  GElf_Addr vaddr;
311  if (INTUSE(dwfl_module_build_id) (mod, &bits, &vaddr) > 0)
312    {
313      /* Dropping most arguments means we cannot rely on them in
314	 dwfl_build_id_find_debuginfo.  But leave it that way since
315	 some user code out there also does this, so we'll have to
316	 handle it anyway.  */
317      int fd = INTUSE(dwfl_build_id_find_debuginfo) (mod,
318						     NULL, NULL, 0,
319						     NULL, NULL, 0,
320						     debuginfo_file_name);
321
322      /* Did the build_id callback find something or report an error?
323         Then we are done.  Otherwise fallback on path based search.  */
324      if (fd >= 0
325	  || (mod->dw == NULL && mod->debug.elf != NULL)
326	  || (mod->dw != NULL && mod->alt_elf != NULL)
327	  || errno != 0)
328	return fd;
329    }
330
331  /* Failing that, search the path by name.  */
332  int fd = find_debuginfo_in_path (mod, file_name,
333				   debuglink_file, debuglink_crc,
334				   debuginfo_file_name);
335
336  if (fd < 0 && errno == 0)
337    {
338      /* If FILE_NAME is a symlink, the debug file might be associated
339	 with the symlink target name instead.  */
340
341      char *canon = canonicalize_file_name (file_name);
342      if (canon != NULL && strcmp (file_name, canon))
343	fd = find_debuginfo_in_path (mod, canon,
344				     debuglink_file, debuglink_crc,
345				     debuginfo_file_name);
346      free (canon);
347    }
348
349  return fd;
350}
351INTDEF (dwfl_standard_find_debuginfo)
352