Blame libdwfl/find-debuginfo.c

Packit 032894
/* Standard find_debuginfo callback for libdwfl.
Packit 032894
   Copyright (C) 2005-2010, 2014, 2015, 2019 Red Hat, Inc.
Packit 032894
   This file is part of elfutils.
Packit 032894
Packit 032894
   This file is free software; you can redistribute it and/or modify
Packit 032894
   it under the terms of either
Packit 032894
Packit 032894
     * the GNU Lesser General Public License as published by the Free
Packit 032894
       Software Foundation; either version 3 of the License, or (at
Packit 032894
       your option) any later version
Packit 032894
Packit 032894
   or
Packit 032894
Packit 032894
     * the GNU General Public License as published by the Free
Packit 032894
       Software Foundation; either version 2 of the License, or (at
Packit 032894
       your option) any later version
Packit 032894
Packit 032894
   or both in parallel, as here.
Packit 032894
Packit 032894
   elfutils is distributed in the hope that it will be useful, but
Packit 032894
   WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 032894
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 032894
   General Public License for more details.
Packit 032894
Packit 032894
   You should have received copies of the GNU General Public License and
Packit 032894
   the GNU Lesser General Public License along with this program.  If
Packit 032894
   not, see <http://www.gnu.org/licenses/>.  */
Packit 032894
Packit 032894
#ifdef HAVE_CONFIG_H
Packit 032894
# include <config.h>
Packit 032894
#endif
Packit 032894
Packit 032894
#include "libdwflP.h"
Packit 032894
#include <stdio.h>
Packit 032894
#include <fcntl.h>
Packit 032894
#include <unistd.h>
Packit 032894
#include <sys/stat.h>
Packit 032894
#include "system.h"
Packit 032894
Packit 032894
Packit 032894
/* Try to open [DIR/][SUBDIR/]DEBUGLINK, return file descriptor or -1.
Packit 032894
   On success, *DEBUGINFO_FILE_NAME has the malloc'd name of the open file.  */
Packit 032894
static int
Packit 032894
try_open (const struct stat *main_stat,
Packit 032894
	  const char *dir, const char *subdir, const char *debuglink,
Packit 032894
	  char **debuginfo_file_name)
