Branch data Line data Source code
1 : : /* Standard libdwfl callbacks for debugging the running Linux kernel.
2 : : Copyright (C) 2005-2011, 2013, 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 : : /* In case we have a bad fts we include this before config.h because it
30 : : can't handle _FILE_OFFSET_BITS.
31 : : Everything we need here is fine if its declarations just come first.
32 : : Also, include sys/types.h before fts. On some systems fts.h is not self
33 : : contained. */
34 : : #ifdef BAD_FTS
35 : : #include <sys/types.h>
36 : : #include <fts.h>
37 : : #endif
38 : :
39 : : #include <config.h>
40 : : #include <system.h>
41 : :
42 : : #include "libelfP.h"
43 : : #include "libdwflP.h"
44 : : #include <inttypes.h>
45 : : #include <errno.h>
46 : : #include <stdio.h>
47 : : #include <stdio_ext.h>
48 : : #include <string.h>
49 : : #include <stdlib.h>
50 : : #include <sys/utsname.h>
51 : : #include <fcntl.h>
52 : : #include <unistd.h>
53 : :
54 : : /* If fts.h is included before config.h, its indirect inclusions may not
55 : : give us the right LFS aliases of these functions, so map them manually. */
56 : : #ifdef BAD_FTS
57 : : #ifdef _FILE_OFFSET_BITS
58 : : #define open open64
59 : : #define fopen fopen64
60 : : #endif
61 : : #else
62 : : #include <sys/types.h>
63 : : #include <fts.h>
64 : : #endif
65 : :
66 : :
67 : : #define KERNEL_MODNAME "kernel"
68 : :
69 : : #define MODULEDIRFMT "/lib/modules/%s"
70 : :
71 : : #define KNOTESFILE "/sys/kernel/notes"
72 : : #define MODNOTESFMT "/sys/module/%s/notes"
73 : : #define KSYMSFILE "/proc/kallsyms"
74 : : #define MODULELIST "/proc/modules"
75 : : #define SECADDRDIRFMT "/sys/module/%s/sections/"
76 : : #define MODULE_SECT_NAME_LEN 32 /* Minimum any linux/module.h has had. */
77 : :
78 : :
79 : : static const char *vmlinux_suffixes[] =
80 : : {
81 : : ".gz",
82 : : #ifdef USE_BZLIB
83 : : ".bz2",
84 : : #endif
85 : : #ifdef USE_LZMA
86 : : ".xz",
87 : : #endif
88 : : };
89 : :
90 : : /* Try to open the given file as it is or under the debuginfo directory. */
91 : : static int
92 : 0 : try_kernel_name (Dwfl *dwfl, char **fname, bool try_debug)
93 : : {
94 [ # # ]: 0 : if (*fname == NULL)
95 : : return -1;
96 : :
97 : : /* Don't bother trying *FNAME itself here if the path will cause it to be
98 : : tried because we give its own basename as DEBUGLINK_FILE. */
99 : 0 : int fd = ((((dwfl->callbacks->debuginfo_path
100 [ # # ]: 0 : ? *dwfl->callbacks->debuginfo_path : NULL)
101 [ # # # # ]: 0 : ?: DEFAULT_DEBUGINFO_PATH)[0] == ':') ? -1
102 [ # # # # ]: 0 : : TEMP_FAILURE_RETRY (open (*fname, O_RDONLY)));
103 : :
104 [ # # ]: 0 : if (fd < 0)
105 : : {
106 : 0 : Dwfl_Module fakemod = { .dwfl = dwfl };
107 : :
108 [ # # ]: 0 : if (try_debug)
109 : : /* Passing NULL for DEBUGLINK_FILE searches for both the basenamer
110 : : "vmlinux" and the default of basename + ".debug", to look for
111 : : "vmlinux.debug" files. */
112 : 0 : fd = INTUSE(dwfl_standard_find_debuginfo) (&fakemod, NULL, NULL, 0,
113 : : *fname, NULL, 0,
114 : : &fakemod.debug.name);
115 : : else
116 : : /* Try the file's unadorned basename as DEBUGLINK_FILE,
117 : : to look only for "vmlinux" files. */
118 : 0 : fd = INTUSE(dwfl_standard_find_debuginfo) (&fakemod, NULL, NULL, 0,
119 : : *fname, xbasename (*fname),
120 : : 0, &fakemod.debug.name);
121 : :
122 [ # # ]: 0 : if (fakemod.debug.name != NULL)
123 : : {
124 : 0 : free (*fname);
125 : 0 : *fname = fakemod.debug.name;
126 : : }
127 : : }
128 : :
129 [ # # ]: 0 : if (fd < 0)
130 : : for (size_t i = 0;
131 [ # # ]: 0 : i < sizeof vmlinux_suffixes / sizeof vmlinux_suffixes[0] && fd < 0;
132 : 0 : ++i)
133 : : {
134 : 0 : char *zname;
135 [ # # ]: 0 : if (asprintf (&zname, "%s%s", *fname, vmlinux_suffixes[i]) > 0)
136 : : {
137 [ # # # # ]: 0 : fd = TEMP_FAILURE_RETRY (open (zname, O_RDONLY));
138 [ # # ]: 0 : if (fd < 0)
139 : 0 : free (zname);
140 : : else
141 : : {
142 : 0 : free (*fname);
143 : 0 : *fname = zname;
144 : : }
145 : : }
146 : : }
147 : :
148 [ # # ]: 0 : if (fd < 0)
149 : : {
150 : 0 : free (*fname);
151 : 0 : *fname = NULL;
152 : : }
153 : :
154 : : return fd;
155 : : }
156 : :
157 : : static inline const char *
158 : 0 : kernel_release (void)
159 : : {
160 : : #ifdef __linux__
161 : : /* Cache the `uname -r` string we'll use. */
162 : 0 : static struct utsname utsname;
163 [ # # # # ]: 0 : if (utsname.release[0] == '\0' && uname (&utsname) != 0)
164 : : return NULL;
165 : : return utsname.release;
166 : : #else
167 : : /* Used for finding the running linux kernel, which isn't supported
168 : : on non-linux kernel systems. */
169 : : errno = ENOTSUP;
170 : : return NULL;
171 : : #endif
172 : : }
173 : :
174 : : static int
175 : 0 : find_kernel_elf (Dwfl *dwfl, const char *release, char **fname)
176 : : {
177 : : /* First try to find an uncompressed vmlinux image. Possibly
178 : : including debuginfo. */
179 [ # # ]: 0 : if (release == NULL
180 [ # # ]: 0 : || ((release[0] == '/'
181 [ # # ]: 0 : ? asprintf (fname, "%s/vmlinux", release)
182 [ # # ]: 0 : : asprintf (fname, "/boot/vmlinux-%s", release)) < 0))
183 : 0 : return -1;
184 : :
185 : 0 : int fd = try_kernel_name (dwfl, fname, true);
186 [ # # # # ]: 0 : if (fd < 0 && release[0] != '/')
187 : : {
188 : 0 : free (*fname);
189 [ # # ]: 0 : if (asprintf (fname, MODULEDIRFMT "/vmlinux", release) < 0)
190 : : return -1;
191 : 0 : fd = try_kernel_name (dwfl, fname, true);
192 : : }
193 : :
194 : : /* There might be a compressed vmlinuz image. Probably without
195 : : debuginfo, but try to find it under the debug path also, just in
196 : : case. */
197 [ # # ]: 0 : if (fd < 0)
198 : : {
199 : 0 : free (*fname);
200 [ # # ]: 0 : if ((release[0] == '/'
201 : 0 : ? asprintf (fname, "%s/vmlinuz", release)
202 [ # # ]: 0 : : asprintf (fname, "/boot/vmlinuz-%s", release)) < 0)
203 : : return -1;
204 : :
205 : 0 : fd = try_kernel_name (dwfl, fname, true);
206 [ # # # # ]: 0 : if (fd < 0 && release[0] != '/')
207 : : {
208 : 0 : free (*fname);
209 [ # # ]: 0 : if (asprintf (fname, MODULEDIRFMT "/vmlinuz", release) < 0)
210 : : return -1;
211 : 0 : fd = try_kernel_name (dwfl, fname, true);
212 : : }
213 : : }
214 : :
215 : : return fd;
216 : : }
217 : :
218 : : static int
219 : 0 : get_release (Dwfl *dwfl, const char **release)
220 : : {
221 [ # # ]: 0 : if (dwfl == NULL)
222 : : return -1;
223 : :
224 [ # # ]: 0 : const char *release_string = release == NULL ? NULL : *release;
225 [ # # ]: 0 : if (release_string == NULL)
226 : : {
227 : 0 : release_string = kernel_release ();
228 [ # # ]: 0 : if (release_string == NULL)
229 : 0 : return errno;
230 [ # # ]: 0 : if (release != NULL)
231 : 0 : *release = release_string;
232 : : }
233 : :
234 : : return 0;
235 : : }
236 : :
237 : : static int
238 : 0 : report_kernel (Dwfl *dwfl, const char **release,
239 : : int (*predicate) (const char *module, const char *file))
240 : : {
241 : 0 : int result = get_release (dwfl, release);
242 [ # # ]: 0 : if (unlikely (result != 0))
243 : : return result;
244 : :
245 [ # # # # ]: 0 : if (release == NULL || *release == NULL)
246 : : return EINVAL;
247 : :
248 : 0 : char *fname;
249 : 0 : int fd = find_kernel_elf (dwfl, *release, &fname);
250 : :
251 [ # # ]: 0 : if (fd < 0)
252 [ # # ]: 0 : result = ((predicate != NULL && !(*predicate) (KERNEL_MODNAME, NULL))
253 [ # # # # ]: 0 : ? 0 : errno ?: ENOENT);
254 : : else
255 : : {
256 : 0 : bool report = true;
257 : :
258 [ # # ]: 0 : if (predicate != NULL)
259 : : {
260 : : /* Let the predicate decide whether to use this one. */
261 : 0 : int want = (*predicate) (KERNEL_MODNAME, fname);
262 [ # # ]: 0 : if (want < 0)
263 : 0 : result = errno;
264 : 0 : report = want > 0;
265 : : }
266 : :
267 [ # # ]: 0 : if (report)
268 : : {
269 : : /* Note that on some architectures (e.g. x86_64) the vmlinux
270 : : is ET_EXEC, while on others (e.g. ppc64) it is ET_DYN.
271 : : In both cases the phdr p_vaddr load address will be non-zero.
272 : : We want the image to be placed as if it was ET_DYN, so
273 : : pass true for add_p_vaddr which will do the right thing
274 : : (in combination with a zero base) in either case. */
275 : 0 : Dwfl_Module *mod = INTUSE(dwfl_report_elf) (dwfl, KERNEL_MODNAME,
276 : : fname, fd, 0, true);
277 [ # # ]: 0 : if (mod == NULL)
278 : : result = -1;
279 : : else
280 : : /* The kernel is ET_EXEC, but always treat it as relocatable. */
281 : 0 : mod->e_type = ET_DYN;
282 : : }
283 : :
284 : 0 : free (fname);
285 : :
286 [ # # ]: 0 : if (!report || result < 0)
287 : 0 : close (fd);
288 : : }
289 : :
290 : : return result;
291 : : }
292 : :
293 : : /* Look for a kernel debug archive. If we find one, report all its modules.
294 : : If not, return ENOENT. */
295 : : static int
296 : 0 : report_kernel_archive (Dwfl *dwfl, const char **release,
297 : : int (*predicate) (const char *module, const char *file))
298 : : {
299 : 0 : int result = get_release (dwfl, release);
300 [ # # ]: 0 : if (unlikely (result != 0))
301 : : return result;
302 : :
303 [ # # # # ]: 0 : if (release == NULL || *release == NULL)
304 : : return EINVAL;
305 : :
306 : 0 : char *archive;
307 : 0 : int res = (((*release)[0] == '/')
308 : 0 : ? asprintf (&archive, "%s/debug.a", *release)
309 [ # # ]: 0 : : asprintf (&archive, MODULEDIRFMT "/debug.a", *release));
310 [ # # ]: 0 : if (unlikely (res < 0))
311 : : return ENOMEM;
312 : :
313 : 0 : int fd = try_kernel_name (dwfl, &archive, false);
314 [ # # ]: 0 : if (fd < 0)
315 [ # # ]: 0 : result = errno ?: ENOENT;
316 : : else
317 : : {
318 : : /* We have the archive file open! */
319 : 0 : Dwfl_Module *last = __libdwfl_report_offline (dwfl, NULL, archive, fd,
320 : : true, predicate);
321 [ # # ]: 0 : if (unlikely (last == NULL))
322 : : result = -1;
323 : : else
324 : : {
325 : : /* Find the kernel and move it to the head of the list. */
326 : 0 : Dwfl_Module **tailp = &dwfl->modulelist, **prevp = tailp;
327 [ # # ]: 0 : for (Dwfl_Module *m = *prevp; m != NULL; m = *(prevp = &m->next))
328 [ # # # # : 0 : if (!m->gc && m->e_type != ET_REL && !strcmp (m->name, "kernel"))
# # ]
329 : : {
330 : 0 : *prevp = m->next;
331 : 0 : m->next = *tailp;
332 : 0 : *tailp = m;
333 : 0 : break;
334 : : }
335 : : }
336 : : }
337 : :
338 : 0 : free (archive);
339 : 0 : return result;
340 : : }
341 : :
342 : : static size_t
343 : 0 : check_suffix (const FTSENT *f, size_t namelen)
344 : : {
345 : : #define TRY(sfx) \
346 : : if ((namelen ? f->fts_namelen == namelen + sizeof sfx - 1 \
347 : : : f->fts_namelen >= sizeof sfx) \
348 : : && !memcmp (f->fts_name + f->fts_namelen - (sizeof sfx - 1), \
349 : : sfx, sizeof sfx)) \
350 : : return sizeof sfx - 1
351 : :
352 [ # # # # : 0 : TRY (".ko");
# # ]
353 [ # # # # : 0 : TRY (".ko.gz");
# # ]
354 : : #if USE_BZLIB
355 [ # # # # : 0 : TRY (".ko.bz2");
# # ]
356 : : #endif
357 : : #if USE_LZMA
358 [ # # # # ]: 0 : TRY (".ko.xz");
359 : : #endif
360 : : #if USE_ZSTD
361 [ # # # # ]: 0 : TRY (".ko.zst");
362 : : #endif
363 : :
364 : : return 0;
365 : :
366 : : #undef TRY
367 : : }
368 : :
369 : : /* Report a kernel and all its modules found on disk, for offline use.
370 : : If RELEASE starts with '/', it names a directory to look in;
371 : : if not, it names a directory to find under /lib/modules/;
372 : : if null, /lib/modules/`uname -r` is used.
373 : : Returns zero on success, -1 if dwfl_report_module failed,
374 : : or an errno code if finding the files on disk failed. */
375 : : int
376 : 0 : dwfl_linux_kernel_report_offline (Dwfl *dwfl, const char *release,
377 : : int (*predicate) (const char *module,
378 : : const char *file))
379 : : {
380 : 0 : int result = report_kernel_archive (dwfl, &release, predicate);
381 [ # # ]: 0 : if (result != ENOENT)
382 : : return result;
383 : :
384 : : /* First report the kernel. */
385 : 0 : result = report_kernel (dwfl, &release, predicate);
386 [ # # ]: 0 : if (result == 0)
387 : : {
388 : : /* Do "find /lib/modules/RELEASE -name *.ko". */
389 : :
390 : 0 : char *modulesdir[] = { NULL, NULL };
391 [ # # ]: 0 : if (release[0] == '/')
392 : 0 : modulesdir[0] = (char *) release;
393 : : else
394 : : {
395 [ # # ]: 0 : if (asprintf (&modulesdir[0], MODULEDIRFMT, release) < 0)
396 : 0 : return errno;
397 : : }
398 : :
399 : 0 : FTS *fts = fts_open (modulesdir, FTS_NOSTAT | FTS_LOGICAL, NULL);
400 [ # # ]: 0 : if (modulesdir[0] == (char *) release)
401 : 0 : modulesdir[0] = NULL;
402 [ # # ]: 0 : if (fts == NULL)
403 : : {
404 : 0 : free (modulesdir[0]);
405 : 0 : return errno;
406 : : }
407 : :
408 : : FTSENT *f;
409 [ # # ]: 0 : while ((f = fts_read (fts)) != NULL)
410 : : {
411 : : /* Skip a "source" subtree, which tends to be large.
412 : : This insane hard-coding of names is what depmod does too. */
413 [ # # ]: 0 : if (f->fts_namelen == sizeof "source" - 1
414 [ # # ]: 0 : && !strcmp (f->fts_name, "source"))
415 : : {
416 : 0 : fts_set (fts, f, FTS_SKIP);
417 : 0 : continue;
418 : : }
419 : :
420 [ # # # ]: 0 : switch (f->fts_info)
421 : 0 : {
422 : 0 : case FTS_F:
423 : : case FTS_SL:
424 : 0 : case FTS_NSOK:;
425 : : /* See if this file name matches "*.ko". */
426 : 0 : const size_t suffix = check_suffix (f, 0);
427 [ # # ]: 0 : if (suffix)
428 : : {
429 : : /* We have a .ko file to report. Following the algorithm
430 : : by which the kernel makefiles set KBUILD_MODNAME, we
431 : : replace all ',' or '-' with '_' in the file name and
432 : : call that the module name. Modules could well be
433 : : built using different embedded names than their file
434 : : names. To handle that, we would have to look at the
435 : : __this_module.name contents in the module's text. */
436 : :
437 : 0 : char *name = strndup (f->fts_name, f->fts_namelen - suffix);
438 [ # # ]: 0 : if (unlikely (name == NULL))
439 : : {
440 : 0 : __libdwfl_seterrno (DWFL_E_NOMEM);
441 : 0 : result = -1;
442 : 0 : break;
443 : : }
444 [ # # ]: 0 : for (size_t i = 0; i < f->fts_namelen - suffix; ++i)
445 [ # # ]: 0 : if (name[i] == '-' || name[i] == ',')
446 : 0 : name[i] = '_';
447 : :
448 [ # # ]: 0 : if (predicate != NULL)
449 : : {
450 : : /* Let the predicate decide whether to use this one. */
451 : 0 : int want = (*predicate) (name, f->fts_path);
452 [ # # ]: 0 : if (want < 0)
453 : : {
454 : 0 : result = -1;
455 : 0 : free (name);
456 : 0 : break;
457 : : }
458 [ # # ]: 0 : if (!want)
459 : : {
460 : 0 : free (name);
461 : 0 : continue;
462 : : }
463 : : }
464 : :
465 [ # # ]: 0 : if (dwfl_report_offline (dwfl, name, f->fts_path, -1) == NULL)
466 : : {
467 : 0 : free (name);
468 : 0 : result = -1;
469 : 0 : break;
470 : : }
471 : 0 : free (name);
472 : : }
473 : 0 : continue;
474 : :
475 : 0 : case FTS_ERR:
476 : : case FTS_DNR:
477 : : case FTS_NS:
478 : 0 : result = f->fts_errno;
479 : 0 : break;
480 : :
481 : 0 : case FTS_SLNONE:
482 : : default:
483 : 0 : continue;
484 : : }
485 : :
486 : : /* We only get here in error cases. */
487 : : break;
488 : : }
489 : 0 : fts_close (fts);
490 : 0 : free (modulesdir[0]);
491 : : }
492 : :
493 : : return result;
494 : : }
495 : : INTDEF (dwfl_linux_kernel_report_offline)
496 : :
497 : :
498 : : /* State of read_address used by intuit_kernel_bounds. */
499 : : struct read_address_state {
500 : : FILE *f;
501 : : char *line;
502 : : size_t linesz;
503 : : size_t n;
504 : : char *p;
505 : : const char *type;
506 : : };
507 : :
508 : : static inline bool
509 : 0 : read_address (struct read_address_state *state, Dwarf_Addr *addr)
510 : : {
511 [ # # ]: 0 : if ((state->n = getline (&state->line, &state->linesz, state->f)) < 1 ||
512 [ # # ]: 0 : state->line[state->n - 2] == ']')
513 : : return false;
514 : 0 : *addr = strtoull (state->line, &state->p, 16);
515 : 0 : state->p += strspn (state->p, " \t");
516 : 0 : state->type = strsep (&state->p, " \t\n");
517 [ # # ]: 0 : if (state->type == NULL)
518 : : return false;
519 [ # # # # ]: 0 : return state->p != NULL && state->p != state->line;
520 : : }
521 : :
522 : :
523 : : /* Grovel around to guess the bounds of the runtime kernel image. */
524 : : static int
525 : 0 : intuit_kernel_bounds (Dwarf_Addr *start, Dwarf_Addr *end, Dwarf_Addr *notes)
526 : : {
527 : 0 : struct read_address_state state = { NULL, NULL, 0, 0, NULL, NULL };
528 : :
529 : 0 : *notes = 0;
530 : :
531 : 0 : state.f = fopen (KSYMSFILE, "r");
532 [ # # ]: 0 : if (state.f == NULL)
533 : 0 : return errno;
534 : :
535 : 0 : (void) __fsetlocking (state.f, FSETLOCKING_BYCALLER);
536 : :
537 : 0 : int result;
538 : 0 : do
539 [ # # ]: 0 : result = read_address (&state, start) ? 0 : -1;
540 [ # # ]: 0 : while (result == 0 && strchr ("TtRr", *state.type) == NULL);
541 : :
542 [ # # ]: 0 : if (result == 0)
543 : : {
544 : 0 : Dwarf_Addr addr;
545 : 0 : *end = *start;
546 [ # # # # ]: 0 : while (read_address (&state, &addr) && addr >= *end)
547 : : {
548 : 0 : *end = addr;
549 [ # # # # ]: 0 : if (*notes == 0 && !strcmp (state.p, "__start_notes\n"))
550 : 0 : *notes = *end;
551 : : }
552 : :
553 : 0 : Dwarf_Addr round_kernel = sysconf (_SC_PAGESIZE);
554 : 0 : *start &= -(Dwarf_Addr) round_kernel;
555 : 0 : *end += round_kernel - 1;
556 : 0 : *end &= -(Dwarf_Addr) round_kernel;
557 [ # # # # ]: 0 : if (*start >= *end || *end - *start < round_kernel)
558 : 0 : result = -1;
559 : : }
560 : 0 : free (state.line);
561 : :
562 [ # # ]: 0 : if (result == -1)
563 [ # # ]: 0 : result = ferror_unlocked (state.f) ? errno : ENOEXEC;
564 : :
565 : 0 : fclose (state.f);
566 : :
567 : 0 : return result;
568 : : }
569 : :
570 : :
571 : : /* Look for a build ID note in NOTESFILE and associate the ID with MOD. */
572 : : static int
573 : 0 : check_notes (Dwfl_Module *mod, const char *notesfile,
574 : : Dwarf_Addr vaddr, const char *secname)
575 : : {
576 : 0 : int fd = open (notesfile, O_RDONLY);
577 [ # # ]: 0 : if (fd < 0)
578 : : return 1;
579 : :
580 : 0 : assert (sizeof (Elf32_Nhdr) == sizeof (GElf_Nhdr));
581 : 0 : assert (sizeof (Elf64_Nhdr) == sizeof (GElf_Nhdr));
582 : 0 : union
583 : : {
584 : : GElf_Nhdr nhdr;
585 : : unsigned char data[8192];
586 : : } buf;
587 : :
588 : 0 : ssize_t n = read (fd, buf.data, sizeof buf);
589 : 0 : close (fd);
590 : :
591 [ # # ]: 0 : if (n <= 0)
592 : : return 1;
593 : :
594 : : unsigned char *p = buf.data;
595 : : size_t len = 0;
596 [ # # ]: 0 : while (p < &buf.data[n])
597 : : {
598 : : /* No translation required since we are reading the native kernel. */
599 : 0 : GElf_Nhdr *nhdr = (void *) p;
600 : 0 : len += sizeof *nhdr;
601 : 0 : p += len;
602 : 0 : unsigned char *name = p;
603 : 0 : unsigned char *bits;
604 : : /* This is somewhat ugly, GNU Property notes use different padding,
605 : : but all we have is the file content, so we have to actually check
606 : : the name and type. */
607 [ # # ]: 0 : if (nhdr->n_type == NT_GNU_PROPERTY_TYPE_0
608 [ # # ]: 0 : && nhdr->n_namesz == sizeof "GNU"
609 [ # # ]: 0 : && name + nhdr->n_namesz < &buf.data[n]
610 [ # # ]: 0 : && !memcmp (name, "GNU", sizeof "GNU"))
611 : : {
612 : 0 : len += nhdr->n_namesz;
613 : 0 : len = NOTE_ALIGN8 (len);
614 : 0 : p = buf.data + len;
615 : 0 : bits = p;
616 : 0 : len += nhdr->n_descsz;
617 : 0 : len = NOTE_ALIGN8 (len);
618 : 0 : p = buf.data + len;
619 : : }
620 : : else
621 : : {
622 : 0 : len += nhdr->n_namesz;
623 : 0 : len = NOTE_ALIGN4 (len);
624 : 0 : p = buf.data + len;
625 : 0 : bits = p;
626 : 0 : len += nhdr->n_descsz;
627 : 0 : len = NOTE_ALIGN4 (len);
628 : 0 : p = buf.data + len;
629 : : }
630 : :
631 [ # # ]: 0 : if (p <= &buf.data[n]
632 [ # # ]: 0 : && nhdr->n_type == NT_GNU_BUILD_ID
633 [ # # ]: 0 : && nhdr->n_namesz == sizeof "GNU"
634 [ # # ]: 0 : && !memcmp (name, "GNU", sizeof "GNU"))
635 : : {
636 : : /* Found it. For a module we must figure out its VADDR now. */
637 : :
638 [ # # ]: 0 : if (secname != NULL
639 [ # # ]: 0 : && (INTUSE(dwfl_linux_kernel_module_section_address)
640 : 0 : (mod, NULL, mod->name, 0, secname, 0, NULL, &vaddr) != 0
641 [ # # ]: 0 : || vaddr == (GElf_Addr) -1l))
642 : 0 : vaddr = 0;
643 : :
644 [ # # ]: 0 : if (vaddr != 0)
645 : 0 : vaddr += bits - buf.data;
646 : 0 : return INTUSE(dwfl_module_report_build_id) (mod, bits,
647 : 0 : nhdr->n_descsz, vaddr);
648 : : }
649 : : }
650 : :
651 : : return 0;
652 : : }
653 : :
654 : : /* Look for a build ID for the kernel. */
655 : : static int
656 : 0 : check_kernel_notes (Dwfl_Module *kernelmod, GElf_Addr vaddr)
657 : : {
658 [ # # ]: 0 : return check_notes (kernelmod, KNOTESFILE, vaddr, NULL) < 0 ? -1 : 0;
659 : : }
660 : :
661 : : /* Look for a build ID for a loaded kernel module. */
662 : : static int
663 : 0 : check_module_notes (Dwfl_Module *mod)
664 : : {
665 : 0 : char *dirs[2] = { NULL, NULL };
666 [ # # ]: 0 : if (asprintf (&dirs[0], MODNOTESFMT, mod->name) < 0)
667 : : return ENOMEM;
668 : :
669 : 0 : FTS *fts = fts_open (dirs, FTS_NOSTAT | FTS_LOGICAL, NULL);
670 [ # # ]: 0 : if (fts == NULL)
671 : : {
672 : 0 : free (dirs[0]);
673 : 0 : return 0;
674 : : }
675 : :
676 : : int result = 0;
677 : : FTSENT *f;
678 [ # # ]: 0 : while ((f = fts_read (fts)) != NULL)
679 : : {
680 [ # # # ]: 0 : switch (f->fts_info)
681 : : {
682 : 0 : case FTS_F:
683 : : case FTS_SL:
684 : : case FTS_NSOK:
685 : 0 : result = check_notes (mod, f->fts_accpath, 0, f->fts_name);
686 [ # # ]: 0 : if (result > 0) /* Nothing found. */
687 : : {
688 : 0 : result = 0;
689 : 0 : continue;
690 : : }
691 : : break;
692 : :
693 : 0 : case FTS_ERR:
694 : : case FTS_DNR:
695 : 0 : result = f->fts_errno;
696 : 0 : break;
697 : :
698 : 0 : case FTS_NS:
699 : : case FTS_SLNONE:
700 : : default:
701 : 0 : continue;
702 : : }
703 : :
704 : : /* We only get here when finished or in error cases. */
705 : : break;
706 : : }
707 : 0 : fts_close (fts);
708 : 0 : free (dirs[0]);
709 : :
710 : 0 : return result;
711 : : }
712 : :
713 : : int
714 : 0 : dwfl_linux_kernel_report_kernel (Dwfl *dwfl)
715 : : {
716 : 0 : Dwarf_Addr start = 0;
717 : 0 : Dwarf_Addr end = 0;
718 : :
719 : : #define report() \
720 : : (INTUSE(dwfl_report_module) (dwfl, KERNEL_MODNAME, start, end))
721 : :
722 : : /* This is a bit of a kludge. If we already reported the kernel,
723 : : don't bother figuring it out again--it never changes. */
724 [ # # ]: 0 : for (Dwfl_Module *m = dwfl->modulelist; m != NULL; m = m->next)
725 [ # # ]: 0 : if (!strcmp (m->name, KERNEL_MODNAME))
726 : : {
727 : 0 : start = m->low_addr;
728 : 0 : end = m->high_addr;
729 [ # # ]: 0 : return report () == NULL ? -1 : 0;
730 : : }
731 : :
732 : : /* Try to figure out the bounds of the kernel image without
733 : : looking for any vmlinux file. */
734 : 0 : Dwarf_Addr notes;
735 : 0 : int result = intuit_kernel_bounds (&start, &end, ¬es);
736 [ # # ]: 0 : if (result == 0)
737 : : {
738 : 0 : Dwfl_Module *mod = report ();
739 [ # # ]: 0 : return unlikely (mod == NULL) ? -1 : check_kernel_notes (mod, notes);
740 : : }
741 [ # # ]: 0 : if (result != ENOENT)
742 : : return result;
743 : :
744 : : /* Find the ELF file for the running kernel and dwfl_report_elf it. */
745 : 0 : return report_kernel (dwfl, NULL, NULL);
746 : : }
747 : : INTDEF (dwfl_linux_kernel_report_kernel)
748 : :
749 : :
750 : : static inline bool
751 : 0 : subst_name (char from, char to,
752 : : const char * const module_name,
753 : : char * const alternate_name,
754 : : const size_t namelen)
755 : : {
756 : 0 : const char *n = memchr (module_name, from, namelen);
757 [ # # ]: 0 : if (n == NULL)
758 : : return false;
759 : 0 : char *a = mempcpy (alternate_name, module_name, n - module_name);
760 : 0 : *a++ = to;
761 : 0 : ++n;
762 : 0 : const char *p;
763 [ # # ]: 0 : while ((p = memchr (n, from, namelen - (n - module_name))) != NULL)
764 : : {
765 : 0 : a = mempcpy (a, n, p - n);
766 : 0 : *a++ = to;
767 : 0 : n = p + 1;
768 : : }
769 : 0 : memcpy (a, n, namelen - (n - module_name) + 1);
770 : 0 : return true;
771 : : }
772 : :
773 : : /* Dwfl_Callbacks.find_elf for the running Linux kernel and its modules. */
774 : :
775 : : int
776 : 0 : dwfl_linux_kernel_find_elf (Dwfl_Module *mod,
777 : : void **userdata __attribute__ ((unused)),
778 : : const char *module_name,
779 : : Dwarf_Addr base __attribute__ ((unused)),
780 : : char **file_name, Elf **elfp)
781 : : {
782 [ # # ]: 0 : if (mod->build_id_len > 0)
783 : : {
784 : 0 : int fd = INTUSE(dwfl_build_id_find_elf) (mod, NULL, NULL, 0,
785 : : file_name, elfp);
786 [ # # # # : 0 : if (fd >= 0 || mod->main.elf != NULL || errno != 0)
# # ]
787 : : return fd;
788 : : }
789 : :
790 : 0 : const char *release = kernel_release ();
791 [ # # ]: 0 : if (release == NULL)
792 : 0 : return errno;
793 : :
794 [ # # ]: 0 : if (!strcmp (module_name, KERNEL_MODNAME))
795 : 0 : return find_kernel_elf (mod->dwfl, release, file_name);
796 : :
797 : : /* Do "find /lib/modules/`uname -r` -name MODULE_NAME.ko". */
798 : :
799 : 0 : char *modulesdir[] = { NULL, NULL };
800 [ # # ]: 0 : if (asprintf (&modulesdir[0], MODULEDIRFMT, release) < 0)
801 : : return -1;
802 : :
803 : 0 : FTS *fts = fts_open (modulesdir, FTS_NOSTAT | FTS_LOGICAL, NULL);
804 [ # # ]: 0 : if (fts == NULL)
805 : : {
806 : 0 : free (modulesdir[0]);
807 : 0 : return -1;
808 : : }
809 : :
810 : 0 : size_t namelen = strlen (module_name);
811 : :
812 : : /* This is a kludge. There is no actual necessary relationship between
813 : : the name of the .ko file installed and the module name the kernel
814 : : knows it by when it's loaded. The kernel's only idea of the module
815 : : name comes from the name embedded in the object's magic
816 : : .gnu.linkonce.this_module section.
817 : :
818 : : In practice, these module names match the .ko file names except for
819 : : some using '_' and some using '-'. So our cheap kludge is to look for
820 : : two files when either a '_' or '-' appears in a module name, one using
821 : : only '_' and one only using '-'. */
822 : :
823 : 0 : char *alternate_name = malloc (namelen + 1);
824 [ # # ]: 0 : if (unlikely (alternate_name == NULL))
825 : : {
826 : 0 : free (modulesdir[0]);
827 : 0 : return ENOMEM;
828 : : }
829 [ # # # # ]: 0 : if (!subst_name ('-', '_', module_name, alternate_name, namelen) &&
830 : 0 : !subst_name ('_', '-', module_name, alternate_name, namelen))
831 : 0 : alternate_name[0] = '\0';
832 : :
833 : : FTSENT *f;
834 : : int error = ENOENT;
835 [ # # ]: 0 : while ((f = fts_read (fts)) != NULL)
836 : : {
837 : : /* Skip a "source" subtree, which tends to be large.
838 : : This insane hard-coding of names is what depmod does too. */
839 [ # # ]: 0 : if (f->fts_namelen == sizeof "source" - 1
840 [ # # ]: 0 : && !strcmp (f->fts_name, "source"))
841 : : {
842 : 0 : fts_set (fts, f, FTS_SKIP);
843 : 0 : continue;
844 : : }
845 : :
846 : 0 : error = ENOENT;
847 [ # # # ]: 0 : switch (f->fts_info)
848 : : {
849 : 0 : case FTS_F:
850 : : case FTS_SL:
851 : : case FTS_NSOK:
852 : : /* See if this file name is "MODULE_NAME.ko". */
853 [ # # ]: 0 : if (check_suffix (f, namelen)
854 [ # # ]: 0 : && (!memcmp (f->fts_name, module_name, namelen)
855 [ # # ]: 0 : || !memcmp (f->fts_name, alternate_name, namelen)))
856 : : {
857 : 0 : int fd = open (f->fts_accpath, O_RDONLY);
858 : 0 : *file_name = strdup (f->fts_path);
859 : 0 : fts_close (fts);
860 : 0 : free (modulesdir[0]);
861 : 0 : free (alternate_name);
862 [ # # ]: 0 : if (fd < 0)
863 : 0 : free (*file_name);
864 [ # # ]: 0 : else if (*file_name == NULL)
865 : : {
866 : 0 : close (fd);
867 : 0 : fd = -1;
868 : : }
869 : 0 : return fd;
870 : : }
871 : : break;
872 : :
873 : 0 : case FTS_ERR:
874 : : case FTS_DNR:
875 : : case FTS_NS:
876 : 0 : error = f->fts_errno;
877 : 0 : break;
878 : :
879 : : case FTS_SLNONE:
880 : : default:
881 : : break;
882 : : }
883 : : }
884 : :
885 : 0 : fts_close (fts);
886 : 0 : free (modulesdir[0]);
887 : 0 : free (alternate_name);
888 : 0 : errno = error;
889 : 0 : return -1;
890 : : }
891 : : INTDEF (dwfl_linux_kernel_find_elf)
892 : :
893 : :
894 : : /* Dwfl_Callbacks.section_address for kernel modules in the running Linux.
895 : : We read the information from /sys/module directly. */
896 : :
897 : : int
898 : 0 : dwfl_linux_kernel_module_section_address
899 : : (Dwfl_Module *mod __attribute__ ((unused)),
900 : : void **userdata __attribute__ ((unused)),
901 : : const char *modname, Dwarf_Addr base __attribute__ ((unused)),
902 : : const char *secname, Elf32_Word shndx __attribute__ ((unused)),
903 : : const GElf_Shdr *shdr __attribute__ ((unused)),
904 : : Dwarf_Addr *addr)
905 : : {
906 : 0 : char *sysfile;
907 [ # # ]: 0 : if (asprintf (&sysfile, SECADDRDIRFMT "%s", modname, secname) < 0)
908 : : return DWARF_CB_ABORT;
909 : :
910 : 0 : FILE *f = fopen (sysfile, "r");
911 : 0 : free (sysfile);
912 : :
913 [ # # ]: 0 : if (f == NULL)
914 : : {
915 [ # # ]: 0 : if (errno == ENOENT)
916 : : {
917 : : /* The .modinfo and .data.percpu sections are never kept
918 : : loaded in the kernel. If the kernel was compiled without
919 : : CONFIG_MODULE_UNLOAD, the .exit.* sections are not
920 : : actually loaded at all.
921 : :
922 : : Setting *ADDR to -1 tells the caller this section is
923 : : actually absent from memory. */
924 : :
925 [ # # ]: 0 : if (!strcmp (secname, ".modinfo")
926 [ # # ]: 0 : || !strcmp (secname, ".data.percpu")
927 [ # # ]: 0 : || startswith (secname, ".exit"))
928 : : {
929 : 0 : *addr = (Dwarf_Addr) -1l;
930 : 0 : return DWARF_CB_OK;
931 : : }
932 : :
933 : : /* The goofy PPC64 module_frob_arch_sections function tweaks
934 : : the section names as a way to control other kernel code's
935 : : behavior, and this cruft leaks out into the /sys information.
936 : : The file name for ".init*" may actually look like "_init*". */
937 : :
938 : 0 : const bool is_init = startswith (secname, ".init");
939 [ # # ]: 0 : if (is_init)
940 : : {
941 [ # # ]: 0 : if (asprintf (&sysfile, SECADDRDIRFMT "_%s",
942 : : modname, &secname[1]) < 0)
943 : : return ENOMEM;
944 : 0 : f = fopen (sysfile, "r");
945 : 0 : free (sysfile);
946 [ # # ]: 0 : if (f != NULL)
947 : 0 : goto ok;
948 : : }
949 : :
950 : : /* The kernel truncates section names to MODULE_SECT_NAME_LEN - 1.
951 : : In case that size increases in the future, look for longer
952 : : truncated names first. */
953 : 0 : size_t namelen = strlen (secname);
954 [ # # ]: 0 : if (namelen >= MODULE_SECT_NAME_LEN)
955 : : {
956 : 0 : int len = asprintf (&sysfile, SECADDRDIRFMT "%s",
957 : : modname, secname);
958 [ # # ]: 0 : if (len < 0)
959 : : return DWARF_CB_ABORT;
960 : 0 : char *end = sysfile + len;
961 : 0 : do
962 : : {
963 : 0 : *--end = '\0';
964 : 0 : f = fopen (sysfile, "r");
965 [ # # # # ]: 0 : if (is_init && f == NULL && errno == ENOENT)
966 : : {
967 : 0 : sysfile[len - namelen] = '_';
968 : 0 : f = fopen (sysfile, "r");
969 : 0 : sysfile[len - namelen] = '.';
970 : : }
971 : : }
972 [ # # ]: 0 : while (f == NULL && errno == ENOENT
973 [ # # # # ]: 0 : && end - &sysfile[len - namelen] >= MODULE_SECT_NAME_LEN);
974 : 0 : free (sysfile);
975 : :
976 [ # # ]: 0 : if (f != NULL)
977 : 0 : goto ok;
978 : : }
979 : : }
980 : :
981 : 0 : return DWARF_CB_ABORT;
982 : : }
983 : :
984 : 0 : ok:
985 : 0 : (void) __fsetlocking (f, FSETLOCKING_BYCALLER);
986 : :
987 : 0 : int result = (fscanf (f, "%" PRIx64 "\n", addr) == 1 ? 0
988 [ # # # # ]: 0 : : ferror_unlocked (f) ? errno : ENOEXEC);
989 : 0 : fclose (f);
990 : :
991 [ # # ]: 0 : if (result == 0)
992 : : return DWARF_CB_OK;
993 : :
994 : 0 : errno = result;
995 : 0 : return DWARF_CB_ABORT;
996 : : }
997 : : INTDEF (dwfl_linux_kernel_module_section_address)
998 : :
999 : : int
1000 : 0 : dwfl_linux_kernel_report_modules (Dwfl *dwfl)
1001 : : {
1002 : 0 : FILE *f = fopen (MODULELIST, "r");
1003 [ # # ]: 0 : if (f == NULL)
1004 : 0 : return errno;
1005 : :
1006 : 0 : (void) __fsetlocking (f, FSETLOCKING_BYCALLER);
1007 : :
1008 : 0 : int result = 0;
1009 : 0 : Dwarf_Addr modaddr;
1010 : 0 : unsigned long int modsz;
1011 : 0 : char modname[128+1];
1012 : 0 : char *line = NULL;
1013 : 0 : size_t linesz = 0;
1014 : : /* We can't just use fscanf here because it's not easy to distinguish \n
1015 : : from other whitespace so as to take the optional word following the
1016 : : address but always stop at the end of the line. */
1017 : 0 : while (getline (&line, &linesz, f) > 0
1018 [ # # # # ]: 0 : && sscanf (line, "%128s %lu %*s %*s %*s %" PRIx64 " %*s\n",
1019 : : modname, &modsz, &modaddr) == 3)
1020 : : {
1021 : 0 : Dwfl_Module *mod = INTUSE(dwfl_report_module) (dwfl, modname,
1022 : : modaddr, modaddr + modsz);
1023 [ # # ]: 0 : if (mod == NULL)
1024 : : {
1025 : : result = -1;
1026 : : break;
1027 : : }
1028 : :
1029 : 0 : result = check_module_notes (mod);
1030 : : }
1031 : 0 : free (line);
1032 : :
1033 [ # # ]: 0 : if (result == 0)
1034 [ # # # # ]: 0 : result = ferror_unlocked (f) ? errno : feof_unlocked (f) ? 0 : ENOEXEC;
1035 : :
1036 : 0 : fclose (f);
1037 : :
1038 : 0 : return result;
1039 : : }
1040 : : INTDEF (dwfl_linux_kernel_report_modules)
|