• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Find an ELF file for a module from its build ID.
2    Copyright (C) 2007-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 #ifdef HAVE_CONFIG_H
30 # include <config.h>
31 #endif
32 
33 #include "libdwflP.h"
34 #include <inttypes.h>
35 #include <fcntl.h>
36 #include <unistd.h>
37 #include "system.h"
38 
39 
40 int
41 internal_function
__libdwfl_open_by_build_id(Dwfl_Module * mod,bool debug,char ** file_name,const size_t id_len,const uint8_t * id)42 __libdwfl_open_by_build_id (Dwfl_Module *mod, bool debug, char **file_name,
43 			    const size_t id_len, const uint8_t *id)
44 {
45   /* We don't handle very short or really large build-ids.  We need at
46      at least 3 and allow for up to 64 (normally ids are 20 long).  */
47 #define MIN_BUILD_ID_BYTES 3
48 #define MAX_BUILD_ID_BYTES 64
49   if (id_len < MIN_BUILD_ID_BYTES || id_len > MAX_BUILD_ID_BYTES)
50     {
51       __libdwfl_seterrno (DWFL_E_WRONG_ID_ELF);
52       return -1;
53     }
54 
55   /* Search debuginfo_path directories' .build-id/ subdirectories.  */
56 
57   char id_name[sizeof "/.build-id/" + 1 + MAX_BUILD_ID_BYTES * 2
58 	       + sizeof ".debug" - 1];
59   strcpy (id_name, "/.build-id/");
60   int n = snprintf (&id_name[sizeof "/.build-id/" - 1],
61 		    4, "%02" PRIx8 "/", (uint8_t) id[0]);
62   assert (n == 3);
63   for (size_t i = 1; i < id_len; ++i)
64     {
65       n = snprintf (&id_name[sizeof "/.build-id/" - 1 + 3 + (i - 1) * 2],
66 		    3, "%02" PRIx8, (uint8_t) id[i]);
67       assert (n == 2);
68     }
69   if (debug)
70     strcpy (&id_name[sizeof "/.build-id/" - 1 + 3 + (id_len - 1) * 2],
71 	    ".debug");
72 
73   const Dwfl_Callbacks *const cb = mod->dwfl->callbacks;
74   char *path = strdup ((cb->debuginfo_path ? *cb->debuginfo_path : NULL)
75 		       ?: DEFAULT_DEBUGINFO_PATH);
76   if (path == NULL)
77     return -1;
78 
79   int fd = -1;
80   char *dir;
81   char *paths = path;
82   while (fd < 0 && (dir = strsep (&paths, ":")) != NULL)
83     {
84       if (dir[0] == '+' || dir[0] == '-')
85 	++dir;
86 
87       /* Only absolute directory names are useful to us.  */
88       if (dir[0] != '/')
89 	continue;
90 
91       size_t dirlen = strlen (dir);
92       char *name = malloc (dirlen + sizeof id_name);
93       if (unlikely (name == NULL))
94 	break;
95       memcpy (mempcpy (name, dir, dirlen), id_name, sizeof id_name);
96 
97       fd = TEMP_FAILURE_RETRY (open (name, O_RDONLY));
98       if (fd >= 0)
99 	{
100 	  if (*file_name != NULL)
101 	    free (*file_name);
102 	  *file_name = realpath (name, NULL);
103 	  if (*file_name == NULL)
104 	    {
105 	      *file_name = name;
106 	      name = NULL;
107 	    }
108 	}
109       free (name);
110     }
111 
112   free (path);
113 
114   /* If we simply found nothing, clear errno.  If we had some other error
115      with the file, report that.  Possibly this should treat other errors
116      like ENOENT too.  But ignoring all errors could mask some that should
117      be reported.  */
118   if (fd < 0 && errno == ENOENT)
119     errno = 0;
120 
121   return fd;
122 }
123 
124 int
125 internal_function
__libdwfl_open_mod_by_build_id(Dwfl_Module * mod,bool debug,char ** file_name)126 __libdwfl_open_mod_by_build_id (Dwfl_Module *mod, bool debug, char **file_name)
127 {
128   /* If *FILE_NAME was primed into the module, leave it there
129      as the fallback when we have nothing to offer.  */
130   errno = 0;
131   if (mod->build_id_len <= 0)
132     return -1;
133 
134   const size_t id_len = mod->build_id_len;
135   const uint8_t *id = mod->build_id_bits;
136 
137   return __libdwfl_open_by_build_id (mod, debug, file_name, id_len, id);
138 }
139 
140 int
dwfl_build_id_find_elf(Dwfl_Module * mod,void ** userdata,const char * modname,Dwarf_Addr base,char ** file_name,Elf ** elfp)141 dwfl_build_id_find_elf (Dwfl_Module *mod,
142 			void **userdata __attribute__ ((unused)),
143 			const char *modname __attribute__ ((unused)),
144 			Dwarf_Addr base __attribute__ ((unused)),
145 			char **file_name, Elf **elfp)
146 {
147   *elfp = NULL;
148   if (mod->is_executable
149       && mod->dwfl->user_core != NULL
150       && mod->dwfl->user_core->executable_for_core != NULL)
151     {
152       /* When dwfl_core_file_report was called with a non-NULL executable file
153 	 name this callback will replace the Dwfl_Module main.name with the
154 	 recorded executable file when MOD was identified as main executable
155 	 (which then triggers opening and reporting of the executable).  */
156       const char *executable = mod->dwfl->user_core->executable_for_core;
157       int fd = open (executable, O_RDONLY);
158       if (fd >= 0)
159 	{
160 	  *file_name = strdup (executable);
161 	  if (*file_name != NULL)
162 	    return fd;
163 	  else
164 	    close (fd);
165 	}
166     }
167   int fd = __libdwfl_open_mod_by_build_id (mod, false, file_name);
168   if (fd >= 0)
169     {
170       Dwfl_Error error = __libdw_open_file (&fd, elfp, true, false);
171       if (error != DWFL_E_NOERROR)
172 	__libdwfl_seterrno (error);
173       else if (__libdwfl_find_build_id (mod, false, *elfp) == 2)
174 	{
175 	  /* This is a backdoor signal to short-circuit the ID refresh.  */
176 	  mod->main.valid = true;
177 	  return fd;
178 	}
179       else
180 	{
181 	  /* This file does not contain the ID it should!  */
182 	  elf_end (*elfp);
183 	  *elfp = NULL;
184 	  close (fd);
185 	  fd = -1;
186 	}
187       free (*file_name);
188       *file_name = NULL;
189     }
190   else if (errno == 0 && mod->build_id_len > 0)
191     /* Setting this with no file yet loaded is a marker that
192        the build ID is authoritative even if we also know a
193        putative *FILE_NAME.  */
194     mod->main.valid = true;
195 
196   return fd;
197 }
198 INTDEF (dwfl_build_id_find_elf)
199