Blame libdwfl/dwfl_build_id_find_elf.c

Packit Service 97d2fb
/* Find an ELF file for a module from its build ID.
Packit Service 97d2fb
   Copyright (C) 2007-2010, 2014, 2015, 2019 Red Hat, Inc.
Packit Service 97d2fb
   This file is part of elfutils.
Packit Service 97d2fb
Packit Service 97d2fb
   This file is free software; you can redistribute it and/or modify
Packit Service 97d2fb
   it under the terms of either
Packit Service 97d2fb
Packit Service 97d2fb
     * the GNU Lesser General Public License as published by the Free
Packit Service 97d2fb
       Software Foundation; either version 3 of the License, or (at
Packit Service 97d2fb
       your option) any later version
Packit Service 97d2fb
Packit Service 97d2fb
   or
Packit Service 97d2fb
Packit Service 97d2fb
     * the GNU General Public License as published by the Free
Packit Service 97d2fb
       Software Foundation; either version 2 of the License, or (at
Packit Service 97d2fb
       your option) any later version
Packit Service 97d2fb
Packit Service 97d2fb
   or both in parallel, as here.
Packit Service 97d2fb
Packit Service 97d2fb
   elfutils is distributed in the hope that it will be useful, but
Packit Service 97d2fb
   WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 97d2fb
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 97d2fb
   General Public License for more details.
Packit Service 97d2fb
Packit Service 97d2fb
   You should have received copies of the GNU General Public License and
Packit Service 97d2fb
   the GNU Lesser General Public License along with this program.  If
Packit Service 97d2fb
   not, see <http://www.gnu.org/licenses/>.  */
Packit Service 97d2fb
Packit Service 97d2fb
#ifdef HAVE_CONFIG_H
Packit Service 97d2fb
# include <config.h>
Packit Service 97d2fb
#endif
Packit Service 97d2fb
Packit Service 97d2fb
#include "libdwflP.h"
Packit Service 97d2fb
#include <inttypes.h>
Packit Service 97d2fb
#include <fcntl.h>
Packit Service 97d2fb
#include <unistd.h>
Packit Service 97d2fb
#include "system.h"
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
int
Packit Service 97d2fb
internal_function
Packit Service 97d2fb
__libdwfl_open_by_build_id (Dwfl_Module *mod, bool debug, char **file_name,
Packit Service 97d2fb
			    const size_t id_len, const uint8_t *id)
