Blame libdwfl/linux-kernel-modules.c

Packit Service 97d2fb
/* Standard libdwfl callbacks for debugging the running Linux kernel.
Packit Service 97d2fb
   Copyright (C) 2005-2011, 2013, 2014, 2015 Red Hat, Inc.
Packit Service 97d2fb
   This file is part of elfutils.
Packit Service 97d2fb
Packit Service 97d2fb
   This file is free software; you can redistribute it and/or modify
Packit Service 97d2fb
   it under the terms of either
Packit Service 97d2fb
Packit Service 97d2fb
     * the GNU Lesser General Public License as published by the Free
Packit Service 97d2fb
       Software Foundation; either version 3 of the License, or (at
Packit Service 97d2fb
       your option) any later version
Packit Service 97d2fb
Packit Service 97d2fb
   or
Packit Service 97d2fb
Packit Service 97d2fb
     * the GNU General Public License as published by the Free
Packit Service 97d2fb
       Software Foundation; either version 2 of the License, or (at
Packit Service 97d2fb
       your option) any later version
Packit Service 97d2fb
Packit Service 97d2fb
   or both in parallel, as here.
Packit Service 97d2fb
Packit Service 97d2fb
   elfutils is distributed in the hope that it will be useful, but
Packit Service 97d2fb
   WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 97d2fb
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 97d2fb
   General Public License for more details.
Packit Service 97d2fb
Packit Service 97d2fb
   You should have received copies of the GNU General Public License and
Packit Service 97d2fb
   the GNU Lesser General Public License along with this program.  If
Packit Service 97d2fb
   not, see <http://www.gnu.org/licenses/>.  */
Packit Service 97d2fb
Packit Service 97d2fb
/* In case we have a bad fts we include this before config.h because it
Packit Service 97d2fb
   can't handle _FILE_OFFSET_BITS.
Packit Service 97d2fb
   Everything we need here is fine if its declarations just come first.
Packit Service 97d2fb
   Also, include sys/types.h before fts. On some systems fts.h is not self
Packit Service 97d2fb
   contained. */
Packit Service 97d2fb
#ifdef BAD_FTS
Packit Service 97d2fb
  #include <sys/types.h>
Packit Service 97d2fb
  #include <fts.h>
Packit Service 97d2fb
#endif
Packit Service 97d2fb
Packit Service 97d2fb
#include <config.h>
Packit Service 97d2fb
#include <system.h>
Packit Service 97d2fb
Packit Service 97d2fb
#include "libelfP.h"
Packit Service 97d2fb
#include "libdwflP.h"
Packit Service 97d2fb
#include <inttypes.h>
Packit Service 97d2fb
#include <errno.h>
Packit Service 97d2fb
#include <stdio.h>
Packit Service 97d2fb
#include <stdio_ext.h>
Packit Service 97d2fb
#include <string.h>
Packit Service 97d2fb
#include <stdlib.h>
Packit Service 97d2fb
#include <sys/utsname.h>
Packit Service 97d2fb
#include <fcntl.h>
Packit Service 97d2fb
#include <unistd.h>
Packit Service 97d2fb
Packit Service 97d2fb
/* If fts.h is included before config.h, its indirect inclusions may not
Packit Service 97d2fb
   give us the right LFS aliases of these functions, so map them manually.  */
Packit Service 97d2fb
#ifdef BAD_FTS
Packit Service 97d2fb
  #ifdef _FILE_OFFSET_BITS
Packit Service 97d2fb
    #define open open64
Packit Service 97d2fb
    #define fopen fopen64
Packit Service 97d2fb
  #endif
Packit Service 97d2fb
#else
Packit Service 97d2fb
  #include <sys/types.h>
Packit Service 97d2fb
  #include <fts.h>
Packit Service 97d2fb
#endif
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
#define KERNEL_MODNAME	"kernel"
Packit Service 97d2fb
Packit Service 97d2fb
#define MODULEDIRFMT	"/lib/modules/%s"
Packit Service 97d2fb
Packit Service 97d2fb
#define KNOTESFILE	"/sys/kernel/notes"
Packit Service 97d2fb
#define	MODNOTESFMT	"/sys/module/%s/notes"
Packit Service 97d2fb
#define KSYMSFILE	"/proc/kallsyms"
Packit Service 97d2fb
#define MODULELIST	"/proc/modules"
Packit Service 97d2fb
#define	SECADDRDIRFMT	"/sys/module/%s/sections/"
Packit Service 97d2fb
#define MODULE_SECT_NAME_LEN 32	/* Minimum any linux/module.h has had.  */
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
static const char *vmlinux_suffixes[] =
Packit Service 97d2fb
  {
Packit Service 97d2fb
    ".gz",
Packit Service 97d2fb
#ifdef USE_BZLIB
Packit Service 97d2fb
    ".bz2",
Packit Service 97d2fb
#endif
Packit Service 97d2fb
#ifdef USE_LZMA
Packit Service 97d2fb
    ".xz",
Packit Service 97d2fb
#endif
Packit Service 97d2fb
  };
