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