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.  */
39 static int
try_open(const struct stat64 * main_stat,const char * dir,const char * subdir,const char * debuglink,char ** debuginfo_file_name)40 try_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.  */
76 static inline bool
check_crc(int fd,GElf_Word debuglink_crc)77 check_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 
84 static bool
validate(Dwfl_Module * mod,int fd,bool check,GElf_Word debuglink_crc)85 validate (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 
156 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)157 find_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 
297 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)298 dwfl_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 }
351 INTDEF (dwfl_standard_find_debuginfo)
352