Packit 032894
{
Packit 032894
  char *fname;
Packit 032894
  if (dir == NULL && subdir == NULL)
Packit 032894
    {
Packit 032894
      fname = strdup (debuglink);
Packit 032894
      if (unlikely (fname == NULL))
Packit 032894
	return -1;
Packit 032894
    }
Packit 032894
  else if ((subdir == NULL ? asprintf (&fname, "%s/%s", dir, debuglink)
Packit 032894
	    : dir == NULL ? asprintf (&fname, "%s/%s", subdir, debuglink)
Packit 032894
	    : asprintf (&fname, "%s/%s/%s", dir, subdir, debuglink)) < 0)
Packit 032894
    return -1;
Packit 032894
Packit 032894
  struct stat st;
Packit 032894
  int fd = TEMP_FAILURE_RETRY (open (fname, O_RDONLY));
Packit 032894
  if (fd < 0)
Packit 032894
    free (fname);
Packit 032894
  else if (fstat (fd, &st) == 0
Packit 032894
	   && st.st_ino == main_stat->st_ino
Packit 032894
	   && st.st_dev == main_stat->st_dev)
Packit 032894
    {
Packit 032894
      /* This is the main file by another name.  Don't look at it again.  */
Packit 032894
      free (fname);
Packit 032894
      close (fd);
Packit 032894
      errno = ENOENT;
Packit 032894
      fd = -1;
Packit 032894
    }
Packit 032894
  else
Packit 032894
    *debuginfo_file_name = fname;
Packit 032894
Packit 032894
  return fd;
Packit 032894
}
Packit 032894
Packit 032894
/* Return true iff the FD's contents CRC matches DEBUGLINK_CRC.  */
Packit 032894
static inline bool
Packit 032894
check_crc (int fd, GElf_Word debuglink_crc)
Packit 032894
{
Packit 032894
  uint32_t file_crc;
Packit 032894
  return (__libdwfl_crc32_file (fd, &file_crc) == 0
Packit 032894
	  && file_crc == debuglink_crc);
Packit 032894
}
Packit 032894
Packit 032894
static bool
Packit 032894
validate (Dwfl_Module *mod, int fd, bool check, GElf_Word debuglink_crc)
Packit 032894
{
Packit 032894
  /* For alt debug files always check the build-id from the Dwarf and alt.  */
Packit 032894
  if (mod->dw != NULL)
Packit 032894
    {
Packit 032894
      bool valid = false;
Packit 032894
      const void *build_id;
Packit 032894
      const char *altname;
Packit 032894
      ssize_t build_id_len = INTUSE(dwelf_dwarf_gnu_debugaltlink) (mod->dw,
Packit 032894
								   &altname,
Packit 032894
								   &build_id);
Packit 032894
      if (build_id_len > 0)
Packit 032894
	{
Packit 032894
	  /* We need to open an Elf handle on the file so we can check its
Packit 032894
	     build ID note for validation.  Backdoor the handle into the
Packit 032894
	     module data structure since we had to open it early anyway.  */
Packit 032894
	  Dwfl_Error error = __libdw_open_file (&fd, &mod->alt_elf,
Packit 032894
						false, false);
Packit 032894
	  if (error != DWFL_E_NOERROR)
Packit 032894
	    __libdwfl_seterrno (error);
Packit 032894
	  else
Packit 032894
	    {
Packit 032894
	      const void *alt_build_id;
Packit 032894
	      ssize_t alt_len = INTUSE(dwelf_elf_gnu_build_id) (mod->alt_elf,
Packit 032894
								&alt_build_id);
Packit 032894
	      if (alt_len > 0 && alt_len == build_id_len
Packit 032894
		  && memcmp (build_id, alt_build_id, alt_len) == 0)
Packit 032894
		valid = true;
Packit 032894
	      else
Packit 032894
		{
Packit 032894
		  /* A mismatch!  */
Packit 032894
		  elf_end (mod->alt_elf);
Packit 032894
		  mod->alt_elf = NULL;
Packit 032894
		  close (fd);
Packit 032894
		  fd = -1;
Packit 032894
		}
Packit 032894
	    }
Packit 032894
	}
Packit 032894
      return valid;
Packit 032894
    }
Packit 032894
Packit 032894
  /* If we have a build ID, check only that.  */
Packit 032894
  if (mod->build_id_len > 0)
Packit 032894
    {
Packit 032894
      /* We need to open an Elf handle on the file so we can check its
Packit 032894
	 build ID note for validation.  Backdoor the handle into the
Packit 032894
	 module data structure since we had to open it early anyway.  */
Packit 032894
Packit 032894
      mod->debug.valid = false;
Packit 032894
      Dwfl_Error error = __libdw_open_file (&fd, &mod->debug.elf, false, false);
Packit 032894
      if (error != DWFL_E_NOERROR)
Packit 032894
	__libdwfl_seterrno (error);
Packit 032894
      else if (likely (__libdwfl_find_build_id (mod, false,
Packit 032894
						mod->debug.elf) == 2))
Packit 032894
	/* Also backdoor the gratuitous flag.  */
Packit 032894
	mod->debug.valid = true;
Packit 032894
      else
Packit 032894
	{
Packit 032894
	  /* A mismatch!  */
Packit 032894
	  elf_end (mod->debug.elf);
Packit 032894
	  mod->debug.elf = NULL;
Packit 032894
	  close (fd);
Packit 032894
	  fd = -1;
Packit 032894
	}
Packit 032894
Packit 032894
      return mod->debug.valid;
Packit 032894
    }
Packit 032894
Packit 032894
  return !check || check_crc (fd, debuglink_crc);
Packit 032894
}
Packit 032894
Packit 032894
static int
Packit 032894
find_debuginfo_in_path (Dwfl_Module *mod, const char *file_name,
Packit 032894
			const char *debuglink_file, GElf_Word debuglink_crc,
Packit 032894
			char **debuginfo_file_name)
