Home | History | Annotate | Download | only in libdwfl
      1 /* Standard find_debuginfo callback for libdwfl.
      2    Copyright (C) 2005, 2006, 2007, 2008 Red Hat, Inc.
      3    This file is part of Red Hat elfutils.
      4 
      5    Red Hat elfutils is free software; you can redistribute it and/or modify
      6    it under the terms of the GNU General Public License as published by the
      7    Free Software Foundation; version 2 of the License.
      8 
      9    Red Hat elfutils is distributed in the hope that it will be useful, but
     10    WITHOUT ANY WARRANTY; without even the implied warranty of
     11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12    General Public License for more details.
     13 
     14    You should have received a copy of the GNU General Public License along
     15    with Red Hat elfutils; if not, write to the Free Software Foundation,
     16    Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
     17 
     18    In addition, as a special exception, Red Hat, Inc. gives You the
     19    additional right to link the code of Red Hat elfutils with code licensed
     20    under any Open Source Initiative certified open source license
     21    (http://www.opensource.org/licenses/index.php) which requires the
     22    distribution of source code with any binary distribution and to
     23    distribute linked combinations of the two.  Non-GPL Code permitted under
     24    this exception must only link to the code of Red Hat elfutils through
     25    those well defined interfaces identified in the file named EXCEPTION
     26    found in the source code files (the "Approved Interfaces").  The files
     27    of Non-GPL Code may instantiate templates or use macros or inline
     28    functions from the Approved Interfaces without causing the resulting
     29    work to be covered by the GNU General Public License.  Only Red Hat,
     30    Inc. may make changes or additions to the list of Approved Interfaces.
     31    Red Hat's grant of this exception is conditioned upon your not adding
     32    any new exceptions.  If you wish to add a new Approved Interface or
     33    exception, please contact Red Hat.  You must obey the GNU General Public
     34    License in all respects for all of the Red Hat elfutils code and other
     35    code used in conjunction with Red Hat elfutils except the Non-GPL Code
     36    covered by this exception.  If you modify this file, you may extend this
     37    exception to your version of the file, but you are not obligated to do
     38    so.  If you do not wish to provide this exception without modification,
     39    you must delete this exception statement from your version and license
     40    this file solely under the GPL without exception.
     41 
     42    Red Hat elfutils is an included package of the Open Invention Network.
     43    An included package of the Open Invention Network is a package for which
     44    Open Invention Network licensees cross-license their patents.  No patent
     45    license is granted, either expressly or impliedly, by designation as an
     46    included package.  Should you wish to participate in the Open Invention
     47    Network licensing program, please visit www.openinventionnetwork.com
     48    <http://www.openinventionnetwork.com>.  */
     49 
     50 #include "libdwflP.h"
     51 #include <stdio.h>
     52 #include <fcntl.h>
     53 #include <unistd.h>
     54 #include "system.h"
     55 
     56 /* Try to open64 [DIR/][SUBDIR/]DEBUGLINK, return file descriptor or -1.
     57    On success, *DEBUGINFO_FILE_NAME has the malloc'd name of the open file.  */
     58 static int
     59 try_open (const char *dir, const char *subdir, const char *debuglink,
     60 	  char **debuginfo_file_name)
     61 {
     62   char *fname;
     63   if (dir == NULL && subdir == NULL)
     64     {
     65       fname = strdup (debuglink);
     66       if (fname == NULL)
     67 	return -1;
     68     }
     69   else if ((subdir == NULL ? asprintf (&fname, "%s/%s", dir, debuglink)
     70 	    : dir == NULL ? asprintf (&fname, "%s/%s", subdir, debuglink)
     71 	    : asprintf (&fname, "%s/%s/%s", dir, subdir, debuglink)) < 0)
     72     return -1;
     73 
     74   int fd = TEMP_FAILURE_RETRY (open64 (fname, O_RDONLY));
     75   if (fd < 0)
     76     free (fname);
     77   else
     78     *debuginfo_file_name = fname;
     79 
     80   return fd;
     81 }
     82 
     83 /* Return true iff the FD's contents CRC matches DEBUGLINK_CRC.  */
     84 static inline bool
     85 check_crc (int fd, GElf_Word debuglink_crc)
     86 {
     87   uint32_t file_crc;
     88   return (__libdwfl_crc32_file (fd, &file_crc) == 0
     89 	  && file_crc == debuglink_crc);
     90 }
     91 
     92 static bool
     93 validate (Dwfl_Module *mod, int fd, bool check, GElf_Word debuglink_crc)
     94 {
     95   /* If we have a build ID, check only that.  */
     96   if (mod->build_id_len > 0)
     97     {
     98       /* We need to open an Elf handle on the file so we can check its
     99 	 build ID note for validation.  Backdoor the handle into the
    100 	 module data structure since we had to open it early anyway.  */
    101       mod->debug.elf = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, NULL);
    102       if (likely (__libdwfl_find_build_id (mod, false, mod->debug.elf) == 2))
    103 	/* Also backdoor the gratuitous flag.  */
    104 	mod->debug.valid = true;
    105       else
    106 	{
    107 	  /* A mismatch!  */
    108 	  elf_end (mod->debug.elf);
    109 	  mod->debug.elf = NULL;
    110 	  mod->debug.valid = false;
    111 	}
    112 
    113       return mod->debug.valid;
    114     }
    115 
    116   return !check || check_crc (fd, debuglink_crc);
    117 }
    118 
    119 static int
    120 find_debuginfo_in_path (Dwfl_Module *mod, const char *file_name,
    121 			const char *debuglink_file, GElf_Word debuglink_crc,
    122 			char **debuginfo_file_name)
    123 {
    124   bool cancheck = debuglink_crc != (GElf_Word) 0;
    125 
    126   const char *file_basename = file_name == NULL ? NULL : basename (file_name);
    127   if (debuglink_file == NULL)
    128     {
    129       if (file_basename == NULL)
    130 	{
    131 	  errno = 0;
    132 	  return -1;
    133 	}
    134 
    135       size_t len = strlen (file_basename);
    136       char *localname = alloca (len + sizeof ".debug");
    137       memcpy (localname, file_basename, len);
    138       memcpy (&localname[len], ".debug", sizeof ".debug");
    139       debuglink_file = localname;
    140       cancheck = false;
    141     }
    142 
    143   /* Look for a file named DEBUGLINK_FILE in the directories
    144      indicated by the debug directory path setting.  */
    145 
    146   const Dwfl_Callbacks *const cb = mod->dwfl->callbacks;
    147 /* ANDROID_CHANGE_BEGIN */
    148 #if defined(__BIONIC__) || defined(__APPLE__)
    149   char *path = strdup ((cb->debuginfo_path ? *cb->debuginfo_path : NULL)
    150 			?: DEFAULT_DEBUGINFO_PATH);
    151 #else
    152   char *path = strdupa ((cb->debuginfo_path ? *cb->debuginfo_path : NULL)
    153 			?: DEFAULT_DEBUGINFO_PATH);
    154 #endif
    155 /* ANDROID_CHANGE_END */
    156 
    157   /* A leading - or + in the whole path sets whether to check file CRCs.  */
    158   bool defcheck = true;
    159   if (path[0] == '-' || path[0] == '+')
    160     {
    161       defcheck = path[0] == '+';
    162       ++path;
    163     }
    164 
    165   /* ANDROID_CHANGE_BEGIN */
    166 #if defined(__BIONIC__) || defined(__APPLE__)
    167   char *file_dirname = (file_basename == file_name ? NULL
    168 			: strndup (file_name, file_basename - 1 - file_name));
    169 #else
    170   char *file_dirname = (file_basename == file_name ? NULL
    171 			: strndupa (file_name, file_basename - 1 - file_name));
    172 #endif
    173   /* ANDROID_CHANGE_END */
    174   char *p;
    175   while ((p = strsep (&path, ":")) != NULL)
    176     {
    177       /* A leading - or + says whether to check file CRCs for this element.  */
    178       bool check = defcheck;
    179       if (*p == '+' || *p == '-')
    180 	check = *p++ == '+';
    181       check = check && cancheck;
    182 
    183       const char *dir, *subdir;
    184       switch (p[0])
    185 	{
    186 	case '\0':
    187 	  /* An empty entry says to try the main file's directory.  */
    188 	  dir = file_dirname;
    189 	  subdir = NULL;
    190 	  break;
    191 	case '/':
    192 	  /* An absolute path says to look there for a subdirectory
    193 	     named by the main file's absolute directory.
    194 	     This cannot be applied to a relative file name.  */
    195 	  if (file_dirname == NULL || file_dirname[0] != '/')
    196 	    continue;
    197 	  dir = p;
    198 	  subdir = file_dirname + 1;
    199 	  break;
    200 	default:
    201 	  /* A relative path says to try a subdirectory of that name
    202 	     in the main file's directory.  */
    203 	  dir = file_dirname;
    204 	  subdir = p;
    205 	  break;
    206 	}
    207 
    208 /* ANDROID_CHANGE_BEGIN */
    209       char *fname = NULL;
    210 /* ANDROID_CHANGE_END */
    211       int fd = try_open (dir, subdir, debuglink_file, &fname);
    212       if (fd < 0)
    213 	switch (errno)
    214 	  {
    215 	  case ENOENT:
    216 	  case ENOTDIR:
    217 	    continue;
    218 	  default:
    219 /* ANDROID_CHANGE_BEGIN */
    220 #ifdef __BIONIC__
    221             free(path);
    222             free(file_dirname);
    223 #endif
    224 /* ANDROID_CHANGE_END */
    225 	    return -1;
    226 	  }
    227       if (validate (mod, fd, check, debuglink_crc))
    228 	{
    229 	  *debuginfo_file_name = fname;
    230 /* ANDROID_CHANGE_BEGIN */
    231 #ifdef __BIONIC__
    232           free(path);
    233           free(file_dirname);
    234 #endif
    235 /* ANDROID_CHANGE_END */
    236 	  return fd;
    237 	}
    238       free (fname);
    239       close (fd);
    240     }
    241 
    242 /* ANDROID_CHANGE_BEGIN */
    243 #ifdef __BIONIC__
    244   free(path);
    245   free(file_dirname);
    246 #endif
    247 /* ANDROID_CHANGE_END */
    248 
    249   /* No dice.  */
    250   errno = 0;
    251   return -1;
    252 }
    253 
    254 int
    255 dwfl_standard_find_debuginfo (Dwfl_Module *mod,
    256 			      void **userdata __attribute__ ((unused)),
    257 			      const char *modname __attribute__ ((unused)),
    258 			      GElf_Addr base __attribute__ ((unused)),
    259 			      const char *file_name,
    260 			      const char *debuglink_file,
    261 			      GElf_Word debuglink_crc,
    262 			      char **debuginfo_file_name)
    263 {
    264   /* First try by build ID if we have one.  If that succeeds or fails
    265      other than just by finding nothing, that's all we do.  */
    266   const unsigned char *bits;
    267   GElf_Addr vaddr;
    268   if (INTUSE(dwfl_module_build_id) (mod, &bits, &vaddr) > 0)
    269     {
    270       int fd = INTUSE(dwfl_build_id_find_debuginfo) (mod,
    271 						     NULL, NULL, 0,
    272 						     NULL, NULL, 0,
    273 						     debuginfo_file_name);
    274       if (fd >= 0 || errno != 0)
    275 	return fd;
    276     }
    277 
    278   /* Failing that, search the path by name.  */
    279   int fd = find_debuginfo_in_path (mod, file_name,
    280 				   debuglink_file, debuglink_crc,
    281 				   debuginfo_file_name);
    282 
    283   if (fd < 0 && errno == 0)
    284     {
    285       /* If FILE_NAME is a symlink, the debug file might be associated
    286 	 with the symlink target name instead.  */
    287 
    288       char *canon = canonicalize_file_name (file_name);
    289       if (canon != NULL && strcmp (file_name, canon))
    290 	fd = find_debuginfo_in_path (mod, canon,
    291 				     debuglink_file, debuglink_crc,
    292 				     debuginfo_file_name);
    293       free (canon);
    294     }
    295 
    296   return fd;
    297 }
    298 INTDEF (dwfl_standard_find_debuginfo)
    299