Packit Service 97d2fb
{
Packit Service 97d2fb
  /* We don't handle very short or really large build-ids.  We need at
Packit Service 97d2fb
     at least 3 and allow for up to 64 (normally ids are 20 long).  */
Packit Service 97d2fb
#define MIN_BUILD_ID_BYTES 3
Packit Service 97d2fb
#define MAX_BUILD_ID_BYTES 64
Packit Service 97d2fb
  if (id_len < MIN_BUILD_ID_BYTES || id_len > MAX_BUILD_ID_BYTES)
Packit Service 97d2fb
    {
Packit Service 97d2fb
    bad_id:
Packit Service 97d2fb
      __libdwfl_seterrno (DWFL_E_WRONG_ID_ELF);
Packit Service 97d2fb
      return -1;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  /* Search debuginfo_path directories' .build-id/ subdirectories.  */
Packit Service 97d2fb
Packit Service 97d2fb
  char id_name[sizeof "/.build-id/" + 1 + MAX_BUILD_ID_BYTES * 2
Packit Service 97d2fb
	       + sizeof ".debug" - 1];
Packit Service 97d2fb
  strcpy (id_name, "/.build-id/");
Packit Service 97d2fb
  int n = snprintf (&id_name[sizeof "/.build-id/" - 1],
Packit Service 97d2fb
		    4, "%02" PRIx8 "/", (uint8_t) id[0]);
Packit Service 97d2fb
  if (n != 3)
Packit Service 97d2fb
    goto bad_id;;
Packit Service 97d2fb
  for (size_t i = 1; i < id_len; ++i)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      n = snprintf (&id_name[sizeof "/.build-id/" - 1 + 3 + (i - 1) * 2],
Packit Service 97d2fb
		    3, "%02" PRIx8, (uint8_t) id[i]);
Packit Service 97d2fb
      if (n != 2)
Packit Service 97d2fb
	goto bad_id;
Packit Service 97d2fb
    }
Packit Service 97d2fb
  if (debug)
Packit Service 97d2fb
    strcpy (&id_name[sizeof "/.build-id/" - 1 + 3 + (id_len - 1) * 2],
Packit Service 97d2fb
	    ".debug");
Packit Service 97d2fb
Packit Service 97d2fb
  const Dwfl_Callbacks *const cb = mod->dwfl->callbacks;
Packit Service 97d2fb
  char *path = strdup ((cb->debuginfo_path ? *cb->debuginfo_path : NULL)
Packit Service 97d2fb
		       ?: DEFAULT_DEBUGINFO_PATH);
Packit Service 97d2fb
  if (path == NULL)
Packit Service 97d2fb
    return -1;
Packit Service 97d2fb
Packit Service 97d2fb
  int fd = -1;
Packit Service 97d2fb
  char *dir;
Packit Service 97d2fb
  char *paths = path;
Packit Service 97d2fb
  while (fd < 0 && (dir = strsep (&paths, ":")) != NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      if (dir[0] == '+' || dir[0] == '-')
Packit Service 97d2fb
	++dir;
Packit Service 97d2fb
Packit Service 97d2fb
      /* Only absolute directory names are useful to us.  */
Packit Service 97d2fb
      if (dir[0] != '/')
Packit Service 97d2fb
	continue;
Packit Service 97d2fb
Packit Service 97d2fb
      size_t dirlen = strlen (dir);
Packit Service 97d2fb
      char *name = malloc (dirlen + sizeof id_name);
Packit Service 97d2fb
      if (unlikely (name == NULL))
Packit Service 97d2fb
	break;
Packit Service 97d2fb
      memcpy (mempcpy (name, dir, dirlen), id_name, sizeof id_name);
Packit Service 97d2fb
Packit Service 97d2fb
      fd = TEMP_FAILURE_RETRY (open (name, O_RDONLY));
Packit Service 97d2fb
      if (fd >= 0)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  if (*file_name != NULL)
Packit Service 97d2fb
	    free (*file_name);
Packit Service 97d2fb
	  *file_name = realpath (name, NULL);
Packit Service 97d2fb
	  if (*file_name == NULL)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      *file_name = name;
Packit Service 97d2fb
	      name = NULL;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	}
Packit Service 97d2fb
      free (name);
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  free (path);
Packit Service 97d2fb
Packit Service 97d2fb
  /* If we simply found nothing, clear errno.  If we had some other error
Packit Service 97d2fb
     with the file, report that.  Possibly this should treat other errors
Packit Service 97d2fb
     like ENOENT too.  But ignoring all errors could mask some that should
Packit Service 97d2fb
     be reported.  */
Packit Service 97d2fb
  if (fd < 0 && errno == ENOENT)
Packit Service 97d2fb
    errno = 0;
Packit Service 97d2fb
Packit Service 97d2fb
  return fd;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
int
Packit Service 97d2fb
internal_function
Packit Service 97d2fb
__libdwfl_open_mod_by_build_id (Dwfl_Module *mod, bool debug, char **file_name)
Packit Service 97d2fb
{
Packit Service 97d2fb
  /* If *FILE_NAME was primed into the module, leave it there
Packit Service 97d2fb
     as the fallback when we have nothing to offer.  */
Packit Service 97d2fb
  errno = 0;
Packit Service 97d2fb
  if (mod->build_id_len <= 0)
Packit Service 97d2fb
    return -1;
Packit Service 97d2fb
Packit Service 97d2fb
  const size_t id_len = mod->build_id_len;
Packit Service 97d2fb
  const uint8_t *id = mod->build_id_bits;
Packit Service 97d2fb
Packit Service 97d2fb
  return __libdwfl_open_by_build_id (mod, debug, file_name, id_len, id);
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
int
Packit Service 97d2fb
dwfl_build_id_find_elf (Dwfl_Module *mod,
Packit Service 97d2fb
			void **userdata __attribute__ ((unused)),
Packit Service 97d2fb
			const char *modname __attribute__ ((unused)),
Packit Service 97d2fb
			Dwarf_Addr base __attribute__ ((unused)),
Packit Service 97d2fb
			char **file_name, Elf **elfp)
Packit Service 97d2fb
{
Packit Service 97d2fb
  *elfp = NULL;
Packit Service 97d2fb
  if (mod->is_executable
Packit Service 97d2fb
      && mod->dwfl->user_core != NULL
Packit Service 97d2fb
      && mod->dwfl->user_core->executable_for_core != NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      /* When dwfl_core_file_report was called with a non-NULL executable file
Packit Service 97d2fb
	 name this callback will replace the Dwfl_Module main.name with the
Packit Service 97d2fb
	 recorded executable file when MOD was identified as main executable
Packit Service 97d2fb
	 (which then triggers opening and reporting of the executable).  */
Packit Service 97d2fb
      const char *executable = mod->dwfl->user_core->executable_for_core;
Packit Service 97d2fb
      int fd = open (executable, O_RDONLY);
Packit Service 97d2fb
      if (fd >= 0)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  *file_name = strdup (executable);
Packit Service 97d2fb
	  if (*file_name != NULL)
Packit Service 97d2fb
	    return fd;
Packit Service 97d2fb
	  else
Packit Service 97d2fb
	    close (fd);
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
  int fd = __libdwfl_open_mod_by_build_id (mod, false, file_name);
Packit Service 97d2fb
  if (fd >= 0)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      Dwfl_Error error = __libdw_open_file (&fd, elfp, true, false);
Packit Service 97d2fb
      if (error != DWFL_E_NOERROR)
Packit Service 97d2fb
	__libdwfl_seterrno (error);
Packit Service 97d2fb
      else if (__libdwfl_find_build_id (mod, false, *elfp) == 2)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  /* This is a backdoor signal to short-circuit the ID refresh.  */
Packit Service 97d2fb
	  mod->main.valid = true;
Packit Service 97d2fb
	  return fd;
Packit Service 97d2fb
	}
Packit Service 97d2fb
      else
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  /* This file does not contain the ID it should!  */
Packit Service 97d2fb
	  elf_end (*elfp);
Packit Service 97d2fb
	  *elfp = NULL;
Packit Service 97d2fb
	  close (fd);
Packit Service 97d2fb
	  fd = -1;
Packit Service 97d2fb
	}
Packit Service 97d2fb
      free (*file_name);
Packit Service 97d2fb
      *file_name = NULL;
Packit Service 97d2fb
    }
Packit Service 97d2fb
  else
Packit Service 97d2fb
    {
Packit Service 97d2fb
#ifdef ENABLE_LIBDEBUGINFOD
Packit Service 97d2fb
      /* If all else fails and a build-id is available, query the
Packit Service 97d2fb
	 debuginfo-server if enabled.  */
Packit Service 97d2fb
      if (fd < 0 && mod->build_id_len > 0)
Packit Service 97d2fb
	fd = __libdwfl_debuginfod_find_executable (mod->dwfl,
Packit Service 97d2fb
						   mod->build_id_bits,
Packit Service 97d2fb
						   mod->build_id_len);
Packit Service 97d2fb
#endif
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  if (fd < 0 && errno == 0 && mod->build_id_len > 0)
Packit Service 97d2fb
    /* Setting this with no file yet loaded is a marker that
Packit Service 97d2fb
       the build ID is authoritative even if we also know a
Packit Service 97d2fb
       putative *FILE_NAME.  */
Packit Service 97d2fb
    mod->main.valid = true;
Packit Service 97d2fb
Packit Service 97d2fb
  return fd;
Packit Service 97d2fb
}
Packit Service 97d2fb
INTDEF (dwfl_build_id_find_elf)