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