Packit 032894
{
Packit 032894
  bool cancheck = debuglink_crc != (GElf_Word) 0;
Packit 032894
Packit 032894
  const char *file_basename = file_name == NULL ? NULL : basename (file_name);
Packit 032894
  char *localname = NULL;
Packit 032894
Packit 032894
  /* We invent a debuglink .debug name if NULL, but then want to try the
Packit 032894
     basename too.  */
Packit 032894
  bool debuglink_null = debuglink_file == NULL;
Packit 032894
  if (debuglink_null)
Packit 032894
    {
Packit 032894
      /* For a alt debug multi file we need a name, for a separate debug
Packit 032894
	 name we may be able to fall back on file_basename.debug.  */
Packit 032894
      if (file_basename == NULL || mod->dw != NULL)
Packit 032894
	{
Packit 032894
	  errno = 0;
Packit 032894
	  return -1;
Packit 032894
	}
Packit 032894
Packit 032894
      size_t len = strlen (file_basename);
Packit 032894
      localname = malloc (len + sizeof ".debug");
Packit 032894
      if (unlikely (localname == NULL))
Packit 032894
	return -1;
Packit 032894
      memcpy (localname, file_basename, len);
Packit 032894
      memcpy (&localname[len], ".debug", sizeof ".debug");
Packit 032894
      debuglink_file = localname;
Packit 032894
      cancheck = false;
Packit 032894
    }
Packit 032894
Packit 032894
  /* Look for a file named DEBUGLINK_FILE in the directories
Packit 032894
     indicated by the debug directory path setting.  */
Packit 032894
Packit 032894
  const Dwfl_Callbacks *const cb = mod->dwfl->callbacks;
Packit 032894
  char *localpath = strdup ((cb->debuginfo_path ? *cb->debuginfo_path : NULL)
Packit 032894
			    ?: DEFAULT_DEBUGINFO_PATH);
Packit 032894
  if (unlikely (localpath == NULL))
Packit 032894
    {
Packit 032894
      free (localname);
Packit 032894
      return -1;
Packit 032894
    }
Packit 032894
Packit 032894
  /* A leading - or + in the whole path sets whether to check file CRCs.  */
Packit 032894
  bool defcheck = true;
Packit 032894
  char *path = localpath;
Packit 032894
  if (path[0] == '-' || path[0] == '+')
Packit 032894
    {
Packit 032894
      defcheck = path[0] == '+';
Packit 032894
      ++path;
Packit 032894
    }
Packit 032894
Packit 032894
  /* XXX dev/ino should be cached in struct dwfl_file.  */
Packit 032894
  struct stat main_stat;
Packit 032894
  if (unlikely ((mod->main.fd != -1 ? fstat (mod->main.fd, &main_stat)
Packit 032894
		 : file_name != NULL ? stat (file_name, &main_stat)
Packit 032894
		 : -1) < 0))
Packit 032894
    {
Packit 032894
      main_stat.st_dev = 0;
Packit 032894
      main_stat.st_ino = 0;
Packit 032894
    }
Packit 032894
Packit 032894
  char *file_dirname = (file_basename == file_name ? NULL
Packit 032894
			: strndup (file_name, file_basename - 1 - file_name));
Packit 032894
  if (file_basename != file_name && file_dirname == NULL)
Packit 032894
    {
Packit 032894
      free (localpath);
Packit 032894
      free (localname);
Packit 032894
      return -1;
Packit 032894
    }
Packit 032894
  char *p;
Packit 032894
  while ((p = strsep (&path, ":")) != NULL)
Packit 032894
    {
Packit 032894
      /* A leading - or + says whether to check file CRCs for this element.  */
Packit 032894
      bool check = defcheck;
Packit 032894
      if (*p == '+' || *p == '-')
Packit 032894
	check = *p++ == '+';
Packit 032894
      check = check && cancheck;
Packit 032894
Packit 032894
      /* Try the basename too, if we made up the debuglink name and this
Packit 032894
	 is not the main directory.  */
Packit 032894
      bool try_file_basename;
Packit 032894
Packit 032894
      const char *dir, *subdir, *file;
Packit 032894
      switch (p[0])
Packit 032894
	{
Packit 032894
	case '\0':
Packit 032894
	  /* An empty entry says to try the main file's directory.  */
Packit 032894
	  dir = file_dirname;
Packit 032894
	  subdir = NULL;
Packit 032894
	  file = debuglink_file;
Packit 032894
	  try_file_basename = false;
Packit 032894
	  break;
Packit 032894
	case '/':
Packit 032894
	  /* An absolute path says to look there for a subdirectory
Packit 032894
	     named by the main file's absolute directory.  This cannot
Packit 032894
	     be applied to a relative file name.  For alt debug files
Packit 032894
	     it means to look for the basename file in that dir or the
Packit 032894
	     .dwz subdir (see below).  */
Packit 032894
	  if (mod->dw == NULL
Packit 032894
	      && (file_dirname == NULL || file_dirname[0] != '/'))
Packit 032894
	    continue;
Packit 032894
	  dir = p;
Packit 032894
	  if (mod->dw == NULL)
Packit 032894
	    {
Packit 032894
	      subdir = file_dirname;
Packit 032894
	      /* We want to explore all sub-subdirs.  Chop off one slash
Packit 032894
		 at a time.  */
Packit 032894
	    explore_dir:
Packit 032894
	      subdir = strchr (subdir, '/');
Packit 032894
	      if (subdir != NULL)
Packit 032894
		subdir = subdir + 1;
Packit 032894
	      if (subdir && *subdir == 0)
Packit 032894
		continue;
Packit 032894
	      file = debuglink_file;
Packit 032894
	    }
Packit 032894
	  else
Packit 032894
	    {
Packit 032894
	      subdir = NULL;
Packit 032894
	      file = basename (debuglink_file);
Packit 032894
	    }
Packit 032894
	  try_file_basename = debuglink_null;
Packit 032894
	  break;
Packit 032894
	default:
Packit 032894
	  /* A relative path says to try a subdirectory of that name
Packit 032894
	     in the main file's directory.  */
Packit 032894
	  dir = file_dirname;
Packit 032894
	  subdir = p;
Packit 032894
	  file = debuglink_file;
Packit 032894
	  try_file_basename = debuglink_null;
Packit 032894
	  break;
Packit 032894
	}
Packit 032894
Packit 032894
      char *fname = NULL;
Packit 032894
      int fd = try_open (&main_stat, dir, subdir, file, &fname);
Packit 032894
      if (fd < 0 && try_file_basename)
Packit 032894
	fd = try_open (&main_stat, dir, subdir, file_basename, &fname);
Packit 032894
      if (fd < 0)
Packit 032894
	switch (errno)
Packit 032894
	  {
Packit 032894
	  case ENOENT:
Packit 032894
	  case ENOTDIR:
Packit 032894
	    /* If we are looking for the alt file also try the .dwz subdir.
Packit 032894
	       But only if this is the empty or absolute path.  */
Packit 032894
	    if (mod->dw != NULL && (p[0] == '\0' || p[0] == '/'))
Packit 032894
	      {
Packit 032894
		fd = try_open (&main_stat, dir, ".dwz",
Packit 032894
			       basename (file), &fname);
Packit 032894
		if (fd < 0)
Packit 032894
		  {
Packit 032894
		    if (errno != ENOENT && errno != ENOTDIR)
Packit 032894
		      goto fail_free;
Packit 032894
		    else
Packit 032894
		      continue;
Packit 032894
		  }
Packit 032894
		break;
Packit 032894
	      }
Packit 032894
	    /* If possible try again with a sub-subdir.  */
Packit 032894
	    if (mod->dw == NULL && subdir)
Packit 032894
	      goto explore_dir;
Packit 032894
	    continue;
Packit 032894
	  default:
Packit 032894
	    goto fail_free;
Packit 032894
	  }
Packit 032894
      if (validate (mod, fd, check, debuglink_crc))
Packit 032894
	{
Packit 032894
	  free (localpath);
Packit 032894
	  free (localname);
Packit 032894
	  free (file_dirname);
Packit 032894
	  *debuginfo_file_name = fname;
Packit 032894
	  return fd;
Packit 032894
	}
Packit 032894
      free (fname);
Packit 032894
      close (fd);
Packit 032894
    }
Packit 032894
Packit 032894
  /* No dice.  */
Packit 032894
  errno = 0;
Packit 032894
fail_free:
Packit 032894
  free (localpath);
Packit 032894
  free (localname);
Packit 032894
  free (file_dirname);
Packit 032894
  return -1;
Packit 032894
}
Packit 032894
Packit 032894
int
Packit 032894
dwfl_standard_find_debuginfo (Dwfl_Module *mod,
Packit 032894
			      void **userdata __attribute__ ((unused)),
Packit 032894
			      const char *modname __attribute__ ((unused)),
Packit 032894
			      GElf_Addr base __attribute__ ((unused)),
Packit 032894
			      const char *file_name,
Packit 032894
			      const char *debuglink_file,
Packit 032894
			      GElf_Word debuglink_crc,
Packit 032894
			      char **debuginfo_file_name)
