Branch data Line data Source code
1 : : /* Standard find_debuginfo callback for libdwfl.
2 : : Copyright (C) 2005-2010 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
40 : 83 : 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 [ + + ]: 83 : if (dir == NULL && subdir == NULL)
46 : : {
47 : 16 : fname = strdup (debuglink);
48 [ + - ]: 16 : if (fname == NULL)
49 : : return -1;
50 : : }
51 [ + - ]: 110 : else if ((subdir == NULL ? asprintf (&fname, "%s/%s", dir, debuglink)
52 : 7 : : dir == NULL ? asprintf (&fname, "%s/%s", subdir, debuglink)
53 [ + + ][ + + ]: 101 : : asprintf (&fname, "%s/%s/%s", dir, subdir, debuglink)) < 0)
54 : : return -1;
55 : :
56 : : struct stat64 st;
57 [ + + ][ - + ]: 83 : int fd = TEMP_FAILURE_RETRY (open64 (fname, O_RDONLY));
58 [ + + ]: 83 : if (fd < 0)
59 : 64 : free (fname);
60 [ + - ]: 19 : else if (fstat64 (fd, &st) == 0
61 [ - + ]: 19 : && st.st_ino == main_stat->st_ino
62 [ # # ]: 0 : && st.st_dev == main_stat->st_dev)
63 : : {
64 : : /* This is the main file by another name. Don't look at it again. */
65 : 0 : close (fd);
66 : 0 : errno = ENOENT;
67 : 0 : fd = -1;
68 : : }
69 : : else
70 : 83 : *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
77 : 1 : check_crc (int fd, GElf_Word debuglink_crc)
78 : : {
79 : : uint32_t file_crc;
80 : 2 : return (__libdwfl_crc32_file (fd, &file_crc) == 0
81 [ + - ][ - + ]: 1 : && file_crc == debuglink_crc);
82 : : }
83 : :
84 : : static bool
85 : 19 : validate (Dwfl_Module *mod, int fd, bool check, GElf_Word debuglink_crc)
86 : : {
87 : : /* If we have a build ID, check only that. */
88 [ + + ]: 19 : if (mod->build_id_len > 0)
89 : : {
90 : : /* We need to open an Elf handle on the file so we can check its
91 : : build ID note for validation. Backdoor the handle into the
92 : : module data structure since we had to open it early anyway. */
93 : :
94 : 18 : mod->debug.valid = false;
95 : 18 : Dwfl_Error error = __libdw_open_file (&fd, &mod->debug.elf, false, false);
96 [ - + ]: 18 : if (error != DWFL_E_NOERROR)
97 : 0 : __libdwfl_seterrno (error);
98 [ + - ]: 18 : else if (likely (__libdwfl_find_build_id (mod, false,
99 : : mod->debug.elf) == 2))
100 : : /* Also backdoor the gratuitous flag. */
101 : 18 : mod->debug.valid = true;
102 : : else
103 : : {
104 : : /* A mismatch! */
105 : 0 : elf_end (mod->debug.elf);
106 : 0 : mod->debug.elf = NULL;
107 : 0 : close (fd);
108 : 0 : fd = -1;
109 : : }
110 : :
111 : 18 : return mod->debug.valid;
112 : : }
113 : :
114 [ + - ][ + - ]: 19 : return !check || check_crc (fd, debuglink_crc);
115 : : }
116 : :
117 : : static int
118 : 49 : find_debuginfo_in_path (Dwfl_Module *mod, const char *file_name,
119 : : const char *debuglink_file, GElf_Word debuglink_crc,
120 : : char **debuginfo_file_name)
121 : : {
122 : 49 : bool cancheck = debuglink_crc != (GElf_Word) 0;
123 : :
124 [ + + ]: 49 : const char *file_basename = file_name == NULL ? NULL : basename (file_name);
125 [ + + ]: 49 : if (debuglink_file == NULL)
126 : : {
127 [ + + ]: 32 : if (file_basename == NULL)
128 : : {
129 : 6 : errno = 0;
130 : : return -1;
131 : : }
132 : :
133 : 26 : size_t len = strlen (file_basename);
134 : 26 : char *localname = alloca (len + sizeof ".debug");
135 : 26 : memcpy (localname, file_basename, len);
136 : 26 : memcpy (&localname[len], ".debug", sizeof ".debug");
137 : 26 : debuglink_file = localname;
138 : 26 : cancheck = false;
139 : : }
140 : :
141 : : /* Look for a file named DEBUGLINK_FILE in the directories
142 : : indicated by the debug directory path setting. */
143 : :
144 : 43 : const Dwfl_Callbacks *const cb = mod->dwfl->callbacks;
145 [ + - ][ - + ]: 43 : char *path = strdupa ((cb->debuginfo_path ? *cb->debuginfo_path : NULL)
146 : : ?: DEFAULT_DEBUGINFO_PATH);
147 : :
148 : : /* A leading - or + in the whole path sets whether to check file CRCs. */
149 : 43 : bool defcheck = true;
150 [ - + ]: 43 : if (path[0] == '-' || path[0] == '+')
151 : : {
152 : 0 : defcheck = path[0] == '+';
153 : 0 : ++path;
154 : : }
155 : :
156 : : /* XXX dev/ino should be cached in struct dwfl_file. */
157 : : struct stat64 main_stat;
158 [ + + ]: 86 : if (unlikely ((mod->main.fd != -1 ? fstat64 (mod->main.fd, &main_stat)
[ + - - + ]
[ - + ]
159 : : : file_name != NULL ? stat64 (file_name, &main_stat)
160 : : : -1) < 0))
161 : : {
162 : 0 : main_stat.st_dev = 0;
163 : 0 : main_stat.st_ino = 0;
164 : : }
165 : :
166 : 43 : char *file_dirname = (file_basename == file_name ? NULL
167 [ + + ]: 43 : : strndupa (file_name, file_basename - 1 - file_name));
168 : : char *p;
169 [ + + ]: 158 : while ((p = strsep (&path, ":")) != NULL)
170 : : {
171 : : /* A leading - or + says whether to check file CRCs for this element. */
172 : 91 : bool check = defcheck;
173 [ - + ]: 91 : if (*p == '+' || *p == '-')
174 : 0 : check = *p++ == '+';
175 : 91 : check = check && cancheck;
176 : :
177 : : const char *dir, *subdir;
178 [ + + + ]: 91 : switch (p[0])
179 : : {
180 : : case '\0':
181 : : /* An empty entry says to try the main file's directory. */
182 : : dir = file_dirname;
183 : : subdir = NULL;
184 : : break;
185 : : case '/':
186 : : /* An absolute path says to look there for a subdirectory
187 : : named by the main file's absolute directory.
188 : : This cannot be applied to a relative file name. */
189 [ + + ][ + + ]: 24 : if (file_dirname == NULL || file_dirname[0] != '/')
190 : 8 : continue;
191 : 16 : dir = p;
192 : 16 : subdir = file_dirname + 1;
193 : 16 : break;
194 : : default:
195 : : /* A relative path says to try a subdirectory of that name
196 : : in the main file's directory. */
197 : 24 : dir = file_dirname;
198 : 24 : subdir = p;
199 : 24 : break;
200 : : }
201 : :
202 : 83 : char *fname = NULL;
203 : 83 : int fd = try_open (&main_stat, dir, subdir, debuglink_file, &fname);
204 [ + + ]: 83 : if (fd < 0)
205 [ + - ]: 64 : switch (errno)
206 : : {
207 : : case ENOENT:
208 : : case ENOTDIR:
209 : 64 : continue;
210 : : default:
211 : : return -1;
212 : : }
213 [ + - ]: 19 : if (validate (mod, fd, check, debuglink_crc))
214 : : {
215 : 19 : *debuginfo_file_name = fname;
216 : : return fd;
217 : : }
218 : 0 : free (fname);
219 : 91 : close (fd);
220 : : }
221 : :
222 : : /* No dice. */
223 : 49 : errno = 0;
224 : : return -1;
225 : : }
226 : :
227 : : int
228 : 46 : dwfl_standard_find_debuginfo (Dwfl_Module *mod,
229 : : void **userdata __attribute__ ((unused)),
230 : : const char *modname __attribute__ ((unused)),
231 : : GElf_Addr base __attribute__ ((unused)),
232 : : const char *file_name,
233 : : const char *debuglink_file,
234 : : GElf_Word debuglink_crc,
235 : : char **debuginfo_file_name)
236 : : {
237 : : /* First try by build ID if we have one. If that succeeds or fails
238 : : other than just by finding nothing, that's all we do. */
239 : : const unsigned char *bits;
240 : : GElf_Addr vaddr;
241 [ + + ]: 46 : if (INTUSE(dwfl_module_build_id) (mod, &bits, &vaddr) > 0)
242 : : {
243 : 38 : int fd = INTUSE(dwfl_build_id_find_debuginfo) (mod,
244 : : NULL, NULL, 0,
245 : : NULL, NULL, 0,
246 : : debuginfo_file_name);
247 [ + + ][ + - ]: 38 : if (fd >= 0 || mod->debug.elf != NULL || errno != 0)
[ + - ]
248 : : return fd;
249 : : }
250 : :
251 : : /* Failing that, search the path by name. */
252 : 37 : int fd = find_debuginfo_in_path (mod, file_name,
253 : : debuglink_file, debuglink_crc,
254 : : debuginfo_file_name);
255 : :
256 [ + + ][ + - ]: 37 : if (fd < 0 && errno == 0)
257 : : {
258 : : /* If FILE_NAME is a symlink, the debug file might be associated
259 : : with the symlink target name instead. */
260 : :
261 : 18 : char *canon = canonicalize_file_name (file_name);
262 [ + + ][ + - ]: 18 : if (canon != NULL && strcmp (file_name, canon))
263 : 12 : fd = find_debuginfo_in_path (mod, canon,
264 : : debuglink_file, debuglink_crc,
265 : : debuginfo_file_name);
266 : 46 : free (canon);
267 : : }
268 : :
269 : : return fd;
270 : : }
271 : : INTDEF (dwfl_standard_find_debuginfo)
|