Home | History | Annotate | Download | only in libdwfl
      1 /* Find an ELF file for a module from its build ID.
      2    Copyright (C) 2007-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 #ifdef HAVE_CONFIG_H
     30 # include <config.h>
     31 #endif
     32 
     33 #include "libdwflP.h"
     34 #include <inttypes.h>
     35 #include <fcntl.h>
     36 #include <unistd.h>
     37 #include "system.h"
     38 
     39 
     40 int
     41 internal_function
     42 __libdwfl_open_by_build_id (Dwfl_Module *mod, bool debug, char **file_name,
     43 			    const size_t id_len, const uint8_t *id)
     44 {
     45   /* We don't handle very short or really large build-ids.  We need at
     46      at least 3 and allow for up to 64 (normally ids are 20 long).  */
     47 #define MIN_BUILD_ID_BYTES 3
     48 #define MAX_BUILD_ID_BYTES 64
     49   if (id_len < MIN_BUILD_ID_BYTES || id_len > MAX_BUILD_ID_BYTES)
     50     {
     51       __libdwfl_seterrno (DWFL_E_WRONG_ID_ELF);
     52       return -1;
     53     }
     54 
     55   /* Search debuginfo_path directories' .build-id/ subdirectories.  */
     56 
     57   char id_name[sizeof "/.build-id/" + 1 + MAX_BUILD_ID_BYTES * 2
     58 	       + sizeof ".debug" - 1];
     59   strcpy (id_name, "/.build-id/");
     60   int n = snprintf (&id_name[sizeof "/.build-id/" - 1],
     61 		    4, "%02" PRIx8 "/", (uint8_t) id[0]);
     62   assert (n == 3);
     63   for (size_t i = 1; i < id_len; ++i)
     64     {
     65       n = snprintf (&id_name[sizeof "/.build-id/" - 1 + 3 + (i - 1) * 2],
     66 		    3, "%02" PRIx8, (uint8_t) id[i]);
     67       assert (n == 2);
     68     }
     69   if (debug)
     70     strcpy (&id_name[sizeof "/.build-id/" - 1 + 3 + (id_len - 1) * 2],
     71 	    ".debug");
     72 
     73   const Dwfl_Callbacks *const cb = mod->dwfl->callbacks;
     74   char *path = strdup ((cb->debuginfo_path ? *cb->debuginfo_path : NULL)
     75 		       ?: DEFAULT_DEBUGINFO_PATH);
     76   if (path == NULL)
     77     return -1;
     78 
     79   int fd = -1;
     80   char *dir;
     81   char *paths = path;
     82   while (fd < 0 && (dir = strsep (&paths, ":")) != NULL)
     83     {
     84       if (dir[0] == '+' || dir[0] == '-')
     85 	++dir;
     86 
     87       /* Only absolute directory names are useful to us.  */
     88       if (dir[0] != '/')
     89 	continue;
     90 
     91       size_t dirlen = strlen (dir);
     92       char *name = malloc (dirlen + sizeof id_name);
     93       if (unlikely (name == NULL))
     94 	break;
     95       memcpy (mempcpy (name, dir, dirlen), id_name, sizeof id_name);
     96 
     97       fd = TEMP_FAILURE_RETRY (open (name, O_RDONLY));
     98       if (fd >= 0)
     99 	{
    100 	  if (*file_name != NULL)
    101 	    free (*file_name);
    102 	  *file_name = realpath (name, NULL);
    103 	  if (*file_name == NULL)
    104 	    {
    105 	      *file_name = name;
    106 	      name = NULL;
    107 	    }
    108 	}
    109       free (name);
    110     }
    111 
    112   free (path);
    113 
    114   /* If we simply found nothing, clear errno.  If we had some other error
    115      with the file, report that.  Possibly this should treat other errors
    116      like ENOENT too.  But ignoring all errors could mask some that should
    117      be reported.  */
    118   if (fd < 0 && errno == ENOENT)
    119     errno = 0;
    120 
    121   return fd;
    122 }
    123 
    124 int
    125 internal_function
    126 __libdwfl_open_mod_by_build_id (Dwfl_Module *mod, bool debug, char **file_name)
    127 {
    128   /* If *FILE_NAME was primed into the module, leave it there
    129      as the fallback when we have nothing to offer.  */
    130   errno = 0;
    131   if (mod->build_id_len <= 0)
    132     return -1;
    133 
    134   const size_t id_len = mod->build_id_len;
    135   const uint8_t *id = mod->build_id_bits;
    136 
    137   return __libdwfl_open_by_build_id (mod, debug, file_name, id_len, id);
    138 }
    139 
    140 int
    141 dwfl_build_id_find_elf (Dwfl_Module *mod,
    142 			void **userdata __attribute__ ((unused)),
    143 			const char *modname __attribute__ ((unused)),
    144 			Dwarf_Addr base __attribute__ ((unused)),
    145 			char **file_name, Elf **elfp)
    146 {
    147   *elfp = NULL;
    148   if (mod->is_executable
    149       && mod->dwfl->user_core != NULL
    150       && mod->dwfl->user_core->executable_for_core != NULL)
    151     {
    152       /* When dwfl_core_file_report was called with a non-NULL executable file
    153 	 name this callback will replace the Dwfl_Module main.name with the
    154 	 recorded executable file when MOD was identified as main executable
    155 	 (which then triggers opening and reporting of the executable).  */
    156       const char *executable = mod->dwfl->user_core->executable_for_core;
    157       int fd = open (executable, O_RDONLY);
    158       if (fd >= 0)
    159 	{
    160 	  *file_name = strdup (executable);
    161 	  if (*file_name != NULL)
    162 	    return fd;
    163 	  else
    164 	    close (fd);
    165 	}
    166     }
    167   int fd = __libdwfl_open_mod_by_build_id (mod, false, file_name);
    168   if (fd >= 0)
    169     {
    170       Dwfl_Error error = __libdw_open_file (&fd, elfp, true, false);
    171       if (error != DWFL_E_NOERROR)
    172 	__libdwfl_seterrno (error);
    173       else if (__libdwfl_find_build_id (mod, false, *elfp) == 2)
    174 	{
    175 	  /* This is a backdoor signal to short-circuit the ID refresh.  */
    176 	  mod->main.valid = true;
    177 	  return fd;
    178 	}
    179       else
    180 	{
    181 	  /* This file does not contain the ID it should!  */
    182 	  elf_end (*elfp);
    183 	  *elfp = NULL;
    184 	  close (fd);
    185 	  fd = -1;
    186 	}
    187       free (*file_name);
    188       *file_name = NULL;
    189     }
    190   else if (errno == 0 && mod->build_id_len > 0)
    191     /* Setting this with no file yet loaded is a marker that
    192        the build ID is authoritative even if we also know a
    193        putative *FILE_NAME.  */
    194     mod->main.valid = true;
    195 
    196   return fd;
    197 }
    198 INTDEF (dwfl_build_id_find_elf)
    199