linux-kernel-modules.c revision b597dfad924980dede10d7c19d87900b6172e599
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    /* We have the archive file open!  */
230    result = __libdwfl_report_offline (dwfl, NULL, archive, fd, true,
231				       predicate) == NULL ? -1 : 0;
232
233  free (archive);
234  return result;
235}
236
237/* Report a kernel and all its modules found on disk, for offline use.
238   If RELEASE starts with '/', it names a directory to look in;
239   if not, it names a directory to find under /lib/modules/;
240   if null, /lib/modules/`uname -r` is used.
241   Returns zero on success, -1 if dwfl_report_module failed,
242   or an errno code if finding the files on disk failed.  */
243int
244dwfl_linux_kernel_report_offline (Dwfl *dwfl, const char *release,
245				  int (*predicate) (const char *module,
246						    const char *file))
247{
248  int result = report_kernel_archive (dwfl, &release, predicate);
249  if (result != ENOENT)
250    return result;
251
252  /* First report the kernel.  */
253  result = report_kernel (dwfl, &release, predicate);
254  if (result == 0)
255    {
256      /* Do "find /lib/modules/RELEASE -name *.ko".  */
257
258      char *modulesdir[] = { NULL, NULL };
259      if (release[0] == '/')
260	modulesdir[0] = (char *) release;
261      else
262	{
263	  if (asprintf (&modulesdir[0], MODULEDIRFMT, release) < 0)
264	    return errno;
265	}
266
267      FTS *fts = fts_open (modulesdir, FTS_NOSTAT, NULL);
268      if (modulesdir[0] == (char *) release)
269	modulesdir[0] = NULL;
270      if (fts == NULL)
271	{
272	  free (modulesdir[0]);
273	  return errno;
274	}
275
276      FTSENT *f;
277      while ((f = fts_read (fts)) != NULL)
278	{
279	  switch (f->fts_info)
280	    {
281	    case FTS_F:
282	    case FTS_NSOK:
283	      /* See if this file name matches "*.ko".  */
284	      if (f->fts_namelen > 3
285		  && !memcmp (f->fts_name + f->fts_namelen - 3, ".ko", 4))
286		{
287		  /* We have a .ko file to report.  Following the algorithm
288		     by which the kernel makefiles set KBUILD_MODNAME, we
289		     replace all ',' or '-' with '_' in the file name and
290		     call that the module name.  Modules could well be
291		     built using different embedded names than their file
292		     names.  To handle that, we would have to look at the
293		     __this_module.name contents in the module's text.  */
294
295		  char name[f->fts_namelen - 3 + 1];
296		  for (size_t i = 0; i < f->fts_namelen - 3U; ++i)
297		    if (f->fts_name[i] == '-' || f->fts_name[i] == ',')
298		      name[i] = '_';
299		    else
300		      name[i] = f->fts_name[i];
301		  name[f->fts_namelen - 3] = '\0';
302
303		  if (predicate != NULL)
304		    {
305		      /* Let the predicate decide whether to use this one.  */
306		      int want = (*predicate) (name, f->fts_path);
307		      if (want < 0)
308			{
309			  result = -1;
310			  break;
311			}
312		      if (!want)
313			continue;
314		    }
315
316		  if (dwfl_report_offline (dwfl, name,
317					   f->fts_path, -1) == NULL)
318		    {
319		      result = -1;
320		      break;
321		    }
322		}
323	      continue;
324
325	    case FTS_ERR:
326	    case FTS_DNR:
327	    case FTS_NS:
328	      result = f->fts_errno;
329	      break;
330
331	    default:
332	      continue;
333	    }
334
335	  /* We only get here in error cases.  */
336	  break;
337	}
338      fts_close (fts);
339      free (modulesdir[0]);
340    }
341
342  return result;
343}
344INTDEF (dwfl_linux_kernel_report_offline)
345
346
347/* Grovel around to guess the bounds of the runtime kernel image.  */
348static int
349intuit_kernel_bounds (Dwarf_Addr *start, Dwarf_Addr *end, Dwarf_Addr *notes)
350{
351  FILE *f = fopen (KSYMSFILE, "r");
352  if (f == NULL)
353    return errno;
354
355  (void) __fsetlocking (f, FSETLOCKING_BYCALLER);
356
357  *notes = 0;
358
359  char *line = NULL;
360  size_t linesz = 0;
361  size_t n = getline (&line, &linesz, f);
362  Dwarf_Addr first;
363  char *p = NULL;
364  int result = 0;
365  if (n > 0 && (first = strtoull (line, &p, 16)) > 0 && p > line)
366    {
367      Dwarf_Addr last = 0;
368      while ((n = getline (&line, &linesz, f)) > 1 && line[n - 2] != ']')
369	{
370	  p = NULL;
371	  last = strtoull (line, &p, 16);
372	  if (p == NULL || p == line || last == 0)
373	    {
374	      result = -1;
375	      break;
376	    }
377
378	  if (*notes == 0)
379	    {
380	      const char *sym = (strsep (&p, " \t\n")
381				 ? strsep (&p, " \t\n") : NULL);
382	      if (sym != NULL && !strcmp (sym, "__start_notes"))
383		*notes = last;
384	    }
385	}
386      if ((n == 0 && feof_unlocked (f)) || (n > 1 && line[n - 2] == ']'))
387	{
388	  Dwarf_Addr round_kernel = sysconf (_SC_PAGE_SIZE);
389	  first &= -(Dwarf_Addr) round_kernel;
390	  last += round_kernel - 1;
391	  last &= -(Dwarf_Addr) round_kernel;
392	  *start = first;
393	  *end = last;
394	  result = 0;
395	}
396    }
397  free (line);
398
399  if (result == -1)
400    result = ferror_unlocked (f) ? errno : ENOEXEC;
401
402  fclose (f);
403
404  return result;
405}
406
407
408/* Look for a build ID note in NOTESFILE and associate the ID with MOD.  */
409static int
410check_notes (Dwfl_Module *mod, const char *notesfile,
411	     Dwarf_Addr vaddr, const char *secname)
412{
413  int fd = open64 (notesfile, O_RDONLY);
414  if (fd < 0)
415    return 1;
416
417  assert (sizeof (Elf32_Nhdr) == sizeof (GElf_Nhdr));
418  assert (sizeof (Elf64_Nhdr) == sizeof (GElf_Nhdr));
419  union
420  {
421    GElf_Nhdr nhdr;
422    unsigned char data[8192];
423  } buf;
424
425  ssize_t n = read (fd, buf.data, sizeof buf);
426  close (fd);
427
428  if (n <= 0)
429    return 1;
430
431  unsigned char *p = buf.data;
432  while (p < &buf.data[n])
433    {
434      /* No translation required since we are reading the native kernel.  */
435      GElf_Nhdr *nhdr = (void *) p;
436      p += sizeof *nhdr;
437      unsigned char *name = p;
438      p += (nhdr->n_namesz + 3) & -4U;
439      unsigned char *bits = p;
440      p += (nhdr->n_descsz + 3) & -4U;
441
442      if (p <= &buf.data[n]
443	  && nhdr->n_type == NT_GNU_BUILD_ID
444	  && nhdr->n_namesz == sizeof "GNU"
445	  && !memcmp (name, "GNU", sizeof "GNU"))
446	{
447	  /* Found it.  For a module we must figure out its VADDR now.  */
448
449	  if (secname != NULL
450	      && (INTUSE(dwfl_linux_kernel_module_section_address)
451		  (mod, NULL, mod->name, 0, secname, 0, NULL, &vaddr) != 0
452		  || vaddr == (GElf_Addr) -1l))
453	    vaddr = 0;
454
455	  if (vaddr != 0)
456	    vaddr += bits - buf.data;
457	  return INTUSE(dwfl_module_report_build_id) (mod, bits,
458						      nhdr->n_descsz, vaddr);
459	}
460    }
461
462  return 0;
463}
464
465/* Look for a build ID for the kernel.  */
466static int
467check_kernel_notes (Dwfl_Module *kernelmod, GElf_Addr vaddr)
468{
469  return check_notes (kernelmod, KNOTESFILE, vaddr, NULL) < 0 ? -1 : 0;
470}
471
472/* Look for a build ID for a loaded kernel module.  */
473static int
474check_module_notes (Dwfl_Module *mod)
475{
476  char *dirs[2] = { NULL, NULL };
477  if (asprintf (&dirs[0], MODNOTESFMT, mod->name) < 0)
478    return ENOMEM;
479
480  FTS *fts = fts_open (dirs, FTS_NOSTAT, NULL);
481  if (fts == NULL)
482    {
483      free (dirs[0]);
484      return 0;
485    }
486
487  int result = 0;
488  FTSENT *f;
489  while ((f = fts_read (fts)) != NULL)
490    {
491      switch (f->fts_info)
492	{
493	case FTS_F:
494	case FTS_NSOK:
495	  result = check_notes (mod, f->fts_accpath, 0, f->fts_name);
496	  if (result > 0)	/* Nothing found.  */
497	    {
498	      result = 0;
499	      continue;
500	    }
501	  break;
502
503	case FTS_ERR:
504	case FTS_DNR:
505	  result = f->fts_errno;
506	  break;
507
508	case FTS_NS:
509	default:
510	  continue;
511	}
512
513      /* We only get here when finished or in error cases.  */
514      break;
515    }
516  fts_close (fts);
517  free (dirs[0]);
518
519  return result;
520}
521
522int
523dwfl_linux_kernel_report_kernel (Dwfl *dwfl)
524{
525  Dwarf_Addr start;
526  Dwarf_Addr end;
527  inline Dwfl_Module *report (void)
528    {
529      return INTUSE(dwfl_report_module) (dwfl, KERNEL_MODNAME, start, end);
530    }
531
532  /* This is a bit of a kludge.  If we already reported the kernel,
533     don't bother figuring it out again--it never changes.  */
534  for (Dwfl_Module *m = dwfl->modulelist; m != NULL; m = m->next)
535    if (!strcmp (m->name, KERNEL_MODNAME))
536      {
537	start = m->low_addr;
538	end = m->high_addr;
539	return report () == NULL ? -1 : 0;
540      }
541
542  /* Try to figure out the bounds of the kernel image without
543     looking for any vmlinux file.  */
544  Dwarf_Addr notes;
545  /* The compiler cannot deduce that if intuit_kernel_bounds returns
546     zero NOTES will be initialized.  Fake the initialization.  */
547  asm ("" : "=m" (notes));
548  int result = intuit_kernel_bounds (&start, &end, &notes);
549  if (result == 0)
550    {
551      Dwfl_Module *mod = report ();
552      return unlikely (mod == NULL) ? -1 : check_kernel_notes (mod, notes);
553    }
554  if (result != ENOENT)
555    return result;
556
557  /* Find the ELF file for the running kernel and dwfl_report_elf it.  */
558  return report_kernel (dwfl, NULL, NULL);
559}
560INTDEF (dwfl_linux_kernel_report_kernel)
561
562
563/* Dwfl_Callbacks.find_elf for the running Linux kernel and its modules.  */
564
565int
566dwfl_linux_kernel_find_elf (Dwfl_Module *mod,
567			    void **userdata __attribute__ ((unused)),
568			    const char *module_name,
569			    Dwarf_Addr base __attribute__ ((unused)),
570			    char **file_name, Elf **elfp)
571{
572  if (mod->build_id_len > 0)
573    {
574      int fd = INTUSE(dwfl_build_id_find_elf) (mod, NULL, NULL, 0,
575					       file_name, elfp);
576      if (fd >= 0 || errno != 0)
577	return fd;
578    }
579
580  const char *release = kernel_release ();
581  if (release == NULL)
582    return errno;
583
584  if (!strcmp (module_name, KERNEL_MODNAME))
585    return find_kernel_elf (mod->dwfl, release, file_name);
586
587  /* Do "find /lib/modules/`uname -r` -name MODULE_NAME.ko".  */
588
589  char *modulesdir[] = { NULL, NULL };
590  if (asprintf (&modulesdir[0], MODULEDIRFMT, release) < 0)
591    return -1;
592
593  FTS *fts = fts_open (modulesdir, FTS_NOSTAT, NULL);
594  if (fts == NULL)
595    {
596      free (modulesdir[0]);
597      return -1;
598    }
599
600  size_t namelen = strlen (module_name);
601
602  /* This is a kludge.  There is no actual necessary relationship between
603     the name of the .ko file installed and the module name the kernel
604     knows it by when it's loaded.  The kernel's only idea of the module
605     name comes from the name embedded in the object's magic
606     .gnu.linkonce.this_module section.
607
608     In practice, these module names match the .ko file names except for
609     some using '_' and some using '-'.  So our cheap kludge is to look for
610     two files when either a '_' or '-' appears in a module name, one using
611     only '_' and one only using '-'.  */
612
613  char alternate_name[namelen + 1];
614  inline bool subst_name (char from, char to)
615    {
616      const char *n = memchr (module_name, from, namelen);
617      if (n == NULL)
618	return false;
619      char *a = mempcpy (alternate_name, module_name, n - module_name);
620      *a++ = to;
621      ++n;
622      const char *p;
623      while ((p = memchr (n, from, namelen - (n - module_name))) != NULL)
624	{
625	  a = mempcpy (a, n, p - n);
626	  *a++ = to;
627	  n = p + 1;
628	}
629      memcpy (a, n, namelen - (n - module_name) + 1);
630      return true;
631    }
632  if (!subst_name ('-', '_') && !subst_name ('_', '-'))
633    alternate_name[0] = '\0';
634
635  FTSENT *f;
636  int error = ENOENT;
637  while ((f = fts_read (fts)) != NULL)
638    {
639      error = ENOENT;
640      switch (f->fts_info)
641	{
642	case FTS_F:
643	case FTS_NSOK:
644	  /* See if this file name is "MODULE_NAME.ko".  */
645	  if (f->fts_namelen == namelen + 3
646	      && !memcmp (f->fts_name + namelen, ".ko", 4)
647	      && (!memcmp (f->fts_name, module_name, namelen)
648		  || !memcmp (f->fts_name, alternate_name, namelen)))
649	    {
650	      int fd = open64 (f->fts_accpath, O_RDONLY);
651	      *file_name = strdup (f->fts_path);
652	      fts_close (fts);
653	      free (modulesdir[0]);
654	      if (fd < 0)
655		free (*file_name);
656	      else if (*file_name == NULL)
657		{
658		  close (fd);
659		  fd = -1;
660		}
661	      return fd;
662	    }
663	  break;
664
665	case FTS_ERR:
666	case FTS_DNR:
667	case FTS_NS:
668	  error = f->fts_errno;
669	  break;
670
671	default:
672	  break;
673	}
674    }
675
676  fts_close (fts);
677  free (modulesdir[0]);
678  errno = error;
679  return -1;
680}
681INTDEF (dwfl_linux_kernel_find_elf)
682
683
684/* Dwfl_Callbacks.section_address for kernel modules in the running Linux.
685   We read the information from /sys/module directly.  */
686
687int
688dwfl_linux_kernel_module_section_address
689(Dwfl_Module *mod __attribute__ ((unused)),
690 void **userdata __attribute__ ((unused)),
691 const char *modname, Dwarf_Addr base __attribute__ ((unused)),
692 const char *secname, Elf32_Word shndx __attribute__ ((unused)),
693 const GElf_Shdr *shdr __attribute__ ((unused)),
694 Dwarf_Addr *addr)
695{
696  char *sysfile;
697  if (asprintf (&sysfile, SECADDRDIRFMT "%s", modname, secname) < 0)
698    return DWARF_CB_ABORT;
699
700  FILE *f = fopen (sysfile, "r");
701  free (sysfile);
702
703  if (f == NULL)
704    {
705      if (errno == ENOENT)
706	{
707	  /* The .modinfo and .data.percpu sections are never kept
708	     loaded in the kernel.  If the kernel was compiled without
709	     CONFIG_MODULE_UNLOAD, the .exit.* sections are not
710	     actually loaded at all.
711
712	     Setting *ADDR to -1 tells the caller this section is
713	     actually absent from memory.  */
714
715	  if (!strcmp (secname, ".modinfo")
716	      || !strcmp (secname, ".data.percpu")
717	      || !strncmp (secname, ".exit", 5))
718	    {
719	      *addr = (Dwarf_Addr) -1l;
720	      return DWARF_CB_OK;
721	    }
722
723	  /* The goofy PPC64 module_frob_arch_sections function tweaks
724	     the section names as a way to control other kernel code's
725	     behavior, and this cruft leaks out into the /sys information.
726	     The file name for ".init*" may actually look like "_init*".  */
727
728	  const bool is_init = !strncmp (secname, ".init", 5);
729	  if (is_init)
730	    {
731	      if (asprintf (&sysfile, SECADDRDIRFMT "_%s",
732			    modname, &secname[1]) < 0)
733		return ENOMEM;
734	      f = fopen (sysfile, "r");
735	      free (sysfile);
736	      if (f != NULL)
737		goto ok;
738	    }
739
740	  /* The kernel truncates section names to MODULE_SECT_NAME_LEN - 1.
741	     In case that size increases in the future, look for longer
742	     truncated names first.  */
743	  size_t namelen = strlen (secname);
744	  if (namelen >= MODULE_SECT_NAME_LEN)
745	    {
746	      int len = asprintf (&sysfile, SECADDRDIRFMT "%s",
747				  modname, secname);
748	      if (len < 0)
749		return DWARF_CB_ABORT;
750	      char *end = sysfile + len;
751	      do
752		{
753		  *--end = '\0';
754		  f = fopen (sysfile, "r");
755		  if (is_init && f == NULL && errno == ENOENT)
756		    {
757		      sysfile[len - namelen] = '_';
758		      f = fopen (sysfile, "r");
759		      sysfile[len - namelen] = '.';
760		    }
761		}
762	      while (f == NULL && errno == ENOENT
763		     && end - &sysfile[len - namelen] >= MODULE_SECT_NAME_LEN);
764	      free (sysfile);
765
766	      if (f != NULL)
767		goto ok;
768	    }
769	}
770
771      return DWARF_CB_ABORT;
772    }
773
774 ok:
775  (void) __fsetlocking (f, FSETLOCKING_BYCALLER);
776
777  int result = (fscanf (f, "%" PRIx64 "\n", addr) == 1 ? 0
778		: ferror_unlocked (f) ? errno : ENOEXEC);
779  fclose (f);
780
781  if (result == 0)
782    return DWARF_CB_OK;
783
784  errno = result;
785  return DWARF_CB_ABORT;
786}
787INTDEF (dwfl_linux_kernel_module_section_address)
788
789int
790dwfl_linux_kernel_report_modules (Dwfl *dwfl)
791{
792  FILE *f = fopen (MODULELIST, "r");
793  if (f == NULL)
794    return errno;
795
796  (void) __fsetlocking (f, FSETLOCKING_BYCALLER);
797
798  int result = 0;
799  Dwarf_Addr modaddr;
800  unsigned long int modsz;
801  char modname[128];
802  char *line = NULL;
803  size_t linesz = 0;
804  /* We can't just use fscanf here because it's not easy to distinguish \n
805     from other whitespace so as to take the optional word following the
806     address but always stop at the end of the line.  */
807  while (getline (&line, &linesz, f) > 0
808	 && sscanf (line, "%128s %lu %*s %*s %*s %" PRIx64 " %*s\n",
809		    modname, &modsz, &modaddr) == 3)
810    {
811      Dwfl_Module *mod = INTUSE(dwfl_report_module) (dwfl, modname,
812						     modaddr, modaddr + modsz);
813      if (mod == NULL)
814	{
815	  result = -1;
816	  break;
817	}
818
819      result = check_module_notes (mod);
820    }
821  free (line);
822
823  if (result == 0)
824    result = ferror_unlocked (f) ? errno : feof_unlocked (f) ? 0 : ENOEXEC;
825
826  fclose (f);
827
828  return result;
829}
830INTDEF (dwfl_linux_kernel_report_modules)
831