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