LCOV - code coverage report
Current view: top level - libdwfl - find-debuginfo.c (source / functions) Hit Total Coverage
Test: lcov.out Lines: 119 144 82.6 %
Date: 2017-01-05 09:15:16 Functions: 4 4 100.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.12