Packit 032894
{
Packit Service 35cfd5
  if (mod == NULL)
Packit Service 35cfd5
    return -1;
Packit Service 35cfd5
Packit 032894
  /* First try by build ID if we have one.  If that succeeds or fails
Packit 032894
     other than just by finding nothing, that's all we do.  */
Packit Service 35cfd5
  const unsigned char *bits = NULL;
Packit 032894
  GElf_Addr vaddr;
Packit 032894
  int bits_len;
Packit 032894
  if ((bits_len = INTUSE(dwfl_module_build_id) (mod, &bits, &vaddr)) > 0)
Packit 032894
    {
Packit 032894
      /* Dropping most arguments means we cannot rely on them in
Packit 032894
	 dwfl_build_id_find_debuginfo.  But leave it that way since
Packit 032894
	 some user code out there also does this, so we'll have to
Packit 032894
	 handle it anyway.  */
Packit 032894
      int fd = INTUSE(dwfl_build_id_find_debuginfo) (mod,
Packit 032894
						     NULL, NULL, 0,
Packit 032894
						     NULL, NULL, 0,
Packit 032894
						     debuginfo_file_name);
Packit 032894
Packit 032894
      /* Did the build_id callback find something or report an error?
Packit 032894
         Then we are done.  Otherwise fallback on path based search.  */
Packit 032894
      if (fd >= 0
Packit 032894
	  || (mod->dw == NULL && mod->debug.elf != NULL)
Packit 032894
	  || (mod->dw != NULL && mod->alt_elf != NULL)
Packit 032894
	  || errno != 0)
Packit 032894
	return fd;
Packit 032894
    }
Packit 032894
Packit 032894
  /* Failing that, search the path by name.  */
Packit 032894
  int fd = find_debuginfo_in_path (mod, file_name,
Packit 032894
				   debuglink_file, debuglink_crc,
Packit 032894
				   debuginfo_file_name);
Packit 032894
Packit 032894
  if (fd < 0 && errno == 0 && file_name != NULL)
Packit 032894
    {
Packit 032894
      /* If FILE_NAME is a symlink, the debug file might be associated
Packit 032894
	 with the symlink target name instead.  */
Packit 032894
Packit 032894
      char *canon = realpath (file_name, NULL);
Packit 032894
      if (canon != NULL && strcmp (file_name, canon))
Packit 032894
	fd = find_debuginfo_in_path (mod, canon,
Packit 032894
				     debuglink_file, debuglink_crc,
Packit 032894
				     debuginfo_file_name);
Packit 032894
      free (canon);
Packit 032894
    }
Packit 032894
Packit Service 35cfd5
  /* Still nothing? Try if we can use the debuginfod client.
Packit Service 35cfd5
     But note that we might be looking for the alt file.
Packit Service 35cfd5
     We use the same trick as dwfl_build_id_find_debuginfo.
Packit Service 35cfd5
     If the debug file (dw) is already set, then we must be
Packit Service 35cfd5
     looking for the altfile. But we cannot use the actual
Packit Service 35cfd5
     file/path name given as hint. We'll have to lookup the
Packit Service 35cfd5
     alt file "build-id". Because the debuginfod client only
Packit Service 35cfd5
     handles build-ids.  */
Packit Service 35cfd5
  if (fd < 0)
Packit Service 35cfd5
    {
Packit Service 35cfd5
      if (mod->dw != NULL)
Packit Service 35cfd5
	{
Packit Service 35cfd5
	  const char *altname;
Packit Service 35cfd5
	  bits_len = INTUSE(dwelf_dwarf_gnu_debugaltlink) (mod->dw, &altname,
Packit Service 35cfd5
							   (const void **)
Packit Service 35cfd5
							   &bits);
Packit Service 35cfd5
	}
Packit Service 35cfd5
Packit Service 35cfd5
      if (bits_len > 0)
Packit Service 35cfd5
	fd = __libdwfl_debuginfod_find_debuginfo (mod->dwfl, bits, bits_len);
Packit Service 35cfd5
    }
Packit 032894
Packit 032894
  return fd;
Packit 032894
}
Packit 032894
INTDEF (dwfl_standard_find_debuginfo)