LCOV - code coverage report
Current view: top level - libdwfl - linux-kernel-modules.c (source / functions) Hit Total Coverage
Test: elfutils-0.181 Lines: 0 413 0.0 %
Date: 2020-09-08 14:07:57 Functions: 0 16 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* Standard libdwfl callbacks for debugging the running Linux kernel.
       2             :    Copyright (C) 2005-2011, 2013, 2014, 2015 Red Hat, Inc.
       3             :    This file is part of elfutils.
       4             : 
       5             :    This file is free software; you can redistribute it and/or modify
       6             :    it under the terms of either
       7             : 
       8             :      * the GNU Lesser General Public License as published by the Free
       9             :        Software Foundation; either version 3 of the License, or (at
      10             :        your option) any later version
      11             : 
      12             :    or
      13             : 
      14             :      * the GNU General Public License as published by the Free
      15             :        Software Foundation; either version 2 of the License, or (at
      16             :        your option) any later version
      17             : 
      18             :    or both in parallel, as here.
      19             : 
      20             :    elfutils is distributed in the hope that it will be useful, but
      21             :    WITHOUT ANY WARRANTY; without even the implied warranty of
      22             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      23             :    General Public License for more details.
      24             : 
      25             :    You should have received copies of the GNU General Public License and
      26             :    the GNU Lesser General Public License along with this program.  If
      27             :    not, see <http://www.gnu.org/licenses/>.  */
      28             : 
      29             : /* In case we have a bad fts we include this before config.h because it
      30             :    can't handle _FILE_OFFSET_BITS.
      31             :    Everything we need here is fine if its declarations just come first.
      32             :    Also, include sys/types.h before fts. On some systems fts.h is not self
      33             :    contained. */
      34             : #ifdef BAD_FTS
      35             :   #include <sys/types.h>
      36             :   #include <fts.h>
      37             : #endif
      38             : 
      39             : #include <config.h>
      40             : #include <system.h>
      41             : 
      42             : #include "libelfP.h"
      43             : #include "libdwflP.h"
      44             : #include <inttypes.h>
      45             : #include <errno.h>
      46             : #include <stdio.h>
      47             : #include <stdio_ext.h>
      48             : #include <string.h>
      49             : #include <stdlib.h>
      50             : #include <sys/utsname.h>
      51             : #include <fcntl.h>
      52             : #include <unistd.h>
      53             : 
      54             : /* If fts.h is included before config.h, its indirect inclusions may not
      55             :    give us the right LFS aliases of these functions, so map them manually.  */
      56             : #ifdef BAD_FTS
      57             :   #ifdef _FILE_OFFSET_BITS
      58             :     #define open open64
      59             :     #define fopen fopen64
      60             :   #endif
      61             : #else
      62             :   #include <sys/types.h>
      63             :   #include <fts.h>
      64             : #endif
      65             : 
      66             : 
      67             : #define KERNEL_MODNAME  "kernel"
      68             : 
      69             : #define MODULEDIRFMT    "/lib/modules/%s"
      70             : 
      71             : #define KNOTESFILE      "/sys/kernel/notes"
      72             : #define MODNOTESFMT     "/sys/module/%s/notes"
      73             : #define KSYMSFILE       "/proc/kallsyms"
      74             : #define MODULELIST      "/proc/modules"
      75             : #define SECADDRDIRFMT   "/sys/module/%s/sections/"
      76             : #define MODULE_SECT_NAME_LEN 32 /* Minimum any linux/module.h has had.  */
      77             : 
      78             : 
      79             : static const char *vmlinux_suffixes[] =
      80             :   {
      81             :     ".gz",
      82             : #ifdef USE_BZLIB
      83             :     ".bz2",
      84             : #endif
      85             : #ifdef USE_LZMA
      86             :     ".xz",
      87             : #endif
      88             :   };
      89             : 
      90             : /* Try to open the given file as it is or under the debuginfo directory.  */
      91             : static int
      92           0 : try_kernel_name (Dwfl *dwfl, char **fname, bool try_debug)
      93             : {
      94           0 :   if (*fname == NULL)
      95             :     return -1;
      96             : 
      97             :   /* Don't bother trying *FNAME itself here if the path will cause it to be
      98             :      tried because we give its own basename as DEBUGLINK_FILE.  */
      99           0 :   int fd = ((((dwfl->callbacks->debuginfo_path
     100           0 :                ? *dwfl->callbacks->debuginfo_path : NULL)
     101           0 :               ?: DEFAULT_DEBUGINFO_PATH)[0] == ':') ? -1
     102           0 :             : TEMP_FAILURE_RETRY (open (*fname, O_RDONLY)));
     103             : 
     104           0 :   if (fd < 0)
     105             :     {
     106           0 :       Dwfl_Module fakemod = { .dwfl = dwfl };
     107             : 
     108           0 :       if (try_debug)
     109             :         /* Passing NULL for DEBUGLINK_FILE searches for both the basenamer
     110             :            "vmlinux" and the default of basename + ".debug", to look for
     111             :            "vmlinux.debug" files.  */
     112           0 :         fd = INTUSE(dwfl_standard_find_debuginfo) (&fakemod, NULL, NULL, 0,
     113             :                                                    *fname, NULL, 0,
     114             :                                                    &fakemod.debug.name);
     115             :       else
     116             :         /* Try the file's unadorned basename as DEBUGLINK_FILE,
     117             :            to look only for "vmlinux" files.  */
     118           0 :         fd = INTUSE(dwfl_standard_find_debuginfo) (&fakemod, NULL, NULL, 0,
     119           0 :                                                    *fname, basename (*fname),
     120             :                                                    0, &fakemod.debug.name);
     121             : 
     122           0 :       if (fakemod.debug.name != NULL)
     123             :         {
     124           0 :           free (*fname);
     125           0 :           *fname = fakemod.debug.name;
     126             :         }
     127             :     }
     128             : 
     129           0 :   if (fd < 0)
     130           0 :     for (size_t i = 0;
     131           0 :          i < sizeof vmlinux_suffixes / sizeof vmlinux_suffixes[0] && fd < 0;
     132           0 :          ++i)
     133             :       {
     134           0 :         char *zname;
     135           0 :         if (asprintf (&zname, "%s%s", *fname, vmlinux_suffixes[i]) > 0)
     136             :           {
     137           0 :             fd = TEMP_FAILURE_RETRY (open (zname, O_RDONLY));
     138           0 :             if (fd < 0)
     139           0 :               free (zname);
     140             :             else
     141             :               {
     142           0 :                 free (*fname);
     143           0 :                 *fname = zname;
     144             :               }
     145             :           }
     146             :       }
     147             : 
     148           0 :   if (fd < 0)
     149             :     {
     150           0 :       free (*fname);
     151           0 :       *fname = NULL;
     152             :     }
     153             : 
     154             :   return fd;
     155             : }
     156             : 
     157             : static inline const char *
     158             : kernel_release (void)
     159             : {
     160             : #ifdef __linux__
     161             :   /* Cache the `uname -r` string we'll use.  */
     162           0 :   static struct utsname utsname;
     163           0 :   if (utsname.release[0] == '\0' && uname (&utsname) != 0)
     164           0 :     return NULL;
     165             :   return utsname.release;
     166             : #else
     167             :   /* Used for finding the running linux kernel, which isn't supported
     168             :      on non-linux kernel systems.  */
     169             :   errno = ENOTSUP;
     170             :   return NULL;
     171             : #endif
     172             : }
     173             : 
     174             : static int
     175           0 : find_kernel_elf (Dwfl *dwfl, const char *release, char **fname)
     176             : {
     177             :   /* First try to find an uncompressed vmlinux image.  Possibly
     178             :      including debuginfo.  */
     179           0 :   if (release == NULL
     180           0 :       || ((release[0] == '/'
     181           0 :            ? asprintf (fname, "%s/vmlinux", release)
     182           0 :            : asprintf (fname, "/boot/vmlinux-%s", release)) < 0))
     183           0 :     return -1;
     184             : 
     185           0 :   int fd = try_kernel_name (dwfl, fname, true);
     186           0 :   if (fd < 0 && release[0] != '/')
     187             :     {
     188           0 :       free (*fname);
     189           0 :       if (asprintf (fname, MODULEDIRFMT "/vmlinux", release) < 0)
     190             :         return -1;
     191           0 :       fd = try_kernel_name (dwfl, fname, true);
     192             :     }
     193             : 
     194             :   /* There might be a compressed vmlinuz image.  Probably without
     195             :      debuginfo, but try to find it under the debug path also, just in
     196             :      case.  */
     197           0 :   if (fd < 0)
     198             :     {
     199           0 :       free (*fname);
     200           0 :       if ((release[0] == '/'
     201           0 :            ? asprintf (fname, "%s/vmlinuz", release)
     202           0 :            : asprintf (fname, "/boot/vmlinuz-%s", release)) < 0)
     203             :         return -1;
     204             : 
     205           0 :       fd = try_kernel_name (dwfl, fname, true);
     206           0 :       if (fd < 0 && release[0] != '/')
     207             :         {
     208           0 :           free (*fname);
     209           0 :           if (asprintf (fname, MODULEDIRFMT "/vmlinuz", release) < 0)
     210             :             return -1;
     211           0 :           fd = try_kernel_name (dwfl, fname, true);
     212             :         }
     213             :     }
     214             : 
     215             :   return fd;
     216             : }
     217             : 
     218             : static int
     219           0 : get_release (Dwfl *dwfl, const char **release)
     220             : {
     221           0 :   if (dwfl == NULL)
     222             :     return -1;
     223             : 
     224           0 :   const char *release_string = release == NULL ? NULL : *release;
     225           0 :   if (release_string == NULL)
     226             :     {
     227           0 :       release_string = kernel_release ();
     228           0 :       if (release_string == NULL)
     229           0 :         return errno;
     230           0 :       if (release != NULL)
     231           0 :         *release = release_string;
     232             :     }
     233             : 
     234             :   return 0;
     235             : }
     236             : 
     237             : static int
     238           0 : report_kernel (Dwfl *dwfl, const char **release,
     239             :                int (*predicate) (const char *module, const char *file))
     240             : {
     241           0 :   int result = get_release (dwfl, release);
     242           0 :   if (unlikely (result != 0))
     243             :     return result;
     244             : 
     245           0 :   if (release == NULL || *release == NULL)
     246             :     return EINVAL;
     247             : 
     248           0 :   char *fname;
     249           0 :   int fd = find_kernel_elf (dwfl, *release, &fname);
     250             : 
     251           0 :   if (fd < 0)
     252           0 :     result = ((predicate != NULL && !(*predicate) (KERNEL_MODNAME, NULL))
     253           0 :               ? 0 : errno ?: ENOENT);
     254             :   else
     255             :     {
     256           0 :       bool report = true;
     257             : 
     258           0 :       if (predicate != NULL)
     259             :         {
     260             :           /* Let the predicate decide whether to use this one.  */
     261           0 :           int want = (*predicate) (KERNEL_MODNAME, fname);
     262           0 :           if (want < 0)
     263           0 :             result = errno;
     264           0 :           report = want > 0;
     265             :         }
     266             : 
     267           0 :       if (report)
     268             :         {
     269             :           /* Note that on some architectures (e.g. x86_64) the vmlinux
     270             :              is ET_EXEC, while on others (e.g. ppc64) it is ET_DYN.
     271             :              In both cases the phdr p_vaddr load address will be non-zero.
     272             :              We want the image to be placed as if it was ET_DYN, so
     273             :              pass true for add_p_vaddr which will do the right thing
     274             :              (in combination with a zero base) in either case.  */
     275           0 :           Dwfl_Module *mod = INTUSE(dwfl_report_elf) (dwfl, KERNEL_MODNAME,
     276             :                                                       fname, fd, 0, true);
     277           0 :           if (mod == NULL)
     278             :             result = -1;
     279             :           else
     280             :             /* The kernel is ET_EXEC, but always treat it as relocatable.  */
     281           0 :             mod->e_type = ET_DYN;
     282             :         }
     283             : 
     284           0 :       free (fname);
     285             : 
     286           0 :       if (!report || result < 0)
     287           0 :         close (fd);
     288             :     }
     289             : 
     290             :   return result;
     291             : }
     292             : 
     293             : /* Look for a kernel debug archive.  If we find one, report all its modules.
     294             :    If not, return ENOENT.  */
     295             : static int
     296           0 : report_kernel_archive (Dwfl *dwfl, const char **release,
     297             :                        int (*predicate) (const char *module, const char *file))
     298             : {
     299           0 :   int result = get_release (dwfl, release);
     300           0 :   if (unlikely (result != 0))
     301             :     return result;
     302             : 
     303           0 :   if (release == NULL || *release == NULL)
     304             :     return EINVAL;
     305             : 
     306           0 :   char *archive;
     307           0 :   int res = (((*release)[0] == '/')
     308           0 :              ? asprintf (&archive, "%s/debug.a", *release)
     309           0 :              : asprintf (&archive, MODULEDIRFMT "/debug.a", *release));
     310           0 :   if (unlikely (res < 0))
     311             :     return ENOMEM;
     312             : 
     313           0 :   int fd = try_kernel_name (dwfl, &archive, false);
     314           0 :   if (fd < 0)
     315           0 :     result = errno ?: ENOENT;
     316             :   else
     317             :     {
     318             :       /* We have the archive file open!  */
     319           0 :       Dwfl_Module *last = __libdwfl_report_offline (dwfl, NULL, archive, fd,
     320             :                                                     true, predicate);
     321           0 :       if (unlikely (last == NULL))
     322             :         result = -1;
     323             :       else
     324             :         {
     325             :           /* Find the kernel and move it to the head of the list.  */
     326           0 :           Dwfl_Module **tailp = &dwfl->modulelist, **prevp = tailp;
     327           0 :           for (Dwfl_Module *m = *prevp; m != NULL; m = *(prevp = &m->next))
     328           0 :             if (!m->gc && m->e_type != ET_REL && !strcmp (m->name, "kernel"))
     329             :               {
     330           0 :                 *prevp = m->next;
     331           0 :                 m->next = *tailp;
     332           0 :                 *tailp = m;
     333           0 :                 break;
     334             :               }
     335             :         }
     336             :     }
     337             : 
     338           0 :   free (archive);
     339           0 :   return result;
     340             : }
     341             : 
     342             : static size_t
     343           0 : check_suffix (const FTSENT *f, size_t namelen)
     344             : {
     345             : #define TRY(sfx)                                                        \
     346             :   if ((namelen ? f->fts_namelen == namelen + sizeof sfx - 1          \
     347             :        : f->fts_namelen >= sizeof sfx)                                    \
     348             :       && !memcmp (f->fts_name + f->fts_namelen - (sizeof sfx - 1),        \
     349             :                   sfx, sizeof sfx))                                     \
     350             :     return sizeof sfx - 1
     351             : 
     352           0 :   TRY (".ko");
     353           0 :   TRY (".ko.gz");
     354             : #if USE_BZLIB
     355           0 :   TRY (".ko.bz2");
     356             : #endif
     357             : #if USE_LZMA
     358           0 :   TRY (".ko.xz");
     359             : #endif
     360             : 
     361             :   return 0;
     362             : 
     363             : #undef  TRY
     364             : }
     365             : 
     366             : /* Report a kernel and all its modules found on disk, for offline use.
     367             :    If RELEASE starts with '/', it names a directory to look in;
     368             :    if not, it names a directory to find under /lib/modules/;
     369             :    if null, /lib/modules/`uname -r` is used.
     370             :    Returns zero on success, -1 if dwfl_report_module failed,
     371             :    or an errno code if finding the files on disk failed.  */
     372             : int
     373           0 : dwfl_linux_kernel_report_offline (Dwfl *dwfl, const char *release,
     374             :                                   int (*predicate) (const char *module,
     375             :                                                     const char *file))
     376             : {
     377           0 :   int result = report_kernel_archive (dwfl, &release, predicate);
     378           0 :   if (result != ENOENT)
     379             :     return result;
     380             : 
     381             :   /* First report the kernel.  */
     382           0 :   result = report_kernel (dwfl, &release, predicate);
     383           0 :   if (result == 0)
     384             :     {
     385             :       /* Do "find /lib/modules/RELEASE -name *.ko".  */
     386             : 
     387           0 :       char *modulesdir[] = { NULL, NULL };
     388           0 :       if (release[0] == '/')
     389           0 :         modulesdir[0] = (char *) release;
     390             :       else
     391             :         {
     392           0 :           if (asprintf (&modulesdir[0], MODULEDIRFMT, release) < 0)
     393           0 :             return errno;
     394             :         }
     395             : 
     396           0 :       FTS *fts = fts_open (modulesdir, FTS_NOSTAT | FTS_LOGICAL, NULL);
     397           0 :       if (modulesdir[0] == (char *) release)
     398           0 :         modulesdir[0] = NULL;
     399           0 :       if (fts == NULL)
     400             :         {
     401           0 :           free (modulesdir[0]);
     402           0 :           return errno;
     403             :         }
     404             : 
     405             :       FTSENT *f;
     406           0 :       while ((f = fts_read (fts)) != NULL)
     407             :         {
     408             :           /* Skip a "source" subtree, which tends to be large.
     409             :              This insane hard-coding of names is what depmod does too.  */
     410           0 :           if (f->fts_namelen == sizeof "source" - 1
     411           0 :               && !strcmp (f->fts_name, "source"))
     412             :             {
     413           0 :               fts_set (fts, f, FTS_SKIP);
     414           0 :               continue;
     415             :             }
     416             : 
     417           0 :           switch (f->fts_info)
     418             :             {
     419           0 :             case FTS_F:
     420             :             case FTS_SL:
     421           0 :             case FTS_NSOK:;
     422             :               /* See if this file name matches "*.ko".  */
     423           0 :               const size_t suffix = check_suffix (f, 0);
     424           0 :               if (suffix)
     425             :                 {
     426             :                   /* We have a .ko file to report.  Following the algorithm
     427             :                      by which the kernel makefiles set KBUILD_MODNAME, we
     428             :                      replace all ',' or '-' with '_' in the file name and
     429             :                      call that the module name.  Modules could well be
     430             :                      built using different embedded names than their file
     431             :                      names.  To handle that, we would have to look at the
     432             :                      __this_module.name contents in the module's text.  */
     433             : 
     434           0 :                   char *name = strndup (f->fts_name, f->fts_namelen - suffix);
     435           0 :                   if (unlikely (name == NULL))
     436             :                     {
     437           0 :                       __libdwfl_seterrno (DWFL_E_NOMEM);
     438           0 :                       result = -1;
     439           0 :                       break;
     440             :                     }
     441           0 :                   for (size_t i = 0; i < f->fts_namelen - suffix; ++i)
     442           0 :                     if (name[i] == '-' || name[i] == ',')
     443           0 :                       name[i] = '_';
     444             : 
     445           0 :                   if (predicate != NULL)
     446             :                     {
     447             :                       /* Let the predicate decide whether to use this one.  */
     448           0 :                       int want = (*predicate) (name, f->fts_path);
     449           0 :                       if (want < 0)
     450             :                         {
     451           0 :                           result = -1;
     452           0 :                           free (name);
     453           0 :                           break;
     454             :                         }
     455           0 :                       if (!want)
     456             :                         {
     457           0 :                           free (name);
     458           0 :                           continue;
     459             :                         }
     460             :                     }
     461             : 
     462           0 :                   if (dwfl_report_offline (dwfl, name, f->fts_path, -1) == NULL)
     463             :                     {
     464           0 :                       free (name);
     465           0 :                       result = -1;
     466           0 :                       break;
     467             :                     }
     468           0 :                   free (name);
     469             :                 }
     470           0 :               continue;
     471             : 
     472           0 :             case FTS_ERR:
     473             :             case FTS_DNR:
     474             :             case FTS_NS:
     475           0 :               result = f->fts_errno;
     476           0 :               break;
     477             : 
     478             :             case FTS_SLNONE:
     479             :             default:
     480             :               continue;
     481             :             }
     482             : 
     483             :           /* We only get here in error cases.  */
     484             :           break;
     485             :         }
     486           0 :       fts_close (fts);
     487           0 :       free (modulesdir[0]);
     488             :     }
     489             : 
     490             :   return result;
     491             : }
     492             : INTDEF (dwfl_linux_kernel_report_offline)
     493             : 
     494             : 
     495             : /* State of read_address used by intuit_kernel_bounds. */
     496             : struct read_address_state {
     497             :   FILE *f;
     498             :   char *line;
     499             :   size_t linesz;
     500             :   size_t n;
     501             :   char *p;
     502             :   const char *type;
     503             : };
     504             : 
     505             : static inline bool
     506           0 : read_address (struct read_address_state *state, Dwarf_Addr *addr)
     507             : {
     508           0 :   if ((state->n = getline (&state->line, &state->linesz, state->f)) < 1 ||
     509           0 :       state->line[state->n - 2] == ']')
     510             :     return false;
     511           0 :   *addr = strtoull (state->line, &state->p, 16);
     512           0 :   state->p += strspn (state->p, " \t");
     513           0 :   state->type = strsep (&state->p, " \t\n");
     514           0 :   if (state->type == NULL)
     515             :     return false;
     516           0 :   return state->p != NULL && state->p != state->line;
     517             : }
     518             : 
     519             : 
     520             : /* Grovel around to guess the bounds of the runtime kernel image.  */
     521             : static int
     522           0 : intuit_kernel_bounds (Dwarf_Addr *start, Dwarf_Addr *end, Dwarf_Addr *notes)
     523             : {
     524           0 :   struct read_address_state state = { NULL, NULL, 0, 0, NULL, NULL };
     525             : 
     526           0 :   *notes = 0;
     527             : 
     528           0 :   state.f = fopen (KSYMSFILE, "r");
     529           0 :   if (state.f == NULL)
     530           0 :     return errno;
     531             : 
     532           0 :   (void) __fsetlocking (state.f, FSETLOCKING_BYCALLER);
     533             : 
     534           0 :   int result;
     535           0 :   do
     536           0 :     result = read_address (&state, start) ? 0 : -1;
     537           0 :   while (result == 0 && strchr ("TtRr", *state.type) == NULL);
     538             : 
     539           0 :   if (result == 0)
     540             :     {
     541           0 :       Dwarf_Addr addr;
     542           0 :       *end = *start;
     543           0 :       while (read_address (&state, &addr) && addr >= *end)
     544             :         {
     545           0 :           *end = addr;
     546           0 :           if (*notes == 0 && !strcmp (state.p, "__start_notes\n"))
     547           0 :             *notes = *end;
     548             :         }
     549             : 
     550           0 :       Dwarf_Addr round_kernel = sysconf (_SC_PAGESIZE);
     551           0 :       *start &= -(Dwarf_Addr) round_kernel;
     552           0 :       *end += round_kernel - 1;
     553           0 :       *end &= -(Dwarf_Addr) round_kernel;
     554           0 :       if (*start >= *end || *end - *start < round_kernel)
     555           0 :         result = -1;
     556             :     }
     557           0 :   free (state.line);
     558             : 
     559           0 :   if (result == -1)
     560           0 :     result = ferror_unlocked (state.f) ? errno : ENOEXEC;
     561             : 
     562           0 :   fclose (state.f);
     563             : 
     564           0 :   return result;
     565             : }
     566             : 
     567             : 
     568             : /* Look for a build ID note in NOTESFILE and associate the ID with MOD.  */
     569             : static int
     570           0 : check_notes (Dwfl_Module *mod, const char *notesfile,
     571             :              Dwarf_Addr vaddr, const char *secname)
     572             : {
     573           0 :   int fd = open (notesfile, O_RDONLY);
     574           0 :   if (fd < 0)
     575             :     return 1;
     576             : 
     577           0 :   assert (sizeof (Elf32_Nhdr) == sizeof (GElf_Nhdr));
     578           0 :   assert (sizeof (Elf64_Nhdr) == sizeof (GElf_Nhdr));
     579           0 :   union
     580             :   {
     581             :     GElf_Nhdr nhdr;
     582             :     unsigned char data[8192];
     583             :   } buf;
     584             : 
     585           0 :   ssize_t n = read (fd, buf.data, sizeof buf);
     586           0 :   close (fd);
     587             : 
     588           0 :   if (n <= 0)
     589             :     return 1;
     590             : 
     591             :   unsigned char *p = buf.data;
     592             :   size_t len = 0;
     593           0 :   while (p < &buf.data[n])
     594             :     {
     595             :       /* No translation required since we are reading the native kernel.  */
     596           0 :       GElf_Nhdr *nhdr = (void *) p;
     597           0 :       len += sizeof *nhdr;
     598           0 :       p += len;
     599           0 :       unsigned char *name = p;
     600           0 :       unsigned char *bits;
     601             :       /* This is somewhat ugly, GNU Property notes use different padding,
     602             :          but all we have is the file content, so we have to actually check
     603             :          the name and type.  */
     604           0 :       if (nhdr->n_type == NT_GNU_PROPERTY_TYPE_0
     605           0 :           && nhdr->n_namesz == sizeof "GNU"
     606           0 :           && name + nhdr->n_namesz < &buf.data[n]
     607           0 :           && !memcmp (name, "GNU", sizeof "GNU"))
     608             :         {
     609           0 :           len += nhdr->n_namesz;
     610           0 :           len = NOTE_ALIGN8 (len);
     611           0 :           p = buf.data + len;
     612           0 :           bits = p;
     613           0 :           len += nhdr->n_descsz;
     614           0 :           len = NOTE_ALIGN8 (len);
     615           0 :           p = buf.data + len;
     616             :         }
     617             :       else
     618             :         {
     619           0 :           len += nhdr->n_namesz;
     620           0 :           len = NOTE_ALIGN4 (len);
     621           0 :           p = buf.data + len;
     622           0 :           bits = p;
     623           0 :           len += nhdr->n_descsz;
     624           0 :           len = NOTE_ALIGN4 (len);
     625           0 :           p = buf.data + len;
     626             :         }
     627             : 
     628           0 :       if (p <= &buf.data[n]
     629           0 :           && nhdr->n_type == NT_GNU_BUILD_ID
     630           0 :           && nhdr->n_namesz == sizeof "GNU"
     631           0 :           && !memcmp (name, "GNU", sizeof "GNU"))
     632             :         {
     633             :           /* Found it.  For a module we must figure out its VADDR now.  */
     634             : 
     635           0 :           if (secname != NULL
     636           0 :               && (INTUSE(dwfl_linux_kernel_module_section_address)
     637           0 :                   (mod, NULL, mod->name, 0, secname, 0, NULL, &vaddr) != 0
     638           0 :                   || vaddr == (GElf_Addr) -1l))
     639           0 :             vaddr = 0;
     640             : 
     641           0 :           if (vaddr != 0)
     642           0 :             vaddr += bits - buf.data;
     643           0 :           return INTUSE(dwfl_module_report_build_id) (mod, bits,
     644           0 :                                                       nhdr->n_descsz, vaddr);
     645             :         }
     646             :     }
     647             : 
     648             :   return 0;
     649             : }
     650             : 
     651             : /* Look for a build ID for the kernel.  */
     652             : static int
     653             : check_kernel_notes (Dwfl_Module *kernelmod, GElf_Addr vaddr)
     654             : {
     655           0 :   return check_notes (kernelmod, KNOTESFILE, vaddr, NULL) < 0 ? -1 : 0;
     656             : }
     657             : 
     658             : /* Look for a build ID for a loaded kernel module.  */
     659             : static int
     660           0 : check_module_notes (Dwfl_Module *mod)
     661             : {
     662           0 :   char *dirs[2] = { NULL, NULL };
     663           0 :   if (asprintf (&dirs[0], MODNOTESFMT, mod->name) < 0)
     664             :     return ENOMEM;
     665             : 
     666           0 :   FTS *fts = fts_open (dirs, FTS_NOSTAT | FTS_LOGICAL, NULL);
     667           0 :   if (fts == NULL)
     668             :     {
     669           0 :       free (dirs[0]);
     670           0 :       return 0;
     671             :     }
     672             : 
     673             :   int result = 0;
     674             :   FTSENT *f;
     675           0 :   while ((f = fts_read (fts)) != NULL)
     676             :     {
     677           0 :       switch (f->fts_info)
     678             :         {
     679           0 :         case FTS_F:
     680             :         case FTS_SL:
     681             :         case FTS_NSOK:
     682           0 :           result = check_notes (mod, f->fts_accpath, 0, f->fts_name);
     683           0 :           if (result > 0)    /* Nothing found.  */
     684             :             {
     685             :               result = 0;
     686             :               continue;
     687             :             }
     688             :           break;
     689             : 
     690           0 :         case FTS_ERR:
     691             :         case FTS_DNR:
     692           0 :           result = f->fts_errno;
     693           0 :           break;
     694             : 
     695             :         case FTS_NS:
     696             :         case FTS_SLNONE:
     697             :         default:
     698             :           continue;
     699             :         }
     700             : 
     701             :       /* We only get here when finished or in error cases.  */
     702             :       break;
     703             :     }
     704           0 :   fts_close (fts);
     705           0 :   free (dirs[0]);
     706             : 
     707           0 :   return result;
     708             : }
     709             : 
     710             : int
     711           0 : dwfl_linux_kernel_report_kernel (Dwfl *dwfl)
     712             : {
     713           0 :   Dwarf_Addr start = 0;
     714           0 :   Dwarf_Addr end = 0;
     715             : 
     716             :   #define report() \
     717             :     (INTUSE(dwfl_report_module) (dwfl, KERNEL_MODNAME, start, end))
     718             : 
     719             :   /* This is a bit of a kludge.  If we already reported the kernel,
     720             :      don't bother figuring it out again--it never changes.  */
     721           0 :   for (Dwfl_Module *m = dwfl->modulelist; m != NULL; m = m->next)
     722           0 :     if (!strcmp (m->name, KERNEL_MODNAME))
     723             :       {
     724           0 :         start = m->low_addr;
     725           0 :         end = m->high_addr;
     726           0 :         return report () == NULL ? -1 : 0;
     727             :       }
     728             : 
     729             :   /* Try to figure out the bounds of the kernel image without
     730             :      looking for any vmlinux file.  */
     731           0 :   Dwarf_Addr notes;
     732           0 :   int result = intuit_kernel_bounds (&start, &end, &notes);
     733           0 :   if (result == 0)
     734             :     {
     735           0 :       Dwfl_Module *mod = report ();
     736           0 :       return unlikely (mod == NULL) ? -1 : check_kernel_notes (mod, notes);
     737             :     }
     738           0 :   if (result != ENOENT)
     739             :     return result;
     740             : 
     741             :   /* Find the ELF file for the running kernel and dwfl_report_elf it.  */
     742           0 :   return report_kernel (dwfl, NULL, NULL);
     743             : }
     744             : INTDEF (dwfl_linux_kernel_report_kernel)
     745             : 
     746             : 
     747             : static inline bool
     748           0 : subst_name (char from, char to,
     749             :             const char * const module_name,
     750             :             char * const alternate_name,
     751             :             const size_t namelen)
     752             : {
     753           0 :   const char *n = memchr (module_name, from, namelen);
     754           0 :   if (n == NULL)
     755             :     return false;
     756           0 :   char *a = mempcpy (alternate_name, module_name, n - module_name);
     757           0 :   *a++ = to;
     758           0 :   ++n;
     759           0 :   const char *p;
     760           0 :   while ((p = memchr (n, from, namelen - (n - module_name))) != NULL)
     761             :     {
     762           0 :       a = mempcpy (a, n, p - n);
     763           0 :       *a++ = to;
     764           0 :       n = p + 1;
     765             :     }
     766           0 :   memcpy (a, n, namelen - (n - module_name) + 1);
     767           0 :   return true;
     768             : }
     769             : 
     770             : /* Dwfl_Callbacks.find_elf for the running Linux kernel and its modules.  */
     771             : 
     772             : int
     773           0 : dwfl_linux_kernel_find_elf (Dwfl_Module *mod,
     774             :                             void **userdata __attribute__ ((unused)),
     775             :                             const char *module_name,
     776             :                             Dwarf_Addr base __attribute__ ((unused)),
     777             :                             char **file_name, Elf **elfp)
     778             : {
     779           0 :   if (mod->build_id_len > 0)
     780             :     {
     781           0 :       int fd = INTUSE(dwfl_build_id_find_elf) (mod, NULL, NULL, 0,
     782             :                                                file_name, elfp);
     783           0 :       if (fd >= 0 || mod->main.elf != NULL || errno != 0)
     784             :         return fd;
     785             :     }
     786             : 
     787           0 :   const char *release = kernel_release ();
     788           0 :   if (release == NULL)
     789           0 :     return errno;
     790             : 
     791           0 :   if (!strcmp (module_name, KERNEL_MODNAME))
     792           0 :     return find_kernel_elf (mod->dwfl, release, file_name);
     793             : 
     794             :   /* Do "find /lib/modules/`uname -r` -name MODULE_NAME.ko".  */
     795             : 
     796           0 :   char *modulesdir[] = { NULL, NULL };
     797           0 :   if (asprintf (&modulesdir[0], MODULEDIRFMT, release) < 0)
     798             :     return -1;
     799             : 
     800           0 :   FTS *fts = fts_open (modulesdir, FTS_NOSTAT | FTS_LOGICAL, NULL);
     801           0 :   if (fts == NULL)
     802             :     {
     803           0 :       free (modulesdir[0]);
     804           0 :       return -1;
     805             :     }
     806             : 
     807           0 :   size_t namelen = strlen (module_name);
     808             : 
     809             :   /* This is a kludge.  There is no actual necessary relationship between
     810             :      the name of the .ko file installed and the module name the kernel
     811             :      knows it by when it's loaded.  The kernel's only idea of the module
     812             :      name comes from the name embedded in the object's magic
     813             :      .gnu.linkonce.this_module section.
     814             : 
     815             :      In practice, these module names match the .ko file names except for
     816             :      some using '_' and some using '-'.  So our cheap kludge is to look for
     817             :      two files when either a '_' or '-' appears in a module name, one using
     818             :      only '_' and one only using '-'.  */
     819             : 
     820           0 :   char *alternate_name = malloc (namelen + 1);
     821           0 :   if (unlikely (alternate_name == NULL))
     822             :     {
     823           0 :       free (modulesdir[0]);
     824           0 :       return ENOMEM;
     825             :     }
     826           0 :   if (!subst_name ('-', '_', module_name, alternate_name, namelen) &&
     827           0 :       !subst_name ('_', '-', module_name, alternate_name, namelen))
     828           0 :     alternate_name[0] = '\0';
     829             : 
     830             :   FTSENT *f;
     831             :   int error = ENOENT;
     832           0 :   while ((f = fts_read (fts)) != NULL)
     833             :     {
     834             :       /* Skip a "source" subtree, which tends to be large.
     835             :          This insane hard-coding of names is what depmod does too.  */
     836           0 :       if (f->fts_namelen == sizeof "source" - 1
     837           0 :           && !strcmp (f->fts_name, "source"))
     838             :         {
     839           0 :           fts_set (fts, f, FTS_SKIP);
     840           0 :           continue;
     841             :         }
     842             : 
     843           0 :       error = ENOENT;
     844           0 :       switch (f->fts_info)
     845             :         {
     846           0 :         case FTS_F:
     847             :         case FTS_SL:
     848             :         case FTS_NSOK:
     849             :           /* See if this file name is "MODULE_NAME.ko".  */
     850           0 :           if (check_suffix (f, namelen)
     851           0 :               && (!memcmp (f->fts_name, module_name, namelen)
     852           0 :                   || !memcmp (f->fts_name, alternate_name, namelen)))
     853             :             {
     854           0 :               int fd = open (f->fts_accpath, O_RDONLY);
     855           0 :               *file_name = strdup (f->fts_path);
     856           0 :               fts_close (fts);
     857           0 :               free (modulesdir[0]);
     858           0 :               free (alternate_name);
     859           0 :               if (fd < 0)
     860           0 :                 free (*file_name);
     861           0 :               else if (*file_name == NULL)
     862             :                 {
     863           0 :                   close (fd);
     864           0 :                   fd = -1;
     865             :                 }
     866           0 :               return fd;
     867             :             }
     868             :           break;
     869             : 
     870           0 :         case FTS_ERR:
     871             :         case FTS_DNR:
     872             :         case FTS_NS:
     873           0 :           error = f->fts_errno;
     874           0 :           break;
     875             : 
     876             :         case FTS_SLNONE:
     877             :         default:
     878             :           break;
     879             :         }
     880             :     }
     881             : 
     882           0 :   fts_close (fts);
     883           0 :   free (modulesdir[0]);
     884           0 :   free (alternate_name);
     885           0 :   errno = error;
     886           0 :   return -1;
     887             : }
     888             : INTDEF (dwfl_linux_kernel_find_elf)
     889             : 
     890             : 
     891             : /* Dwfl_Callbacks.section_address for kernel modules in the running Linux.
     892             :    We read the information from /sys/module directly.  */
     893             : 
     894             : int
     895           0 : dwfl_linux_kernel_module_section_address
     896             : (Dwfl_Module *mod __attribute__ ((unused)),
     897             :  void **userdata __attribute__ ((unused)),
     898             :  const char *modname, Dwarf_Addr base __attribute__ ((unused)),
     899             :  const char *secname, Elf32_Word shndx __attribute__ ((unused)),
     900             :  const GElf_Shdr *shdr __attribute__ ((unused)),
     901             :  Dwarf_Addr *addr)
     902             : {
     903           0 :   char *sysfile;
     904           0 :   if (asprintf (&sysfile, SECADDRDIRFMT "%s", modname, secname) < 0)
     905             :     return DWARF_CB_ABORT;
     906             : 
     907           0 :   FILE *f = fopen (sysfile, "r");
     908           0 :   free (sysfile);
     909             : 
     910           0 :   if (f == NULL)
     911             :     {
     912           0 :       if (errno == ENOENT)
     913             :         {
     914             :           /* The .modinfo and .data.percpu sections are never kept
     915             :              loaded in the kernel.  If the kernel was compiled without
     916             :              CONFIG_MODULE_UNLOAD, the .exit.* sections are not
     917             :              actually loaded at all.
     918             : 
     919             :              Setting *ADDR to -1 tells the caller this section is
     920             :              actually absent from memory.  */
     921             : 
     922           0 :           if (!strcmp (secname, ".modinfo")
     923           0 :               || !strcmp (secname, ".data.percpu")
     924           0 :               || !strncmp (secname, ".exit", 5))
     925             :             {
     926           0 :               *addr = (Dwarf_Addr) -1l;
     927           0 :               return DWARF_CB_OK;
     928             :             }
     929             : 
     930             :           /* The goofy PPC64 module_frob_arch_sections function tweaks
     931             :              the section names as a way to control other kernel code's
     932             :              behavior, and this cruft leaks out into the /sys information.
     933             :              The file name for ".init*" may actually look like "_init*".  */
     934             : 
     935           0 :           const bool is_init = !strncmp (secname, ".init", 5);
     936           0 :           if (is_init)
     937             :             {
     938           0 :               if (asprintf (&sysfile, SECADDRDIRFMT "_%s",
     939             :                             modname, &secname[1]) < 0)
     940             :                 return ENOMEM;
     941           0 :               f = fopen (sysfile, "r");
     942           0 :               free (sysfile);
     943           0 :               if (f != NULL)
     944             :                 goto ok;
     945             :             }
     946             : 
     947             :           /* The kernel truncates section names to MODULE_SECT_NAME_LEN - 1.
     948             :              In case that size increases in the future, look for longer
     949             :              truncated names first.  */
     950           0 :           size_t namelen = strlen (secname);
     951           0 :           if (namelen >= MODULE_SECT_NAME_LEN)
     952             :             {
     953           0 :               int len = asprintf (&sysfile, SECADDRDIRFMT "%s",
     954             :                                   modname, secname);
     955           0 :               if (len < 0)
     956             :                 return DWARF_CB_ABORT;
     957           0 :               char *end = sysfile + len;
     958           0 :               do
     959             :                 {
     960           0 :                   *--end = '\0';
     961           0 :                   f = fopen (sysfile, "r");
     962           0 :                   if (is_init && f == NULL && errno == ENOENT)
     963             :                     {
     964           0 :                       sysfile[len - namelen] = '_';
     965           0 :                       f = fopen (sysfile, "r");
     966           0 :                       sysfile[len - namelen] = '.';
     967             :                     }
     968             :                 }
     969           0 :               while (f == NULL && errno == ENOENT
     970           0 :                      && end - &sysfile[len - namelen] >= MODULE_SECT_NAME_LEN);
     971           0 :               free (sysfile);
     972             : 
     973           0 :               if (f != NULL)
     974             :                 goto ok;
     975             :             }
     976             :         }
     977             : 
     978           0 :       return DWARF_CB_ABORT;
     979             :     }
     980             : 
     981           0 :  ok:
     982           0 :   (void) __fsetlocking (f, FSETLOCKING_BYCALLER);
     983             : 
     984           0 :   int result = (fscanf (f, "%" PRIx64 "\n", addr) == 1 ? 0
     985           0 :                 : ferror_unlocked (f) ? errno : ENOEXEC);
     986           0 :   fclose (f);
     987             : 
     988           0 :   if (result == 0)
     989             :     return DWARF_CB_OK;
     990             : 
     991           0 :   errno = result;
     992           0 :   return DWARF_CB_ABORT;
     993             : }
     994             : INTDEF (dwfl_linux_kernel_module_section_address)
     995             : 
     996             : int
     997           0 : dwfl_linux_kernel_report_modules (Dwfl *dwfl)
     998             : {
     999           0 :   FILE *f = fopen (MODULELIST, "r");
    1000           0 :   if (f == NULL)
    1001           0 :     return errno;
    1002             : 
    1003           0 :   (void) __fsetlocking (f, FSETLOCKING_BYCALLER);
    1004             : 
    1005           0 :   int result = 0;
    1006           0 :   Dwarf_Addr modaddr;
    1007           0 :   unsigned long int modsz;
    1008           0 :   char modname[128];
    1009           0 :   char *line = NULL;
    1010           0 :   size_t linesz = 0;
    1011             :   /* We can't just use fscanf here because it's not easy to distinguish \n
    1012             :      from other whitespace so as to take the optional word following the
    1013             :      address but always stop at the end of the line.  */
    1014           0 :   while (getline (&line, &linesz, f) > 0
    1015           0 :          && sscanf (line, "%128s %lu %*s %*s %*s %" PRIx64 " %*s\n",
    1016             :                     modname, &modsz, &modaddr) == 3)
    1017             :     {
    1018           0 :       Dwfl_Module *mod = INTUSE(dwfl_report_module) (dwfl, modname,
    1019             :                                                      modaddr, modaddr + modsz);
    1020           0 :       if (mod == NULL)
    1021             :         {
    1022             :           result = -1;
    1023             :           break;
    1024             :         }
    1025             : 
    1026           0 :       result = check_module_notes (mod);
    1027             :     }
    1028           0 :   free (line);
    1029             : 
    1030           0 :   if (result == 0)
    1031           0 :     result = ferror_unlocked (f) ? errno : feof_unlocked (f) ? 0 : ENOEXEC;
    1032             : 
    1033           0 :   fclose (f);
    1034             : 
    1035           0 :   return result;
    1036             : }
    1037             : INTDEF (dwfl_linux_kernel_report_modules)

Generated by: LCOV version 1.13