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