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