LCOV - code coverage report
Current view: top level - elfutils/libdwfl - linux-kernel-modules.c (source / functions) Hit Total Coverage
Test: lcov.out Lines: 0 337 0.0 %
Date: 2012-10-31 Functions: 0 19 0.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 0 327 0.0 %

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

Generated by: LCOV version 1.9