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

Generated by: LCOV version 1.16