LCOV - code coverage report
Current view: top level - libdwfl - find-debuginfo.c (source / functions) Hit Total Coverage
Test: elfutils-0.178 Lines: 121 166 72.9 %
Date: 2019-11-26 23:55:16 Functions: 3 4 75.0 %
Legend: Lines: hit not hit

          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           0 : try_open (const struct stat *main_stat,
      45             :           const char *dir, const char *subdir, const char *debuglink,
      46             :           char **debuginfo_file_name)
      47             : {
      48           0 :   char *fname;
      49           0 :   if (dir == NULL && subdir == NULL)
      50             :     {
      51           0 :       fname = strdup (debuglink);
      52           0 :       if (unlikely (fname == NULL))
      53           0 :         return -1;
      54             :     }
      55           0 :   else if ((subdir == NULL ? asprintf (&fname, "%s/%s", dir, debuglink)
      56           0 :             : dir == NULL ? asprintf (&fname, "%s/%s", subdir, debuglink)
      57           0 :             : asprintf (&fname, "%s/%s/%s", dir, subdir, debuglink)) < 0)
      58           0 :     return -1;
      59             : 
      60           0 :   struct stat st;
      61           0 :   int fd = TEMP_FAILURE_RETRY (open (fname, O_RDONLY));
      62           0 :   if (fd < 0)
      63           0 :     free (fname);
      64           0 :   else if (fstat (fd, &st) == 0
      65           0 :            && st.st_ino == main_stat->st_ino
      66           0 :            && st.st_dev == main_stat->st_dev)
      67             :     {
      68             :       /* This is the main file by another name.  Don't look at it again.  */
      69           0 :       free (fname);
      70           0 :       close (fd);
      71           0 :       errno = ENOENT;
      72           0 :       fd = -1;
      73             :     }
      74             :   else
      75           0 :     *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             : 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           3 :   return !check || check_crc (fd, debuglink_crc);
     159             : }
     160             : 
     161             : static int
     162         145 : 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         145 :   bool cancheck = debuglink_crc != (GElf_Word) 0;
     167             : 
     168         145 :   const char *file_basename = file_name == NULL ? NULL : basename (file_name);
     169         145 :   char *localname = NULL;
     170             : 
     171             :   /* We invent a debuglink .debug name if NULL, but then want to try the
     172             :      basename too.  */
     173         145 :   bool debuglink_null = debuglink_file == NULL;
     174         145 :   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          96 :       if (file_basename == NULL || mod->dw != NULL)
     179             :         {
     180          22 :           errno = 0;
     181          22 :           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         123 :   const Dwfl_Callbacks *const cb = mod->dwfl->callbacks;
     198         123 :   char *localpath = strdup ((cb->debuginfo_path ? *cb->debuginfo_path : NULL)
     199             :                             ?: DEFAULT_DEBUGINFO_PATH);
     200         123 :   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         123 :   bool defcheck = true;
     208         123 :   char *path = localpath;
     209         123 :   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         123 :   struct stat main_stat;
     217         123 :   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         246 :   char *file_dirname = (file_basename == file_name ? NULL
     226         123 :                         : strndup (file_name, file_basename - 1 - file_name));
     227         123 :   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         375 :   while ((p = strsep (&path, ":")) != NULL)
     235             :     {
     236             :       /* A leading - or + says whether to check file CRCs for this element.  */
     237         291 :       bool check = defcheck;
     238         291 :       if (*p == '+' || *p == '-')
     239           0 :         check = *p++ == '+';
     240         291 :       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         291 :       bool try_file_basename;
     245             : 
     246         291 :       const char *dir, *subdir, *file;
     247         291 :       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          85 :         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          85 :           if (mod->dw == NULL
     263          81 :               && (file_dirname == NULL || file_dirname[0] != '/'))
     264         252 :             continue;
     265          49 :           dir = p;
     266          49 :           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         338 :             explore_dir:
     272         338 :               subdir = strchr (subdir, '/');
     273         338 :               if (subdir != NULL)
     274         214 :                 subdir = subdir + 1;
     275         338 :               if (subdir && *subdir == 0)
     276             :                 continue;
     277             :               file = debuglink_file;
     278             :             }
     279             :           else
     280             :             {
     281           4 :               subdir = NULL;
     282           4 :               file = basename (debuglink_file);
     283             :             }
     284             :           try_file_basename = debuglink_null;
     285             :           break;
     286          84 :         default:
     287             :           /* A relative path says to try a subdirectory of that name
     288             :              in the main file's directory.  */
     289          84 :           dir = file_dirname;
     290          84 :           subdir = p;
     291          84 :           file = debuglink_file;
     292          84 :           try_file_basename = debuglink_null;
     293          84 :           break;
     294             :         }
     295             : 
     296         548 :       char *fname = NULL;
     297         548 :       int fd = try_open (&main_stat, dir, subdir, file, &fname);
     298         548 :       if (fd < 0 && try_file_basename)
     299         372 :         fd = try_open (&main_stat, dir, subdir, file_basename, &fname);
     300         548 :       if (fd < 0)
     301         511 :         switch (errno)
     302             :           {
     303         511 :           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         511 :             if (mod->dw != NULL && (p[0] == '\0' || p[0] == '/'))
     308             :               {
     309          10 :                 fd = try_open (&main_stat, dir, ".dwz",
     310          10 :                                basename (file), &fname);
     311          10 :                 if (fd < 0)
     312             :                   {
     313           8 :                     if (errno != ENOENT && errno != ENOTDIR)
     314           0 :                       goto fail_free;
     315             :                     else
     316             :                       continue;
     317             :                   }
     318             :                 break;
     319             :               }
     320             :             /* If possible try again with a sub-subdir.  */
     321         501 :             if (mod->dw == NULL && subdir)
     322             :               goto explore_dir;
     323             :             continue;
     324             :           default:
     325             :             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          84 :   errno = 0;
     341          84 : fail_free:
     342          84 :   free (localpath);
     343          84 :   free (localname);
     344          84 :   free (file_dirname);
     345          84 :   return -1;
     346             : }
     347             : 
     348             : int
     349         115 : 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             :   /* First try by build ID if we have one.  If that succeeds or fails
     359             :      other than just by finding nothing, that's all we do.  */
     360         115 :   const unsigned char *bits;
     361         115 :   GElf_Addr vaddr;
     362         115 :   int bits_len;
     363         115 :   if ((bits_len = INTUSE(dwfl_module_build_id) (mod, &bits, &vaddr)) > 0)
     364             :     {
     365             :       /* Dropping most arguments means we cannot rely on them in
     366             :          dwfl_build_id_find_debuginfo.  But leave it that way since
     367             :          some user code out there also does this, so we'll have to
     368             :          handle it anyway.  */
     369         104 :       int fd = INTUSE(dwfl_build_id_find_debuginfo) (mod,
     370             :                                                      NULL, NULL, 0,
     371             :                                                      NULL, NULL, 0,
     372             :                                                      debuginfo_file_name);
     373             : 
     374             :       /* Did the build_id callback find something or report an error?
     375             :          Then we are done.  Otherwise fallback on path based search.  */
     376         104 :       if (fd >= 0
     377          95 :           || (mod->dw == NULL && mod->debug.elf != NULL)
     378          95 :           || (mod->dw != NULL && mod->alt_elf != NULL)
     379          95 :           || errno != 0)
     380             :         return fd;
     381             :     }
     382             : 
     383             :   /* Failing that, search the path by name.  */
     384         106 :   int fd = find_debuginfo_in_path (mod, file_name,
     385             :                                    debuglink_file, debuglink_crc,
     386             :                                    debuginfo_file_name);
     387             : 
     388         106 :   if (fd < 0 && errno == 0 && file_name != NULL)
     389             :     {
     390             :       /* If FILE_NAME is a symlink, the debug file might be associated
     391             :          with the symlink target name instead.  */
     392             : 
     393          47 :       char *canon = realpath (file_name, NULL);
     394          47 :       if (canon != NULL && strcmp (file_name, canon))
     395          39 :         fd = find_debuginfo_in_path (mod, canon,
     396             :                                      debuglink_file, debuglink_crc,
     397             :                                      debuginfo_file_name);
     398          47 :       free (canon);
     399             :     }
     400             : 
     401         106 :   if (fd < 0 && bits_len > 0)
     402          57 :     fd = __libdwfl_debuginfod_find_debuginfo (mod->dwfl, bits, bits_len);
     403             : 
     404             :   return fd;
     405             : }
     406             : INTDEF (dwfl_standard_find_debuginfo)

Generated by: LCOV version 1.13