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