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       char *fname;
    209       int fd = try_open (dir, subdir, debuglink_file, &fname);
    210       if (fd < 0)
    211 	switch (errno)
    212 	  {
    213 	  case ENOENT:
    214 	  case ENOTDIR:
    215 	    continue;
    216 	  default:
    217 /* ANDROID_CHANGE_BEGIN */
    218 #ifdef __BIONIC__
    219             free(path);
    220             free(file_dirname);
    221 #endif
    222 /* ANDROID_CHANGE_END */
    223 	    return -1;
    224 	  }
    225       if (validate (mod, fd, check, debuglink_crc))
    226 	{
    227 	  *debuginfo_file_name = fname;
    228 /* ANDROID_CHANGE_BEGIN */
    229 #ifdef __BIONIC__
    230           free(path);
    231           free(file_dirname);
    232 #endif
    233 /* ANDROID_CHANGE_END */
    234 	  return fd;
    235 	}
    236       free (fname);
    237       close (fd);
    238     }
    239 
    240 /* ANDROID_CHANGE_BEGIN */
    241 #ifdef __BIONIC__
    242   free(path);
    243   free(file_dirname);
    244 #endif
    245 /* ANDROID_CHANGE_END */
    246 
    247   /* No dice.  */
    248   errno = 0;
    249   return -1;
    250 }
    251 
    252 int
    253 dwfl_standard_find_debuginfo (Dwfl_Module *mod,
    254 			      void **userdata __attribute__ ((unused)),
    255 			      const char *modname __attribute__ ((unused)),
    256 			      GElf_Addr base __attribute__ ((unused)),
    257 			      const char *file_name,
    258 			      const char *debuglink_file,
    259 			      GElf_Word debuglink_crc,
    260 			      char **debuginfo_file_name)
    261 {
    262   /* First try by build ID if we have one.  If that succeeds or fails
    263      other than just by finding nothing, that's all we do.  */
    264   const unsigned char *bits;
    265   GElf_Addr vaddr;
    266   if (INTUSE(dwfl_module_build_id) (mod, &bits, &vaddr) > 0)
    267     {
    268       int fd = INTUSE(dwfl_build_id_find_debuginfo) (mod,
    269 						     NULL, NULL, 0,
    270 						     NULL, NULL, 0,
    271 						     debuginfo_file_name);
    272       if (fd >= 0 || errno != 0)
    273 	return fd;
    274     }
    275 
    276   /* Failing that, search the path by name.  */
    277   int fd = find_debuginfo_in_path (mod, file_name,
    278 				   debuglink_file, debuglink_crc,
    279 				   debuginfo_file_name);
    280 
    281   if (fd < 0 && errno == 0)
    282     {
    283       /* If FILE_NAME is a symlink, the debug file might be associated
    284 	 with the symlink target name instead.  */
    285 
    286       char *canon = canonicalize_file_name (file_name);
    287       if (canon != NULL && strcmp (file_name, canon))
    288 	fd = find_debuginfo_in_path (mod, canon,
    289 				     debuglink_file, debuglink_crc,
    290 				     debuginfo_file_name);
    291       free (canon);
    292     }
    293 
    294   return fd;
    295 }
    296 INTDEF (dwfl_standard_find_debuginfo)
    297