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