Packit Service 97d2fb
Packit Service 97d2fb
/* Try to open the given file as it is or under the debuginfo directory.  */
Packit Service 97d2fb
static int
Packit Service 97d2fb
try_kernel_name (Dwfl *dwfl, char **fname, bool try_debug)
Packit Service 97d2fb
{
Packit Service 97d2fb
  if (*fname == NULL)
Packit Service 97d2fb
    return -1;
Packit Service 97d2fb
Packit Service 97d2fb
  /* Don't bother trying *FNAME itself here if the path will cause it to be
Packit Service 97d2fb
     tried because we give its own basename as DEBUGLINK_FILE.  */
Packit Service 97d2fb
  int fd = ((((dwfl->callbacks->debuginfo_path
Packit Service 97d2fb
	       ? *dwfl->callbacks->debuginfo_path : NULL)
Packit Service 97d2fb
	      ?: DEFAULT_DEBUGINFO_PATH)[0] == ':') ? -1
Packit Service 97d2fb
	    : TEMP_FAILURE_RETRY (open (*fname, O_RDONLY)));
Packit Service 97d2fb
Packit Service 97d2fb
  if (fd < 0)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      Dwfl_Module fakemod = { .dwfl = dwfl };
Packit Service 97d2fb
Packit Service 97d2fb
      if (try_debug)
Packit Service 97d2fb
	/* Passing NULL for DEBUGLINK_FILE searches for both the basenamer
Packit Service 97d2fb
	   "vmlinux" and the default of basename + ".debug", to look for
Packit Service 97d2fb
	   "vmlinux.debug" files.  */
Packit Service 97d2fb
	fd = INTUSE(dwfl_standard_find_debuginfo) (&fakemod, NULL, NULL, 0,
Packit Service 97d2fb
						   *fname, NULL, 0,
Packit Service 97d2fb
						   &fakemod.debug.name);
Packit Service 97d2fb
      else
Packit Service 97d2fb
	/* Try the file's unadorned basename as DEBUGLINK_FILE,
Packit Service 97d2fb
	   to look only for "vmlinux" files.  */
Packit Service 97d2fb
	fd = INTUSE(dwfl_standard_find_debuginfo) (&fakemod, NULL, NULL, 0,
Packit Service 97d2fb
						   *fname, basename (*fname),
Packit Service 97d2fb
						   0, &fakemod.debug.name);
Packit Service 97d2fb
Packit Service 97d2fb
      if (fakemod.debug.name != NULL)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  free (*fname);
Packit Service 97d2fb
	  *fname = fakemod.debug.name;
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  if (fd < 0)
Packit Service 97d2fb
    for (size_t i = 0;
Packit Service 97d2fb
	 i < sizeof vmlinux_suffixes / sizeof vmlinux_suffixes[0] && fd < 0;
Packit Service 97d2fb
	 ++i)
Packit Service 97d2fb
      {
Packit Service 97d2fb
	char *zname;
Packit Service 97d2fb
	if (asprintf (&zname, "%s%s", *fname, vmlinux_suffixes[i]) > 0)
Packit Service 97d2fb
	  {
Packit Service 97d2fb
	    fd = TEMP_FAILURE_RETRY (open (zname, O_RDONLY));
Packit Service 97d2fb
	    if (fd < 0)
Packit Service 97d2fb
	      free (zname);
Packit Service 97d2fb
	    else
Packit Service 97d2fb
	      {
Packit Service 97d2fb
		free (*fname);
Packit Service 97d2fb
		*fname = zname;
Packit Service 97d2fb
	      }
Packit Service 97d2fb
	  }
Packit Service 97d2fb
      }
Packit Service 97d2fb
Packit Service 97d2fb
  if (fd < 0)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      free (*fname);
Packit Service 97d2fb
      *fname = NULL;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  return fd;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
static inline const char *
Packit Service 97d2fb
kernel_release (void)
Packit Service 97d2fb
{
Packit Service 97d2fb
#ifdef __linux__
Packit Service 97d2fb
  /* Cache the `uname -r` string we'll use.  */
Packit Service 97d2fb
  static struct utsname utsname;
Packit Service 97d2fb
  if (utsname.release[0] == '\0' && uname (&utsname) != 0)
Packit Service 97d2fb
    return NULL;
Packit Service 97d2fb
  return utsname.release;
Packit Service 97d2fb
#else
Packit Service 97d2fb
  /* Used for finding the running linux kernel, which isn't supported
Packit Service 97d2fb
     on non-linux kernel systems.  */
Packit Service 97d2fb
  errno = ENOTSUP;
Packit Service 97d2fb
  return NULL;
Packit Service 97d2fb
#endif
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
static int
Packit Service 97d2fb
find_kernel_elf (Dwfl *dwfl, const char *release, char **fname)
Packit Service 97d2fb
{
Packit Service 97d2fb
  /* First try to find an uncompressed vmlinux image.  Possibly
Packit Service 97d2fb
     including debuginfo.  */
Packit Service 97d2fb
  if (release == NULL
Packit Service 97d2fb
      || ((release[0] == '/'
Packit Service 97d2fb
	   ? asprintf (fname, "%s/vmlinux", release)
Packit Service 97d2fb
	   : asprintf (fname, "/boot/vmlinux-%s", release)) < 0))
Packit Service 97d2fb
    return -1;
Packit Service 97d2fb
Packit Service 97d2fb
  int fd = try_kernel_name (dwfl, fname, true);
Packit Service 97d2fb
  if (fd < 0 && release[0] != '/')
Packit Service 97d2fb
    {
Packit Service 97d2fb
      free (*fname);
Packit Service 97d2fb
      if (asprintf (fname, MODULEDIRFMT "/vmlinux", release) < 0)
Packit Service 97d2fb
	return -1;
Packit Service 97d2fb
      fd = try_kernel_name (dwfl, fname, true);
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  /* There might be a compressed vmlinuz image.  Probably without
Packit Service 97d2fb
     debuginfo, but try to find it under the debug path also, just in
Packit Service 97d2fb
     case.  */
Packit Service 97d2fb
  if (fd < 0)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      free (*fname);
Packit Service 97d2fb
      if ((release[0] == '/'
Packit Service 97d2fb
           ? asprintf (fname, "%s/vmlinuz", release)
Packit Service 97d2fb
           : asprintf (fname, "/boot/vmlinuz-%s", release)) < 0)
Packit Service 97d2fb
        return -1;
Packit Service 97d2fb
Packit Service 97d2fb
      fd = try_kernel_name (dwfl, fname, true);
Packit Service 97d2fb
      if (fd < 0 && release[0] != '/')
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  free (*fname);
Packit Service 97d2fb
	  if (asprintf (fname, MODULEDIRFMT "/vmlinuz", release) < 0)
Packit Service 97d2fb
	    return -1;
Packit Service 97d2fb
	  fd = try_kernel_name (dwfl, fname, true);
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  return fd;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
static int
Packit Service 97d2fb
get_release (Dwfl *dwfl, const char **release)
Packit Service 97d2fb
{
Packit Service 97d2fb
  if (dwfl == NULL)
Packit Service 97d2fb
    return -1;
Packit Service 97d2fb
Packit Service 97d2fb
  const char *release_string = release == NULL ? NULL : *release;
Packit Service 97d2fb
  if (release_string == NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      release_string = kernel_release ();
Packit Service 97d2fb
      if (release_string == NULL)
Packit Service 97d2fb
	return errno;
Packit Service 97d2fb
      if (release != NULL)
Packit Service 97d2fb
	*release = release_string;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  return 0;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
static int
Packit Service 97d2fb
report_kernel (Dwfl *dwfl, const char **release,
Packit Service 97d2fb
	       int (*predicate) (const char *module, const char *file))
Packit Service 97d2fb
{
Packit Service 97d2fb
  int result = get_release (dwfl, release);
Packit Service 97d2fb
  if (unlikely (result != 0))
Packit Service 97d2fb
    return result;
Packit Service 97d2fb
Packit Service 97d2fb
  if (release == NULL || *release == NULL)
Packit Service 97d2fb
    return EINVAL;
Packit Service 97d2fb
Packit Service 97d2fb
  char *fname;
Packit Service 97d2fb
  int fd = find_kernel_elf (dwfl, *release, &fname);
Packit Service 97d2fb
Packit Service 97d2fb
  if (fd < 0)
Packit Service 97d2fb
    result = ((predicate != NULL && !(*predicate) (KERNEL_MODNAME, NULL))
Packit Service 97d2fb
	      ? 0 : errno ?: ENOENT);
Packit Service 97d2fb
  else
Packit Service 97d2fb
    {
Packit Service 97d2fb
      bool report = true;
Packit Service 97d2fb
Packit Service 97d2fb
      if (predicate != NULL)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  /* Let the predicate decide whether to use this one.  */
Packit Service 97d2fb
	  int want = (*predicate) (KERNEL_MODNAME, fname);
Packit Service 97d2fb
	  if (want < 0)
Packit Service 97d2fb
	    result = errno;
Packit Service 97d2fb
	  report = want > 0;
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      if (report)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  /* Note that on some architectures (e.g. x86_64) the vmlinux
Packit Service 97d2fb
	     is ET_EXEC, while on others (e.g. ppc64) it is ET_DYN.
Packit Service 97d2fb
	     In both cases the phdr p_vaddr load address will be non-zero.
Packit Service 97d2fb
	     We want the image to be placed as if it was ET_DYN, so
Packit Service 97d2fb
	     pass true for add_p_vaddr which will do the right thing
Packit Service 97d2fb
	     (in combination with a zero base) in either case.  */
Packit Service 97d2fb
	  Dwfl_Module *mod = INTUSE(dwfl_report_elf) (dwfl, KERNEL_MODNAME,
Packit Service 97d2fb
						      fname, fd, 0, true);
Packit Service 97d2fb
	  if (mod == NULL)
Packit Service 97d2fb
	    result = -1;
Packit Service 97d2fb
	  else
Packit Service 97d2fb
	    /* The kernel is ET_EXEC, but always treat it as relocatable.  */
Packit Service 97d2fb
	    mod->e_type = ET_DYN;
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      free (fname);
Packit Service 97d2fb
Packit Service 97d2fb
      if (!report || result < 0)
Packit Service 97d2fb
	close (fd);
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  return result;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
/* Look for a kernel debug archive.  If we find one, report all its modules.
Packit Service 97d2fb
   If not, return ENOENT.  */
Packit Service 97d2fb
static int
Packit Service 97d2fb
report_kernel_archive (Dwfl *dwfl, const char **release,
Packit Service 97d2fb
		       int (*predicate) (const char *module, const char *file))
Packit Service 97d2fb
{
Packit Service 97d2fb
  int result = get_release (dwfl, release);
Packit Service 97d2fb
  if (unlikely (result != 0))
Packit Service 97d2fb
    return result;
Packit Service 97d2fb
Packit Service 97d2fb
  if (release == NULL || *release == NULL)
Packit Service 97d2fb
    return EINVAL;
Packit Service 97d2fb
Packit Service 97d2fb
  char *archive;
Packit Service 97d2fb
  int res = (((*release)[0] == '/')
Packit Service 97d2fb
	     ? asprintf (&archive, "%s/debug.a", *release)
Packit Service 97d2fb
	     : asprintf (&archive, MODULEDIRFMT "/debug.a", *release));
Packit Service 97d2fb
  if (unlikely (res < 0))
Packit Service 97d2fb
    return ENOMEM;
Packit Service 97d2fb
Packit Service 97d2fb
  int fd = try_kernel_name (dwfl, &archive, false);
Packit Service 97d2fb
  if (fd < 0)
Packit Service 97d2fb
    result = errno ?: ENOENT;
Packit Service 97d2fb
  else
Packit Service 97d2fb
    {
Packit Service 97d2fb
      /* We have the archive file open!  */
Packit Service 97d2fb
      Dwfl_Module *last = __libdwfl_report_offline (dwfl, NULL, archive, fd,
Packit Service 97d2fb
						    true, predicate);
Packit Service 97d2fb
      if (unlikely (last == NULL))
Packit Service 97d2fb
	result = -1;
Packit Service 97d2fb
      else
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  /* Find the kernel and move it to the head of the list.  */
Packit Service 97d2fb
	  Dwfl_Module **tailp = &dwfl->modulelist, **prevp = tailp;
Packit Service 97d2fb
	  for (Dwfl_Module *m = *prevp; m != NULL; m = *(prevp = &m->next))
Packit Service 97d2fb
	    if (!m->gc && m->e_type != ET_REL && !strcmp (m->name, "kernel"))
Packit Service 97d2fb
	      {
Packit Service 97d2fb
		*prevp = m->next;
Packit Service 97d2fb
		m->next = *tailp;
Packit Service 97d2fb
		*tailp = m;
Packit Service 97d2fb
		break;
Packit Service 97d2fb
	      }
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  free (archive);
Packit Service 97d2fb
  return result;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
static size_t
Packit Service 97d2fb
check_suffix (const FTSENT *f, size_t namelen)
Packit Service 97d2fb
{
Packit Service 97d2fb
#define TRY(sfx)							\
Packit Service 97d2fb
  if ((namelen ? f->fts_namelen == namelen + sizeof sfx - 1		\
Packit Service 97d2fb
       : f->fts_namelen >= sizeof sfx)					\
Packit Service 97d2fb
      && !memcmp (f->fts_name + f->fts_namelen - (sizeof sfx - 1),	\
Packit Service 97d2fb
		  sfx, sizeof sfx))					\
Packit Service 97d2fb
    return sizeof sfx - 1
Packit Service 97d2fb
Packit Service 97d2fb
  TRY (".ko");
Packit Service 97d2fb
  TRY (".ko.gz");
Packit Service 97d2fb
#if USE_BZLIB
Packit Service 97d2fb
  TRY (".ko.bz2");
Packit Service 97d2fb
#endif
Packit Service 97d2fb
#if USE_LZMA
Packit Service 97d2fb
  TRY (".ko.xz");
Packit Service 97d2fb
#endif
Packit Service 97d2fb
#if USE_ZSTD
Packit Service 97d2fb
  TRY (".ko.zst");
Packit Service 97d2fb
#endif
Packit Service 97d2fb
Packit Service 97d2fb
  return 0;
Packit Service 97d2fb
Packit Service 97d2fb
#undef	TRY
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
/* Report a kernel and all its modules found on disk, for offline use.
Packit Service 97d2fb
   If RELEASE starts with '/', it names a directory to look in;
Packit Service 97d2fb
   if not, it names a directory to find under /lib/modules/;
Packit Service 97d2fb
   if null, /lib/modules/`uname -r` is used.
Packit Service 97d2fb
   Returns zero on success, -1 if dwfl_report_module failed,
Packit Service 97d2fb
   or an errno code if finding the files on disk failed.  */
Packit Service 97d2fb
int
Packit Service 97d2fb
dwfl_linux_kernel_report_offline (Dwfl *dwfl, const char *release,
Packit Service 97d2fb
				  int (*predicate) (const char *module,
Packit Service 97d2fb
						    const char *file))
Packit Service 97d2fb
{
Packit Service 97d2fb
  int result = report_kernel_archive (dwfl, &release, predicate);
Packit Service 97d2fb
  if (result != ENOENT)
Packit Service 97d2fb
    return result;
Packit Service 97d2fb
Packit Service 97d2fb
  /* First report the kernel.  */
Packit Service 97d2fb
  result = report_kernel (dwfl, &release, predicate);
Packit Service 97d2fb
  if (result == 0)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      /* Do "find /lib/modules/RELEASE -name *.ko".  */
Packit Service 97d2fb
Packit Service 97d2fb
      char *modulesdir[] = { NULL, NULL };
Packit Service 97d2fb
      if (release[0] == '/')
Packit Service 97d2fb
	modulesdir[0] = (char *) release;
Packit Service 97d2fb
      else
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  if (asprintf (&modulesdir[0], MODULEDIRFMT, release) < 0)
Packit Service 97d2fb
	    return errno;
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      FTS *fts = fts_open (modulesdir, FTS_NOSTAT | FTS_LOGICAL, NULL);
Packit Service 97d2fb
      if (modulesdir[0] == (char *) release)
Packit Service 97d2fb
	modulesdir[0] = NULL;
Packit Service 97d2fb
      if (fts == NULL)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  free (modulesdir[0]);
Packit Service 97d2fb
	  return errno;
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      FTSENT *f;
Packit Service 97d2fb
      while ((f = fts_read (fts)) != NULL)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  /* Skip a "source" subtree, which tends to be large.
Packit Service 97d2fb
	     This insane hard-coding of names is what depmod does too.  */
Packit Service 97d2fb
	  if (f->fts_namelen == sizeof "source" - 1
Packit Service 97d2fb
	      && !strcmp (f->fts_name, "source"))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      fts_set (fts, f, FTS_SKIP);
Packit Service 97d2fb
	      continue;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
Packit Service 97d2fb
	  switch (f->fts_info)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	    case FTS_F:
Packit Service 97d2fb
	    case FTS_SL:
Packit Service 97d2fb
	    case FTS_NSOK:;
Packit Service 97d2fb
	      /* See if this file name matches "*.ko".  */
Packit Service 97d2fb
	      const size_t suffix = check_suffix (f, 0);
Packit Service 97d2fb
	      if (suffix)
Packit Service 97d2fb
		{
Packit Service 97d2fb
		  /* We have a .ko file to report.  Following the algorithm
Packit Service 97d2fb
		     by which the kernel makefiles set KBUILD_MODNAME, we
Packit Service 97d2fb
		     replace all ',' or '-' with '_' in the file name and
Packit Service 97d2fb
		     call that the module name.  Modules could well be
Packit Service 97d2fb
		     built using different embedded names than their file
Packit Service 97d2fb
		     names.  To handle that, we would have to look at the
Packit Service 97d2fb
		     __this_module.name contents in the module's text.  */
Packit Service 97d2fb
Packit Service 97d2fb
		  char *name = strndup (f->fts_name, f->fts_namelen - suffix);
Packit Service 97d2fb
		  if (unlikely (name == NULL))
Packit Service 97d2fb
		    {
Packit Service 97d2fb
		      __libdwfl_seterrno (DWFL_E_NOMEM);
Packit Service 97d2fb
		      result = -1;
Packit Service 97d2fb
		      break;
Packit Service 97d2fb
		    }
Packit Service 97d2fb
		  for (size_t i = 0; i < f->fts_namelen - suffix; ++i)
Packit Service 97d2fb
		    if (name[i] == '-' || name[i] == ',')
Packit Service 97d2fb
		      name[i] = '_';
Packit Service 97d2fb
Packit Service 97d2fb
		  if (predicate != NULL)
Packit Service 97d2fb
		    {
Packit Service 97d2fb
		      /* Let the predicate decide whether to use this one.  */
Packit Service 97d2fb
		      int want = (*predicate) (name, f->fts_path);
Packit Service 97d2fb
		      if (want < 0)
Packit Service 97d2fb
			{
Packit Service 97d2fb
			  result = -1;
Packit Service 97d2fb
			  free (name);
Packit Service 97d2fb
			  break;
Packit Service 97d2fb
			}
Packit Service 97d2fb
		      if (!want)
Packit Service 97d2fb
			{
Packit Service 97d2fb
			  free (name);
Packit Service 97d2fb
			  continue;
Packit Service 97d2fb
			}
Packit Service 97d2fb
		    }
Packit Service 97d2fb
Packit Service 97d2fb
		  if (dwfl_report_offline (dwfl, name, f->fts_path, -1) == NULL)
Packit Service 97d2fb
		    {
Packit Service 97d2fb
		      free (name);
Packit Service 97d2fb
		      result = -1;
Packit Service 97d2fb
		      break;
Packit Service 97d2fb
		    }
Packit Service 97d2fb
		  free (name);
Packit Service 97d2fb
		}
Packit Service 97d2fb
	      continue;
Packit Service 97d2fb
Packit Service 97d2fb
	    case FTS_ERR:
Packit Service 97d2fb
	    case FTS_DNR:
Packit Service 97d2fb
	    case FTS_NS:
Packit Service 97d2fb
	      result = f->fts_errno;
Packit Service 97d2fb
	      break;
Packit Service 97d2fb
Packit Service 97d2fb
	    case FTS_SLNONE:
Packit Service 97d2fb
	    default:
Packit Service 97d2fb
	      continue;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
Packit Service 97d2fb
	  /* We only get here in error cases.  */
Packit Service 97d2fb
	  break;
Packit Service 97d2fb
	}
Packit Service 97d2fb
      fts_close (fts);
Packit Service 97d2fb
      free (modulesdir[0]);
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  return result;
Packit Service 97d2fb
}
Packit Service 97d2fb
INTDEF (dwfl_linux_kernel_report_offline)
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
/* State of read_address used by intuit_kernel_bounds. */
Packit Service 97d2fb
struct read_address_state {
Packit Service 97d2fb
  FILE *f;
Packit Service 97d2fb
  char *line;
Packit Service 97d2fb
  size_t linesz;
Packit Service 97d2fb
  size_t n;
Packit Service 97d2fb
  char *p;
Packit Service 97d2fb
  const char *type;
Packit Service 97d2fb
};
Packit Service 97d2fb
Packit Service 97d2fb
static inline bool
Packit Service 97d2fb
read_address (struct read_address_state *state, Dwarf_Addr *addr)
Packit Service 97d2fb
{
Packit Service 97d2fb
  if ((state->n = getline (&state->line, &state->linesz, state->f)) < 1 ||
Packit Service 97d2fb
      state->line[state->n - 2] == ']')
Packit Service 97d2fb
    return false;
Packit Service 97d2fb
  *addr = strtoull (state->line, &state->p, 16);
Packit Service 97d2fb
  state->p += strspn (state->p, " \t");
Packit Service 97d2fb
  state->type = strsep (&state->p, " \t\n");
Packit Service 97d2fb
  if (state->type == NULL)
Packit Service 97d2fb
    return false;
Packit Service 97d2fb
  return state->p != NULL && state->p != state->line;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
/* Grovel around to guess the bounds of the runtime kernel image.  */
Packit Service 97d2fb
static int
Packit Service 97d2fb
intuit_kernel_bounds (Dwarf_Addr *start, Dwarf_Addr *end, Dwarf_Addr *notes)
Packit Service 97d2fb
{
Packit Service 97d2fb
  struct read_address_state state = { NULL, NULL, 0, 0, NULL, NULL };
Packit Service 97d2fb
Packit Service 97d2fb
  *notes = 0;
Packit Service 97d2fb
Packit Service 97d2fb
  state.f = fopen (KSYMSFILE, "r");
Packit Service 97d2fb
  if (state.f == NULL)
Packit Service 97d2fb
    return errno;
Packit Service 97d2fb
Packit Service 97d2fb
  (void) __fsetlocking (state.f, FSETLOCKING_BYCALLER);
Packit Service 97d2fb
Packit Service 97d2fb
  int result;
Packit Service 97d2fb
  do
Packit Service 97d2fb
    result = read_address (&state, start) ? 0 : -1;
Packit Service 97d2fb
  while (result == 0 && strchr ("TtRr", *state.type) == NULL);
Packit Service 97d2fb
Packit Service 97d2fb
  if (result == 0)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      Dwarf_Addr addr;
Packit Service 97d2fb
      *end = *start;
Packit Service 97d2fb
      while (read_address (&state, &addr) && addr >= *end)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  *end = addr;
Packit Service 97d2fb
	  if (*notes == 0 && !strcmp (state.p, "__start_notes\n"))
Packit Service 97d2fb
	    *notes = *end;
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      Dwarf_Addr round_kernel = sysconf (_SC_PAGESIZE);
Packit Service 97d2fb
      *start &= -(Dwarf_Addr) round_kernel;
Packit Service 97d2fb
      *end += round_kernel - 1;
Packit Service 97d2fb
      *end &= -(Dwarf_Addr) round_kernel;
Packit Service 97d2fb
      if (*start >= *end || *end - *start < round_kernel)
Packit Service 97d2fb
	result = -1;
Packit Service 97d2fb
    }
Packit Service 97d2fb
  free (state.line);
Packit Service 97d2fb
Packit Service 97d2fb
  if (result == -1)
Packit Service 97d2fb
    result = ferror_unlocked (state.f) ? errno : ENOEXEC;
Packit Service 97d2fb
Packit Service 97d2fb
  fclose (state.f);
Packit Service 97d2fb
Packit Service 97d2fb
  return result;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
/* Look for a build ID note in NOTESFILE and associate the ID with MOD.  */
Packit Service 97d2fb
static int
Packit Service 97d2fb
check_notes (Dwfl_Module *mod, const char *notesfile,
Packit Service 97d2fb
	     Dwarf_Addr vaddr, const char *secname)
Packit Service 97d2fb
{
Packit Service 97d2fb
  int fd = open (notesfile, O_RDONLY);
Packit Service 97d2fb
  if (fd < 0)
Packit Service 97d2fb
    return 1;
Packit Service 97d2fb
Packit Service 97d2fb
  assert (sizeof (Elf32_Nhdr) == sizeof (GElf_Nhdr));
Packit Service 97d2fb
  assert (sizeof (Elf64_Nhdr) == sizeof (GElf_Nhdr));
Packit Service 97d2fb
  union
Packit Service 97d2fb
  {
Packit Service 97d2fb
    GElf_Nhdr nhdr;
Packit Service 97d2fb
    unsigned char data[8192];
Packit Service 97d2fb
  } buf;
Packit Service 97d2fb
Packit Service 97d2fb
  ssize_t n = read (fd, buf.data, sizeof buf);
Packit Service 97d2fb
  close (fd);
Packit Service 97d2fb
Packit Service 97d2fb
  if (n <= 0)
Packit Service 97d2fb
    return 1;
Packit Service 97d2fb
Packit Service 97d2fb
  unsigned char *p = buf.data;
Packit Service 97d2fb
  size_t len = 0;
Packit Service 97d2fb
  while (p < &buf.data[n])
Packit Service 97d2fb
    {
Packit Service 97d2fb
      /* No translation required since we are reading the native kernel.  */
Packit Service 97d2fb
      GElf_Nhdr *nhdr = (void *) p;
Packit Service 97d2fb
      len += sizeof *nhdr;
Packit Service 97d2fb
      p += len;
Packit Service 97d2fb
      unsigned char *name = p;
Packit Service 97d2fb
      unsigned char *bits;
Packit Service 97d2fb
      /* This is somewhat ugly, GNU Property notes use different padding,
Packit Service 97d2fb
	 but all we have is the file content, so we have to actually check
Packit Service 97d2fb
	 the name and type.  */
Packit Service 97d2fb
      if (nhdr->n_type == NT_GNU_PROPERTY_TYPE_0
Packit Service 97d2fb
          && nhdr->n_namesz == sizeof "GNU"
Packit Service 97d2fb
          && name + nhdr->n_namesz < &buf.data[n]
Packit Service 97d2fb
          && !memcmp (name, "GNU", sizeof "GNU"))
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  len += nhdr->n_namesz;
Packit Service 97d2fb
	  len = NOTE_ALIGN8 (len);
Packit Service 97d2fb
	  p = buf.data + len;
Packit Service 97d2fb
	  bits = p;
Packit Service 97d2fb
	  len += nhdr->n_descsz;
Packit Service 97d2fb
	  len = NOTE_ALIGN8 (len);
Packit Service 97d2fb
	  p = buf.data + len;
Packit Service 97d2fb
	}
Packit Service 97d2fb
      else
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  len += nhdr->n_namesz;
Packit Service 97d2fb
	  len = NOTE_ALIGN4 (len);
Packit Service 97d2fb
	  p = buf.data + len;
Packit Service 97d2fb
	  bits = p;
Packit Service 97d2fb
	  len += nhdr->n_descsz;
Packit Service 97d2fb
	  len = NOTE_ALIGN4 (len);
Packit Service 97d2fb
	  p = buf.data + len;
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      if (p <= &buf.data[n]
Packit Service 97d2fb
	  && nhdr->n_type == NT_GNU_BUILD_ID
Packit Service 97d2fb
	  && nhdr->n_namesz == sizeof "GNU"
Packit Service 97d2fb
	  && !memcmp (name, "GNU", sizeof "GNU"))
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  /* Found it.  For a module we must figure out its VADDR now.  */
Packit Service 97d2fb
Packit Service 97d2fb
	  if (secname != NULL
Packit Service 97d2fb
	      && (INTUSE(dwfl_linux_kernel_module_section_address)
Packit Service 97d2fb
		  (mod, NULL, mod->name, 0, secname, 0, NULL, &vaddr) != 0
Packit Service 97d2fb
		  || vaddr == (GElf_Addr) -1l))
Packit Service 97d2fb
	    vaddr = 0;
Packit Service 97d2fb
Packit Service 97d2fb
	  if (vaddr != 0)
Packit Service 97d2fb
	    vaddr += bits - buf.data;
Packit Service 97d2fb
	  return INTUSE(dwfl_module_report_build_id) (mod, bits,
Packit Service 97d2fb
						      nhdr->n_descsz, vaddr);
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  return 0;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
/* Look for a build ID for the kernel.  */
Packit Service 97d2fb
static int
Packit Service 97d2fb
check_kernel_notes (Dwfl_Module *kernelmod, GElf_Addr vaddr)
Packit Service 97d2fb
{
Packit Service 97d2fb
  return check_notes (kernelmod, KNOTESFILE, vaddr, NULL) < 0 ? -1 : 0;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
/* Look for a build ID for a loaded kernel module.  */
Packit Service 97d2fb
static int
Packit Service 97d2fb
check_module_notes (Dwfl_Module *mod)
Packit Service 97d2fb
{
Packit Service 97d2fb
  char *dirs[2] = { NULL, NULL };
Packit Service 97d2fb
  if (asprintf (&dirs[0], MODNOTESFMT, mod->name) < 0)
Packit Service 97d2fb
    return ENOMEM;
Packit Service 97d2fb
Packit Service 97d2fb
  FTS *fts = fts_open (dirs, FTS_NOSTAT | FTS_LOGICAL, NULL);
Packit Service 97d2fb
  if (fts == NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      free (dirs[0]);
Packit Service 97d2fb
      return 0;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  int result = 0;
Packit Service 97d2fb
  FTSENT *f;
Packit Service 97d2fb
  while ((f = fts_read (fts)) != NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      switch (f->fts_info)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	case FTS_F:
Packit Service 97d2fb
	case FTS_SL:
Packit Service 97d2fb
	case FTS_NSOK:
Packit Service 97d2fb
	  result = check_notes (mod, f->fts_accpath, 0, f->fts_name);
Packit Service 97d2fb
	  if (result > 0)	/* Nothing found.  */
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      result = 0;
Packit Service 97d2fb
	      continue;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  break;
Packit Service 97d2fb
Packit Service 97d2fb
	case FTS_ERR:
Packit Service 97d2fb
	case FTS_DNR:
Packit Service 97d2fb
	  result = f->fts_errno;
Packit Service 97d2fb
	  break;
Packit Service 97d2fb
Packit Service 97d2fb
	case FTS_NS:
Packit Service 97d2fb
	case FTS_SLNONE:
Packit Service 97d2fb
	default:
Packit Service 97d2fb
	  continue;
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      /* We only get here when finished or in error cases.  */
Packit Service 97d2fb
      break;
Packit Service 97d2fb
    }
Packit Service 97d2fb
  fts_close (fts);
Packit Service 97d2fb
  free (dirs[0]);
Packit Service 97d2fb
Packit Service 97d2fb
  return result;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
int
Packit Service 97d2fb
dwfl_linux_kernel_report_kernel (Dwfl *dwfl)
Packit Service 97d2fb
{
Packit Service 97d2fb
  Dwarf_Addr start = 0;
Packit Service 97d2fb
  Dwarf_Addr end = 0;
Packit Service 97d2fb
Packit Service 97d2fb
  #define report() \
Packit Service 97d2fb
    (INTUSE(dwfl_report_module) (dwfl, KERNEL_MODNAME, start, end))
Packit Service 97d2fb
Packit Service 97d2fb
  /* This is a bit of a kludge.  If we already reported the kernel,
Packit Service 97d2fb
     don't bother figuring it out again--it never changes.  */
Packit Service 97d2fb
  for (Dwfl_Module *m = dwfl->modulelist; m != NULL; m = m->next)
Packit Service 97d2fb
    if (!strcmp (m->name, KERNEL_MODNAME))
Packit Service 97d2fb
      {
Packit Service 97d2fb
	start = m->low_addr;
Packit Service 97d2fb
	end = m->high_addr;
Packit Service 97d2fb
	return report () == NULL ? -1 : 0;
Packit Service 97d2fb
      }
Packit Service 97d2fb
Packit Service 97d2fb
  /* Try to figure out the bounds of the kernel image without
Packit Service 97d2fb
     looking for any vmlinux file.  */
Packit Service 97d2fb
  Dwarf_Addr notes;
Packit Service 97d2fb
  int result = intuit_kernel_bounds (&start, &end, &notes);
Packit Service 97d2fb
  if (result == 0)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      Dwfl_Module *mod = report ();
Packit Service 97d2fb
      return unlikely (mod == NULL) ? -1 : check_kernel_notes (mod, notes);
Packit Service 97d2fb
    }
Packit Service 97d2fb
  if (result != ENOENT)
Packit Service 97d2fb
    return result;
Packit Service 97d2fb
Packit Service 97d2fb
  /* Find the ELF file for the running kernel and dwfl_report_elf it.  */
Packit Service 97d2fb
  return report_kernel (dwfl, NULL, NULL);
Packit Service 97d2fb
}
Packit Service 97d2fb
INTDEF (dwfl_linux_kernel_report_kernel)
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
static inline bool
Packit Service 97d2fb
subst_name (char from, char to,
Packit Service 97d2fb
            const char * const module_name,
Packit Service 97d2fb
            char * const alternate_name,
Packit Service 97d2fb
            const size_t namelen)
Packit Service 97d2fb
{
Packit Service 97d2fb
  const char *n = memchr (module_name, from, namelen);
Packit Service 97d2fb
  if (n == NULL)
Packit Service 97d2fb
    return false;
Packit Service 97d2fb
  char *a = mempcpy (alternate_name, module_name, n - module_name);
Packit Service 97d2fb
  *a++ = to;
Packit Service 97d2fb
  ++n;
Packit Service 97d2fb
  const char *p;
Packit Service 97d2fb
  while ((p = memchr (n, from, namelen - (n - module_name))) != NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      a = mempcpy (a, n, p - n);
Packit Service 97d2fb
      *a++ = to;
Packit Service 97d2fb
      n = p + 1;
Packit Service 97d2fb
    }
Packit Service 97d2fb
  memcpy (a, n, namelen - (n - module_name) + 1);
Packit Service 97d2fb
  return true;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
/* Dwfl_Callbacks.find_elf for the running Linux kernel and its modules.  */
Packit Service 97d2fb
Packit Service 97d2fb
int
Packit Service 97d2fb
dwfl_linux_kernel_find_elf (Dwfl_Module *mod,
Packit Service 97d2fb
			    void **userdata __attribute__ ((unused)),
Packit Service 97d2fb
			    const char *module_name,
Packit Service 97d2fb
			    Dwarf_Addr base __attribute__ ((unused)),
Packit Service 97d2fb
			    char **file_name, Elf **elfp)
Packit Service 97d2fb
{
Packit Service 97d2fb
  if (mod->build_id_len > 0)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      int fd = INTUSE(dwfl_build_id_find_elf) (mod, NULL, NULL, 0,
Packit Service 97d2fb
					       file_name, elfp);
Packit Service 97d2fb
      if (fd >= 0 || mod->main.elf != NULL || errno != 0)
Packit Service 97d2fb
	return fd;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  const char *release = kernel_release ();
Packit Service 97d2fb
  if (release == NULL)
Packit Service 97d2fb
    return errno;
Packit Service 97d2fb
Packit Service 97d2fb
  if (!strcmp (module_name, KERNEL_MODNAME))
Packit Service 97d2fb
    return find_kernel_elf (mod->dwfl, release, file_name);
Packit Service 97d2fb
Packit Service 97d2fb
  /* Do "find /lib/modules/`uname -r` -name MODULE_NAME.ko".  */
Packit Service 97d2fb
Packit Service 97d2fb
  char *modulesdir[] = { NULL, NULL };
Packit Service 97d2fb
  if (asprintf (&modulesdir[0], MODULEDIRFMT, release) < 0)
Packit Service 97d2fb
    return -1;
Packit Service 97d2fb
Packit Service 97d2fb
  FTS *fts = fts_open (modulesdir, FTS_NOSTAT | FTS_LOGICAL, NULL);
Packit Service 97d2fb
  if (fts == NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      free (modulesdir[0]);
Packit Service 97d2fb
      return -1;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  size_t namelen = strlen (module_name);
Packit Service 97d2fb
Packit Service 97d2fb
  /* This is a kludge.  There is no actual necessary relationship between
Packit Service 97d2fb
     the name of the .ko file installed and the module name the kernel
Packit Service 97d2fb
     knows it by when it's loaded.  The kernel's only idea of the module
Packit Service 97d2fb
     name comes from the name embedded in the object's magic
Packit Service 97d2fb
     .gnu.linkonce.this_module section.
Packit Service 97d2fb
Packit Service 97d2fb
     In practice, these module names match the .ko file names except for
Packit Service 97d2fb
     some using '_' and some using '-'.  So our cheap kludge is to look for
Packit Service 97d2fb
     two files when either a '_' or '-' appears in a module name, one using
Packit Service 97d2fb
     only '_' and one only using '-'.  */
Packit Service 97d2fb
Packit Service 97d2fb
  char *alternate_name = malloc (namelen + 1);
Packit Service 97d2fb
  if (unlikely (alternate_name == NULL))
Packit Service 97d2fb
    {
Packit Service 97d2fb
      free (modulesdir[0]);
Packit Service 97d2fb
      return ENOMEM;
Packit Service 97d2fb
    }
Packit Service 97d2fb
  if (!subst_name ('-', '_', module_name, alternate_name, namelen) &&
Packit Service 97d2fb
      !subst_name ('_', '-', module_name, alternate_name, namelen))
Packit Service 97d2fb
    alternate_name[0] = '\0';
Packit Service 97d2fb
Packit Service 97d2fb
  FTSENT *f;
Packit Service 97d2fb
  int error = ENOENT;
Packit Service 97d2fb
  while ((f = fts_read (fts)) != NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      /* Skip a "source" subtree, which tends to be large.
Packit Service 97d2fb
	 This insane hard-coding of names is what depmod does too.  */
Packit Service 97d2fb
      if (f->fts_namelen == sizeof "source" - 1
Packit Service 97d2fb
	  && !strcmp (f->fts_name, "source"))
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  fts_set (fts, f, FTS_SKIP);
Packit Service 97d2fb
	  continue;
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      error = ENOENT;
Packit Service 97d2fb
      switch (f->fts_info)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	case FTS_F:
Packit Service 97d2fb
	case FTS_SL:
Packit Service 97d2fb
	case FTS_NSOK:
Packit Service 97d2fb
	  /* See if this file name is "MODULE_NAME.ko".  */
Packit Service 97d2fb
	  if (check_suffix (f, namelen)
Packit Service 97d2fb
	      && (!memcmp (f->fts_name, module_name, namelen)
Packit Service 97d2fb
		  || !memcmp (f->fts_name, alternate_name, namelen)))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      int fd = open (f->fts_accpath, O_RDONLY);
Packit Service 97d2fb
	      *file_name = strdup (f->fts_path);
Packit Service 97d2fb
	      fts_close (fts);
Packit Service 97d2fb
	      free (modulesdir[0]);
Packit Service 97d2fb
	      free (alternate_name);
Packit Service 97d2fb
	      if (fd < 0)
Packit Service 97d2fb
		free (*file_name);
Packit Service 97d2fb
	      else if (*file_name == NULL)
Packit Service 97d2fb
		{
Packit Service 97d2fb
		  close (fd);
Packit Service 97d2fb
		  fd = -1;
Packit Service 97d2fb
		}
Packit Service 97d2fb
	      return fd;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  break;
Packit Service 97d2fb
Packit Service 97d2fb
	case FTS_ERR:
Packit Service 97d2fb
	case FTS_DNR:
Packit Service 97d2fb
	case FTS_NS:
Packit Service 97d2fb
	  error = f->fts_errno;
Packit Service 97d2fb
	  break;
Packit Service 97d2fb
Packit Service 97d2fb
	case FTS_SLNONE:
Packit Service 97d2fb
	default:
Packit Service 97d2fb
	  break;
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  fts_close (fts);
Packit Service 97d2fb
  free (modulesdir[0]);
Packit Service 97d2fb
  free (alternate_name);
Packit Service 97d2fb
  errno = error;
Packit Service 97d2fb
  return -1;
Packit Service 97d2fb
}
Packit Service 97d2fb
INTDEF (dwfl_linux_kernel_find_elf)
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
/* Dwfl_Callbacks.section_address for kernel modules in the running Linux.
Packit Service 97d2fb
   We read the information from /sys/module directly.  */
Packit Service 97d2fb
Packit Service 97d2fb
int
Packit Service 97d2fb
dwfl_linux_kernel_module_section_address
Packit Service 97d2fb
(Dwfl_Module *mod __attribute__ ((unused)),
Packit Service 97d2fb
 void **userdata __attribute__ ((unused)),
Packit Service 97d2fb
 const char *modname, Dwarf_Addr base __attribute__ ((unused)),
Packit Service 97d2fb
 const char *secname, Elf32_Word shndx __attribute__ ((unused)),
Packit Service 97d2fb
 const GElf_Shdr *shdr __attribute__ ((unused)),
Packit Service 97d2fb
 Dwarf_Addr *addr)
Packit Service 97d2fb
{
Packit Service 97d2fb
  char *sysfile;
Packit Service 97d2fb
  if (asprintf (&sysfile, SECADDRDIRFMT "%s", modname, secname) < 0)
Packit Service 97d2fb
    return DWARF_CB_ABORT;
Packit Service 97d2fb
Packit Service 97d2fb
  FILE *f = fopen (sysfile, "r");
Packit Service 97d2fb
  free (sysfile);
Packit Service 97d2fb
Packit Service 97d2fb
  if (f == NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      if (errno == ENOENT)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  /* The .modinfo and .data.percpu sections are never kept
Packit Service 97d2fb
	     loaded in the kernel.  If the kernel was compiled without
Packit Service 97d2fb
	     CONFIG_MODULE_UNLOAD, the .exit.* sections are not
Packit Service 97d2fb
	     actually loaded at all.
Packit Service 97d2fb
Packit Service 97d2fb
	     Setting *ADDR to -1 tells the caller this section is
Packit Service 97d2fb
	     actually absent from memory.  */
Packit Service 97d2fb
Packit Service 97d2fb
	  if (!strcmp (secname, ".modinfo")
Packit Service 97d2fb
	      || !strcmp (secname, ".data.percpu")
Packit Service 97d2fb
	      || !strncmp (secname, ".exit", 5))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      *addr = (Dwarf_Addr) -1l;
Packit Service 97d2fb
	      return DWARF_CB_OK;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
Packit Service 97d2fb
	  /* The goofy PPC64 module_frob_arch_sections function tweaks
Packit Service 97d2fb
	     the section names as a way to control other kernel code's
Packit Service 97d2fb
	     behavior, and this cruft leaks out into the /sys information.
Packit Service 97d2fb
	     The file name for ".init*" may actually look like "_init*".  */
Packit Service 97d2fb
Packit Service 97d2fb
	  const bool is_init = !strncmp (secname, ".init", 5);
Packit Service 97d2fb
	  if (is_init)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      if (asprintf (&sysfile, SECADDRDIRFMT "_%s",
Packit Service 97d2fb
			    modname, &secname[1]) < 0)
Packit Service 97d2fb
		return ENOMEM;
Packit Service 97d2fb
	      f = fopen (sysfile, "r");
Packit Service 97d2fb
	      free (sysfile);
Packit Service 97d2fb
	      if (f != NULL)
Packit Service 97d2fb
		goto ok;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
Packit Service 97d2fb
	  /* The kernel truncates section names to MODULE_SECT_NAME_LEN - 1.
Packit Service 97d2fb
	     In case that size increases in the future, look for longer
Packit Service 97d2fb
	     truncated names first.  */
Packit Service 97d2fb
	  size_t namelen = strlen (secname);
Packit Service 97d2fb
	  if (namelen >= MODULE_SECT_NAME_LEN)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      int len = asprintf (&sysfile, SECADDRDIRFMT "%s",
Packit Service 97d2fb
				  modname, secname);
Packit Service 97d2fb
	      if (len < 0)
Packit Service 97d2fb
		return DWARF_CB_ABORT;
Packit Service 97d2fb
	      char *end = sysfile + len;
Packit Service 97d2fb
	      do
Packit Service 97d2fb
		{
Packit Service 97d2fb
		  *--end = '\0';
Packit Service 97d2fb
		  f = fopen (sysfile, "r");
Packit Service 97d2fb
		  if (is_init && f == NULL && errno == ENOENT)
Packit Service 97d2fb
		    {
Packit Service 97d2fb
		      sysfile[len - namelen] = '_';
Packit Service 97d2fb
		      f = fopen (sysfile, "r");
Packit Service 97d2fb
		      sysfile[len - namelen] = '.';
Packit Service 97d2fb
		    }
Packit Service 97d2fb
		}
Packit Service 97d2fb
	      while (f == NULL && errno == ENOENT
Packit Service 97d2fb
		     && end - &sysfile[len - namelen] >= MODULE_SECT_NAME_LEN);
Packit Service 97d2fb
	      free (sysfile);
Packit Service 97d2fb
Packit Service 97d2fb
	      if (f != NULL)
Packit Service 97d2fb
		goto ok;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      return DWARF_CB_ABORT;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
 ok:
Packit Service 97d2fb
  (void) __fsetlocking (f, FSETLOCKING_BYCALLER);
Packit Service 97d2fb
Packit Service 97d2fb
  int result = (fscanf (f, "%" PRIx64 "\n", addr) == 1 ? 0
Packit Service 97d2fb
		: ferror_unlocked (f) ? errno : ENOEXEC);
Packit Service 97d2fb
  fclose (f);
Packit Service 97d2fb
Packit Service 97d2fb
  if (result == 0)
Packit Service 97d2fb
    return DWARF_CB_OK;
Packit Service 97d2fb
Packit Service 97d2fb
  errno = result;
Packit Service 97d2fb
  return DWARF_CB_ABORT;
Packit Service 97d2fb
}
Packit Service 97d2fb
INTDEF (dwfl_linux_kernel_module_section_address)
Packit Service 97d2fb
Packit Service 97d2fb
int
Packit Service 97d2fb
dwfl_linux_kernel_report_modules (Dwfl *dwfl)
Packit Service 97d2fb
{
Packit Service 97d2fb
  FILE *f = fopen (MODULELIST, "r");
Packit Service 97d2fb
  if (f == NULL)
Packit Service 97d2fb
    return errno;
Packit Service 97d2fb
Packit Service 97d2fb
  (void) __fsetlocking (f, FSETLOCKING_BYCALLER);
Packit Service 97d2fb
Packit Service 97d2fb
  int result = 0;
Packit Service 97d2fb
  Dwarf_Addr modaddr;
Packit Service 97d2fb
  unsigned long int modsz;
Packit Service 97d2fb
  char modname[128];
Packit Service 97d2fb
  char *line = NULL;
Packit Service 97d2fb
  size_t linesz = 0;
Packit Service 97d2fb
  /* We can't just use fscanf here because it's not easy to distinguish \n
Packit Service 97d2fb
     from other whitespace so as to take the optional word following the
Packit Service 97d2fb
     address but always stop at the end of the line.  */
Packit Service 97d2fb
  while (getline (&line, &linesz, f) > 0
Packit Service 97d2fb
	 && sscanf (line, "%128s %lu %*s %*s %*s %" PRIx64 " %*s\n",
Packit Service 97d2fb
		    modname, &modsz, &modaddr) == 3)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      Dwfl_Module *mod = INTUSE(dwfl_report_module) (dwfl, modname,
Packit Service 97d2fb
						     modaddr, modaddr + modsz);
Packit Service 97d2fb
      if (mod == NULL)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  result = -1;
Packit Service 97d2fb
	  break;
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      result = check_module_notes (mod);
Packit Service 97d2fb
    }
Packit Service 97d2fb
  free (line);
Packit Service 97d2fb
Packit Service 97d2fb
  if (result == 0)
Packit Service 97d2fb
    result = ferror_unlocked (f) ? errno : feof_unlocked (f) ? 0 : ENOEXEC;
Packit Service 97d2fb
Packit Service 97d2fb
  fclose (f);
Packit Service 97d2fb
Packit Service 97d2fb
  return result;
Packit Service 97d2fb
}
Packit Service 97d2fb
INTDEF (dwfl_linux_kernel_report_modules)