LCOV - code coverage report
Current view: top level - libdwfl - find-debuginfo.c (source / functions) Hit Total Coverage
Test: elfutils-0.185 Lines: 150 176 85.2 %
Date: 2021-05-22 21:00:18 Functions: 5 5 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 116 151 76.8 %

           Branch data     Line data    Source code
       1                 :            : /* Standard find_debuginfo callback for libdwfl.
       2                 :            :    Copyright (C) 2005-2010, 2014, 2015, 2019 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                 :            : #ifdef HAVE_CONFIG_H
      30                 :            : # include <config.h>
      31                 :            : #endif
      32                 :            : 
      33                 :            : #include "libdwflP.h"
      34                 :            : #include <stdio.h>
      35                 :            : #include <fcntl.h>
      36                 :            : #include <unistd.h>
      37                 :            : #include <sys/stat.h>
      38                 :            : #include "system.h"
      39                 :            : 
      40                 :            : 
      41                 :            : /* Try to open [DIR/][SUBDIR/]DEBUGLINK, return file descriptor or -1.
      42                 :            :    On success, *DEBUGINFO_FILE_NAME has the malloc'd name of the open file.  */
      43                 :            : static int
      44                 :        922 : try_open (const struct stat *main_stat,
      45                 :            :           const char *dir, const char *subdir, const char *debuglink,
      46                 :            :           char **debuginfo_file_name)
      47                 :            : {
      48                 :        922 :   char *fname;
      49         [ +  + ]:        922 :   if (dir == NULL && subdir == NULL)
      50                 :            :     {
      51                 :        104 :       fname = strdup (debuglink);
      52         [ +  - ]:        104 :       if (unlikely (fname == NULL))
      53                 :            :         return -1;
      54                 :            :     }
      55         [ +  - ]:        818 :   else if ((subdir == NULL ? asprintf (&fname, "%s/%s", dir, debuglink)
      56                 :         56 :             : dir == NULL ? asprintf (&fname, "%s/%s", subdir, debuglink)
      57   [ +  +  +  + ]:       1636 :             : asprintf (&fname, "%s/%s/%s", dir, subdir, debuglink)) < 0)
      58                 :            :     return -1;
      59                 :            : 
      60                 :        922 :   struct stat st;
      61   [ +  +  -  + ]:        922 :   int fd = TEMP_FAILURE_RETRY (open (fname, O_RDONLY));
      62         [ +  + ]:        922 :   if (fd < 0)
      63                 :        811 :     free (fname);
      64         [ +  - ]:        111 :   else if (fstat (fd, &st) == 0
      65         [ +  + ]:        111 :            && st.st_ino == main_stat->st_ino
      66         [ +  - ]:         72 :            && st.st_dev == main_stat->st_dev)
      67                 :            :     {
      68                 :            :       /* This is the main file by another name.  Don't look at it again.  */
      69                 :         72 :       free (fname);
      70                 :         72 :       close (fd);
      71                 :         72 :       errno = ENOENT;
      72                 :         72 :       fd = -1;
      73                 :            :     }
      74                 :            :   else
      75                 :         39 :     *debuginfo_file_name = fname;
      76                 :            : 
      77                 :            :   return fd;
      78                 :            : }
      79                 :            : 
      80                 :            : /* Return true iff the FD's contents CRC matches DEBUGLINK_CRC.  */
      81                 :            : static inline bool
      82                 :          1 : check_crc (int fd, GElf_Word debuglink_crc)
      83                 :            : {
      84                 :          1 :   uint32_t file_crc;
      85                 :          1 :   return (__libdwfl_crc32_file (fd, &file_crc) == 0
      86   [ +  -  -  + ]:          1 :           && file_crc == debuglink_crc);
      87                 :            : }
      88                 :            : 
      89                 :            : static bool
      90                 :         39 : validate (Dwfl_Module *mod, int fd, bool check, GElf_Word debuglink_crc)
      91                 :            : {
      92                 :            :   /* For alt debug files always check the build-id from the Dwarf and alt.  */
      93         [ +  + ]:         39 :   if (mod->dw != NULL)
      94                 :            :     {
      95                 :          9 :       bool valid = false;
      96                 :          9 :       const void *build_id;
      97                 :          9 :       const char *altname;
      98                 :          9 :       ssize_t build_id_len = INTUSE(dwelf_dwarf_gnu_debugaltlink) (mod->dw,
      99                 :            :                                                                    &altname,
     100                 :            :                                                                    &build_id);
     101         [ +  - ]:          9 :       if (build_id_len > 0)
     102                 :            :         {
     103                 :            :           /* We need to open an Elf handle on the file so we can check its
     104                 :            :              build ID note for validation.  Backdoor the handle into the
     105                 :            :              module data structure since we had to open it early anyway.  */
     106                 :          9 :           Dwfl_Error error = __libdw_open_file (&fd, &mod->alt_elf,
     107                 :            :                                                 false, false);
     108         [ -  + ]:          9 :           if (error != DWFL_E_NOERROR)
     109                 :          0 :             __libdwfl_seterrno (error);
     110                 :            :           else
     111                 :            :             {
     112                 :          9 :               const void *alt_build_id;
     113                 :          9 :               ssize_t alt_len = INTUSE(dwelf_elf_gnu_build_id) (mod->alt_elf,
     114                 :            :                                                                 &alt_build_id);
     115         [ +  - ]:          9 :               if (alt_len > 0 && alt_len == build_id_len
     116         [ -  + ]:          9 :                   && memcmp (build_id, alt_build_id, alt_len) == 0)
     117                 :            :                 valid = true;
     118                 :            :               else
     119                 :            :                 {
     120                 :            :                   /* A mismatch!  */
     121                 :          0 :                   elf_end (mod->alt_elf);
     122                 :          0 :                   mod->alt_elf = NULL;
     123                 :          0 :                   close (fd);
     124                 :          0 :                   fd = -1;
     125                 :            :                 }
     126                 :            :             }
     127                 :            :         }
     128                 :          9 :       return valid;
     129                 :            :     }
     130                 :            : 
     131                 :            :   /* If we have a build ID, check only that.  */
     132         [ +  + ]:         30 :   if (mod->build_id_len > 0)
     133                 :            :     {
     134                 :            :       /* We need to open an Elf handle on the file so we can check its
     135                 :            :          build ID note for validation.  Backdoor the handle into the
     136                 :            :          module data structure since we had to open it early anyway.  */
     137                 :            : 
     138                 :         29 :       mod->debug.valid = false;
     139                 :         29 :       Dwfl_Error error = __libdw_open_file (&fd, &mod->debug.elf, false, false);
     140         [ -  + ]:         29 :       if (error != DWFL_E_NOERROR)
     141                 :          0 :         __libdwfl_seterrno (error);
     142         [ +  - ]:         29 :       else if (likely (__libdwfl_find_build_id (mod, false,
     143                 :            :                                                 mod->debug.elf) == 2))
     144                 :            :         /* Also backdoor the gratuitous flag.  */
     145                 :         29 :         mod->debug.valid = true;
     146                 :            :       else
     147                 :            :         {
     148                 :            :           /* A mismatch!  */
     149                 :          0 :           elf_end (mod->debug.elf);
     150                 :          0 :           mod->debug.elf = NULL;
     151                 :          0 :           close (fd);
     152                 :          0 :           fd = -1;
     153                 :            :         }
     154                 :            : 
     155                 :         29 :       return mod->debug.valid;
     156                 :            :     }
     157                 :            : 
     158   [ +  -  +  - ]:          2 :   return !check || check_crc (fd, debuglink_crc);
     159                 :            : }
     160                 :            : 
     161                 :            : static int
     162                 :        142 : find_debuginfo_in_path (Dwfl_Module *mod, const char *file_name,
     163                 :            :                         const char *debuglink_file, GElf_Word debuglink_crc,
     164                 :            :                         char **debuginfo_file_name)
     165                 :            : {
     166                 :        142 :   bool cancheck = debuglink_crc != (GElf_Word) 0;
     167                 :            : 
     168         [ +  + ]:        142 :   const char *file_basename = file_name == NULL ? NULL : basename (file_name);
     169                 :        142 :   char *localname = NULL;
     170                 :            : 
     171                 :            :   /* We invent a debuglink .debug name if NULL, but then want to try the
     172                 :            :      basename too.  */
     173                 :        142 :   bool debuglink_null = debuglink_file == NULL;
     174         [ +  + ]:        142 :   if (debuglink_null)
     175                 :            :     {
     176                 :            :       /* For a alt debug multi file we need a name, for a separate debug
     177                 :            :          name we may be able to fall back on file_basename.debug.  */
     178   [ +  +  -  + ]:         94 :       if (file_basename == NULL || mod->dw != NULL)
     179                 :            :         {
     180                 :         20 :           errno = 0;
     181                 :         20 :           return -1;
     182                 :            :         }
     183                 :            : 
     184                 :         74 :       size_t len = strlen (file_basename);
     185                 :         74 :       localname = malloc (len + sizeof ".debug");
     186         [ +  - ]:         74 :       if (unlikely (localname == NULL))
     187                 :            :         return -1;
     188                 :         74 :       memcpy (localname, file_basename, len);
     189                 :         74 :       memcpy (&localname[len], ".debug", sizeof ".debug");
     190                 :         74 :       debuglink_file = localname;
     191                 :         74 :       cancheck = false;
     192                 :            :     }
     193                 :            : 
     194                 :            :   /* Look for a file named DEBUGLINK_FILE in the directories
     195                 :            :      indicated by the debug directory path setting.  */
     196                 :            : 
     197                 :        122 :   const Dwfl_Callbacks *const cb = mod->dwfl->callbacks;
     198   [ +  +  +  + ]:        122 :   char *localpath = strdup ((cb->debuginfo_path ? *cb->debuginfo_path : NULL)
     199                 :            :                             ?: DEFAULT_DEBUGINFO_PATH);
     200         [ -  + ]:        122 :   if (unlikely (localpath == NULL))
     201                 :            :     {
     202                 :          0 :       free (localname);
     203                 :          0 :       return -1;
     204                 :            :     }
     205                 :            : 
     206                 :            :   /* A leading - or + in the whole path sets whether to check file CRCs.  */
     207                 :        122 :   bool defcheck = true;
     208                 :        122 :   char *path = localpath;
     209         [ -  + ]:        122 :   if (path[0] == '-' || path[0] == '+')
     210                 :            :     {
     211                 :          0 :       defcheck = path[0] == '+';
     212                 :          0 :       ++path;
     213                 :            :     }
     214                 :            : 
     215                 :            :   /* XXX dev/ino should be cached in struct dwfl_file.  */
     216                 :        122 :   struct stat main_stat;
     217   [ +  +  +  -  :        207 :   if (unlikely ((mod->main.fd != -1 ? fstat (mod->main.fd, &main_stat)
             -  +  -  + ]
     218                 :            :                  : file_name != NULL ? stat (file_name, &main_stat)
     219                 :            :                  : -1) < 0))
     220                 :            :     {
     221                 :          0 :       main_stat.st_dev = 0;
     222                 :          0 :       main_stat.st_ino = 0;
     223                 :            :     }
     224                 :            : 
     225                 :        244 :   char *file_dirname = (file_basename == file_name ? NULL
     226         [ +  + ]:        122 :                         : strndup (file_name, file_basename - 1 - file_name));
     227         [ -  + ]:        122 :   if (file_basename != file_name && file_dirname == NULL)
     228                 :            :     {
     229                 :          0 :       free (localpath);
     230                 :          0 :       free (localname);
     231                 :          0 :       return -1;
     232                 :            :     }
     233                 :            :   char *p;
     234         [ +  + ]:        371 :   while ((p = strsep (&path, ":")) != NULL)
     235                 :            :     {
     236                 :            :       /* A leading - or + says whether to check file CRCs for this element.  */
     237                 :        288 :       bool check = defcheck;
     238         [ -  + ]:        288 :       if (*p == '+' || *p == '-')
     239                 :          0 :         check = *p++ == '+';
     240                 :        288 :       check = check && cancheck;
     241                 :            : 
     242                 :            :       /* Try the basename too, if we made up the debuglink name and this
     243                 :            :          is not the main directory.  */
     244                 :        288 :       bool try_file_basename;
     245                 :            : 
     246                 :        288 :       const char *dir, *subdir, *file;
     247      [ +  +  + ]:        288 :       switch (p[0])
     248                 :            :         {
     249                 :            :         case '\0':
     250                 :            :           /* An empty entry says to try the main file's directory.  */
     251                 :            :           dir = file_dirname;
     252                 :            :           subdir = NULL;
     253                 :            :           file = debuglink_file;
     254                 :            :           try_file_basename = false;
     255                 :            :           break;
     256                 :         84 :         case '/':
     257                 :            :           /* An absolute path says to look there for a subdirectory
     258                 :            :              named by the main file's absolute directory.  This cannot
     259                 :            :              be applied to a relative file name.  For alt debug files
     260                 :            :              it means to look for the basename file in that dir or the
     261                 :            :              .dwz subdir (see below).  */
     262         [ +  + ]:         84 :           if (mod->dw == NULL
     263   [ +  +  +  + ]:         78 :               && (file_dirname == NULL || file_dirname[0] != '/'))
     264                 :        249 :             continue;
     265                 :         48 :           dir = p;
     266         [ +  + ]:         48 :           if (mod->dw == NULL)
     267                 :            :             {
     268                 :            :               subdir = file_dirname;
     269                 :            :               /* We want to explore all sub-subdirs.  Chop off one slash
     270                 :            :                  at a time.  */
     271                 :        326 :             explore_dir:
     272                 :        326 :               subdir = strchr (subdir, '/');
     273         [ +  + ]:        326 :               if (subdir != NULL)
     274                 :        208 :                 subdir = subdir + 1;
     275   [ +  +  -  + ]:        326 :               if (subdir && *subdir == 0)
     276                 :          0 :                 continue;
     277                 :            :               file = debuglink_file;
     278                 :            :             }
     279                 :            :           else
     280                 :            :             {
     281                 :          6 :               subdir = NULL;
     282                 :          6 :               file = basename (debuglink_file);
     283                 :            :             }
     284                 :            :           try_file_basename = debuglink_null;
     285                 :            :           break;
     286                 :         83 :         default:
     287                 :            :           /* A relative path says to try a subdirectory of that name
     288                 :            :              in the main file's directory.  */
     289                 :         83 :           dir = file_dirname;
     290                 :         83 :           subdir = p;
     291                 :         83 :           file = debuglink_file;
     292                 :         83 :           try_file_basename = debuglink_null;
     293                 :         83 :           break;
     294                 :            :         }
     295                 :            : 
     296                 :        536 :       char *fname = NULL;
     297                 :        536 :       int fd = try_open (&main_stat, dir, subdir, file, &fname);
     298         [ +  + ]:        536 :       if (fd < 0 && try_file_basename)
     299                 :        372 :         fd = try_open (&main_stat, dir, subdir, file_basename, &fname);
     300         [ +  + ]:        536 :       if (fd < 0)
     301         [ +  - ]:        499 :         switch (errno)
     302                 :            :           {
     303                 :        499 :           case ENOENT:
     304                 :            :           case ENOTDIR:
     305                 :            :             /* If we are looking for the alt file also try the .dwz subdir.
     306                 :            :                But only if this is the empty or absolute path.  */
     307   [ +  +  +  + ]:        499 :             if (mod->dw != NULL && (p[0] == '\0' || p[0] == '/'))
     308                 :            :               {
     309                 :         14 :                 fd = try_open (&main_stat, dir, ".dwz",
     310                 :         14 :                                basename (file), &fname);
     311         [ +  + ]:         14 :                 if (fd < 0)
     312                 :            :                   {
     313         [ -  + ]:         12 :                     if (errno != ENOENT && errno != ENOTDIR)
     314                 :          0 :                       goto fail_free;
     315                 :            :                     else
     316                 :         12 :                       continue;
     317                 :            :                   }
     318                 :            :                 break;
     319                 :            :               }
     320                 :            :             /* If possible try again with a sub-subdir.  */
     321   [ +  +  +  + ]:        485 :             if (mod->dw == NULL && subdir)
     322                 :        284 :               goto explore_dir;
     323                 :        201 :             continue;
     324                 :          0 :           default:
     325                 :          0 :             goto fail_free;
     326                 :            :           }
     327         [ +  - ]:         39 :       if (validate (mod, fd, check, debuglink_crc))
     328                 :            :         {
     329                 :         39 :           free (localpath);
     330                 :         39 :           free (localname);
     331                 :         39 :           free (file_dirname);
     332                 :         39 :           *debuginfo_file_name = fname;
     333                 :         39 :           return fd;
     334                 :            :         }
     335                 :          0 :       free (fname);
     336                 :          0 :       close (fd);
     337                 :            :     }
     338                 :            : 
     339                 :            :   /* No dice.  */
     340                 :         83 :   errno = 0;
     341                 :         83 : fail_free:
     342                 :         83 :   free (localpath);
     343                 :         83 :   free (localname);
     344                 :         83 :   free (file_dirname);
     345                 :         83 :   return -1;
     346                 :            : }
     347                 :            : 
     348                 :            : int
     349                 :        114 : dwfl_standard_find_debuginfo (Dwfl_Module *mod,
     350                 :            :                               void **userdata __attribute__ ((unused)),
     351                 :            :                               const char *modname __attribute__ ((unused)),
     352                 :            :                               GElf_Addr base __attribute__ ((unused)),
     353                 :            :                               const char *file_name,
     354                 :            :                               const char *debuglink_file,
     355                 :            :                               GElf_Word debuglink_crc,
     356                 :            :                               char **debuginfo_file_name)
     357                 :            : {
     358         [ +  - ]:        114 :   if (mod == NULL)
     359                 :            :     return -1;
     360                 :            : 
     361                 :            :   /* First try by build ID if we have one.  If that succeeds or fails
     362                 :            :      other than just by finding nothing, that's all we do.  */
     363                 :        114 :   const unsigned char *bits = NULL;
     364                 :        114 :   GElf_Addr vaddr;
     365                 :        114 :   int bits_len;
     366         [ +  + ]:        114 :   if ((bits_len = INTUSE(dwfl_module_build_id) (mod, &bits, &vaddr)) > 0)
     367                 :            :     {
     368                 :            :       /* Dropping most arguments means we cannot rely on them in
     369                 :            :          dwfl_build_id_find_debuginfo.  But leave it that way since
     370                 :            :          some user code out there also does this, so we'll have to
     371                 :            :          handle it anyway.  */
     372                 :        103 :       int fd = INTUSE(dwfl_build_id_find_debuginfo) (mod,
     373                 :            :                                                      NULL, NULL, 0,
     374                 :            :                                                      NULL, NULL, 0,
     375                 :            :                                                      debuginfo_file_name);
     376                 :            : 
     377                 :            :       /* Did the build_id callback find something or report an error?
     378                 :            :          Then we are done.  Otherwise fallback on path based search.  */
     379         [ +  + ]:        103 :       if (fd >= 0
     380   [ +  +  +  - ]:         93 :           || (mod->dw == NULL && mod->debug.elf != NULL)
     381   [ +  +  +  - ]:         93 :           || (mod->dw != NULL && mod->alt_elf != NULL)
     382         [ +  - ]:         93 :           || errno != 0)
     383                 :            :         return fd;
     384                 :            :     }
     385                 :            : 
     386                 :            :   /* Failing that, search the path by name.  */
     387                 :        104 :   int fd = find_debuginfo_in_path (mod, file_name,
     388                 :            :                                    debuglink_file, debuglink_crc,
     389                 :            :                                    debuginfo_file_name);
     390                 :            : 
     391   [ +  +  +  -  :        104 :   if (fd < 0 && errno == 0 && file_name != NULL)
                   +  + ]
     392                 :            :     {
     393                 :            :       /* If FILE_NAME is a symlink, the debug file might be associated
     394                 :            :          with the symlink target name instead.  */
     395                 :            : 
     396         [ -  + ]:         47 :       char *canon = realpath (file_name, NULL);
     397   [ +  -  +  + ]:         47 :       if (canon != NULL && strcmp (file_name, canon))
     398                 :         38 :         fd = find_debuginfo_in_path (mod, canon,
     399                 :            :                                      debuglink_file, debuglink_crc,
     400                 :            :                                      debuginfo_file_name);
     401                 :         47 :       free (canon);
     402                 :            :     }
     403                 :            : 
     404                 :            : #ifdef ENABLE_LIBDEBUGINFOD
     405                 :            :   /* Still nothing? Try if we can use the debuginfod client.
     406                 :            :      But note that we might be looking for the alt file.
     407                 :            :      We use the same trick as dwfl_build_id_find_debuginfo.
     408                 :            :      If the debug file (dw) is already set, then we must be
     409                 :            :      looking for the altfile. But we cannot use the actual
     410                 :            :      file/path name given as hint. We'll have to lookup the
     411                 :            :      alt file "build-id". Because the debuginfod client only
     412                 :            :      handles build-ids.  */
     413         [ +  + ]:        104 :   if (fd < 0)
     414                 :            :     {
     415         [ +  + ]:         65 :       if (mod->dw != NULL)
     416                 :            :         {
     417                 :          4 :           const char *altname;
     418                 :          4 :           bits_len = INTUSE(dwelf_dwarf_gnu_debugaltlink) (mod->dw, &altname,
     419                 :            :                                                            (const void **)
     420                 :            :                                                            &bits);
     421                 :            :         }
     422                 :            : 
     423         [ +  + ]:         65 :       if (bits_len > 0)
     424                 :         55 :         fd = __libdwfl_debuginfod_find_debuginfo (mod->dwfl, bits, bits_len);
     425                 :            :     }
     426                 :            : #endif
     427                 :            : 
     428                 :            :   return fd;
     429                 :            : }
     430                 :            : INTDEF (dwfl_standard_find_debuginfo)

Generated by: LCOV version 1.14