Blame libdwfl/linux-kernel-modules.c

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