Blame libdwfl/offline.c

Packit Service 97d2fb
/* Recover relocatibility for addresses computed from debug information.
Packit Service 97d2fb
   Copyright (C) 2005-2009, 2012 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 <fcntl.h>
Packit Service 97d2fb
#include <unistd.h>
Packit Service 97d2fb
Packit Service 97d2fb
/* Since dwfl_report_elf lays out the sections already, this will only be
Packit Service 97d2fb
   called when the section headers of the debuginfo file are being
Packit Service 97d2fb
   consulted instead, or for the section placed at 0.  With binutils
Packit Service 97d2fb
   strip-to-debug, the symbol table is in the debuginfo file and relocation
Packit Service 97d2fb
   looks there.  */
Packit Service 97d2fb
int
Packit Service 97d2fb
dwfl_offline_section_address (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
			      const char *secname __attribute__ ((unused)),
Packit Service 97d2fb
			      Elf32_Word shndx,
Packit Service 97d2fb
			      const GElf_Shdr *shdr __attribute__ ((unused)),
Packit Service 97d2fb
			      Dwarf_Addr *addr)
Packit Service 97d2fb
{
Packit Service 97d2fb
  assert (mod->e_type == ET_REL);
Packit Service 97d2fb
  assert (shdr->sh_addr == 0);
Packit Service 97d2fb
  assert (shdr->sh_flags & SHF_ALLOC);
Packit Service 97d2fb
  assert (shndx != 0);
Packit Service 97d2fb
Packit Service 97d2fb
  if (mod->debug.elf == NULL)
Packit Service 97d2fb
    /* We are only here because sh_addr is zero even though layout is complete.
Packit Service 97d2fb
       The first section in the first file under -e is placed at 0.  */
Packit Service 97d2fb
    return 0;
Packit Service 97d2fb
Packit Service 97d2fb
  /* The section numbers might not match between the two files.
Packit Service 97d2fb
     The best we can rely on is the order of SHF_ALLOC sections.  */
Packit Service 97d2fb
Packit Service 97d2fb
  Elf_Scn *ourscn = elf_getscn (mod->debug.elf, shndx);
Packit Service 97d2fb
  Elf_Scn *scn = NULL;
Packit Service 97d2fb
  uint_fast32_t skip_alloc = 0;
Packit Service 97d2fb
  while ((scn = elf_nextscn (mod->debug.elf, scn)) != ourscn)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      assert (scn != NULL);
Packit Service 97d2fb
      GElf_Shdr shdr_mem;
Packit Service 97d2fb
      GElf_Shdr *sh = gelf_getshdr (scn, &shdr_mem);
Packit Service 97d2fb
      if (unlikely (sh == NULL))
Packit Service 97d2fb
	return -1;
Packit Service 97d2fb
      if (sh->sh_flags & SHF_ALLOC)
Packit Service 97d2fb
	++skip_alloc;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  scn = NULL;
Packit Service 97d2fb
  while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      GElf_Shdr shdr_mem;
Packit Service 97d2fb
      GElf_Shdr *main_shdr = gelf_getshdr (scn, &shdr_mem);
Packit Service 97d2fb
      if (unlikely (main_shdr == NULL))
Packit Service 97d2fb
	return -1;
Packit Service 97d2fb
      if ((main_shdr->sh_flags & SHF_ALLOC) && skip_alloc-- == 0)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  assert (main_shdr->sh_flags == shdr->sh_flags);
Packit Service 97d2fb
	  *addr = main_shdr->sh_addr;
Packit Service 97d2fb
	  return 0;
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  /* This should never happen.  */
Packit Service 97d2fb
  return -1;
Packit Service 97d2fb
}
Packit Service 97d2fb
INTDEF (dwfl_offline_section_address)
Packit Service 97d2fb
Packit Service 97d2fb
/* Forward declarations.  */
Packit Service 97d2fb
static Dwfl_Module *process_elf (Dwfl *dwfl, const char *name,
Packit Service 97d2fb
				 const char *file_name, int fd, Elf *elf);
Packit Service 97d2fb
static Dwfl_Module *process_archive (Dwfl *dwfl, const char *name,
Packit Service 97d2fb
				     const char *file_name, int fd, Elf *elf,
Packit Service 97d2fb
				     int (*predicate) (const char *module,
Packit Service 97d2fb
						       const char *file));
Packit Service 97d2fb
Packit Service 97d2fb
/* Report one module for an ELF file, or many for an archive.
Packit Service 97d2fb
   Always consumes ELF and FD.  */
Packit Service 97d2fb
static Dwfl_Module *
Packit Service 97d2fb
process_file (Dwfl *dwfl, const char *name, const char *file_name, int fd,
Packit Service 97d2fb
	      Elf *elf, int (*predicate) (const char *module,
Packit Service 97d2fb
					  const char *file))
Packit Service 97d2fb
{
Packit Service 97d2fb
  switch (elf_kind (elf))
Packit Service 97d2fb
    {
Packit Service 97d2fb
    default:
Packit Service 97d2fb
    case ELF_K_NONE:
Packit Service 97d2fb
      __libdwfl_seterrno (elf == NULL ? DWFL_E_LIBELF : DWFL_E_BADELF);
Packit Service 97d2fb
      return NULL;
Packit Service 97d2fb
Packit Service 97d2fb
    case ELF_K_ELF:
Packit Service 97d2fb
      return process_elf (dwfl, name, file_name, fd, elf);
Packit Service 97d2fb
Packit Service 97d2fb
    case ELF_K_AR:
Packit Service 97d2fb
      return process_archive (dwfl, name, file_name, fd, elf, predicate);
Packit Service 97d2fb
    }
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
/* Report the open ELF file as a module.  Always consumes ELF and FD.  */
Packit Service 97d2fb
static Dwfl_Module *
Packit Service 97d2fb
process_elf (Dwfl *dwfl, const char *name, const char *file_name, int fd,
Packit Service 97d2fb
	     Elf *elf)
Packit Service 97d2fb
{
Packit Service 97d2fb
  Dwfl_Module *mod = __libdwfl_report_elf (dwfl, name, file_name, fd, elf,
Packit Service 97d2fb
					   dwfl->offline_next_address, true,
Packit Service 97d2fb
					   false);
Packit Service 97d2fb
  if (mod != NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      /* If this is an ET_EXEC file with fixed addresses, the address range
Packit Service 97d2fb
	 it consumed may or may not intersect with the arbitrary range we
Packit Service 97d2fb
	 will use for relocatable modules.  Make sure we always use a free
Packit Service 97d2fb
	 range for the offline allocations.  If this module did use
Packit Service 97d2fb
	 offline_next_address, it may have rounded it up for the module's
Packit Service 97d2fb
	 alignment requirements.  */
Packit Service 97d2fb
      if ((dwfl->offline_next_address >= mod->low_addr
Packit Service 97d2fb
	   || mod->low_addr - dwfl->offline_next_address < OFFLINE_REDZONE)
Packit Service 97d2fb
	  && dwfl->offline_next_address < mod->high_addr + OFFLINE_REDZONE)
Packit Service 97d2fb
	dwfl->offline_next_address = mod->high_addr + OFFLINE_REDZONE;
Packit Service 97d2fb
Packit Service 97d2fb
      /* Don't keep the file descriptor around.  */
Packit Service 97d2fb
      if (mod->main.fd != -1 && elf_cntl (mod->main.elf, ELF_C_FDREAD) == 0)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  /* Grab the dir path in case we want to report this file as
Packit Service 97d2fb
	     Dwarf later.  */
Packit Service 97d2fb
	  mod->elfdir = __libdw_debugdir (mod->main.fd);
Packit Service 97d2fb
	  close (mod->main.fd);
Packit Service 97d2fb
	  mod->main.fd = -1;
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  return mod;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
/* Always consumes MEMBER.  Returns elf_next result on success.
Packit Service 97d2fb
   For errors returns ELF_C_NULL with *MOD set to null.  */
Packit Service 97d2fb
static Elf_Cmd
Packit Service 97d2fb
process_archive_member (Dwfl *dwfl, const char *name, const char *file_name,
Packit Service 97d2fb
			int (*predicate) (const char *module, const char *file),
Packit Service 97d2fb
			int fd, Elf *member, Dwfl_Module **mod)
Packit Service 97d2fb
{
Packit Service 97d2fb
  const Elf_Arhdr *h = elf_getarhdr (member);
Packit Service 97d2fb
  if (unlikely (h == NULL))
Packit Service 97d2fb
    {
Packit Service 97d2fb
      __libdwfl_seterrno (DWFL_E_LIBELF);
Packit Service 97d2fb
    fail:
Packit Service 97d2fb
      elf_end (member);
Packit Service 97d2fb
      *mod = NULL;
Packit Service 97d2fb
      return ELF_C_NULL;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  if (!strcmp (h->ar_name, "/") || !strcmp (h->ar_name, "//")
Packit Service 97d2fb
      || !strcmp (h->ar_name, "/SYM64/"))
Packit Service 97d2fb
    {
Packit Service 97d2fb
    skip:;
Packit Service 97d2fb
      /* Skip this and go to the next.  */
Packit Service 97d2fb
      Elf_Cmd result = elf_next (member);
Packit Service 97d2fb
      elf_end (member);
Packit Service 97d2fb
      return result;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  char *member_name;
Packit Service 97d2fb
  if (unlikely (asprintf (&member_name, "%s(%s)", file_name, h->ar_name) < 0))
Packit Service 97d2fb
    {
Packit Service 97d2fb
    nomem:
Packit Service 97d2fb
      __libdwfl_seterrno (DWFL_E_NOMEM);
Packit Service 97d2fb
      elf_end (member);
Packit Service 97d2fb
      *mod = NULL;
Packit Service 97d2fb
      return ELF_C_NULL;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  char *module_name = NULL;
Packit Service 97d2fb
  if (name == NULL || name[0] == '\0')
Packit Service 97d2fb
    name = h->ar_name;
Packit Service 97d2fb
  else if (unlikely (asprintf (&module_name, "%s:%s", name, h->ar_name) < 0))
Packit Service 97d2fb
    {
Packit Service 97d2fb
      free (member_name);
Packit Service 97d2fb
      goto nomem;
Packit Service 97d2fb
    }
Packit Service 97d2fb
  else
Packit Service 97d2fb
    name = module_name;
Packit Service 97d2fb
Packit Service 97d2fb
  if (predicate != NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      /* Let the predicate decide whether to use this one.  */
Packit Service 97d2fb
      int want = (*predicate) (name, member_name);
Packit Service 97d2fb
      if (want <= 0)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  free (member_name);
Packit Service 97d2fb
	  free (module_name);
Packit Service 97d2fb
	  if (unlikely (want < 0))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      __libdwfl_seterrno (DWFL_E_CB);
Packit Service 97d2fb
	      goto fail;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  goto skip;
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  /* We let __libdwfl_report_elf cache the fd in mod->main.fd,
Packit Service 97d2fb
     though it's the same fd for all the members.
Packit Service 97d2fb
     On module teardown we will close it only on the last Elf reference.  */
Packit Service 97d2fb
  *mod = process_file (dwfl, name, member_name, fd, member, predicate);
Packit Service 97d2fb
  free (member_name);
Packit Service 97d2fb
  free (module_name);
Packit Service 97d2fb
Packit Service 97d2fb
  if (*mod == NULL)		/* process_file called elf_end.  */
Packit Service 97d2fb
    return ELF_C_NULL;
Packit Service 97d2fb
Packit Service 97d2fb
  /* Advance the archive-reading offset for the next iteration.  */
Packit Service 97d2fb
  return elf_next (member);
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
/* Report each member of the archive as its own module.  */
Packit Service 97d2fb
static Dwfl_Module *
Packit Service 97d2fb
process_archive (Dwfl *dwfl, const char *name, const char *file_name, int fd,
Packit Service 97d2fb
		 Elf *archive,
Packit Service 97d2fb
		 int (*predicate) (const char *module, const char *file))
Packit Service 97d2fb
Packit Service 97d2fb
{
Packit Service 97d2fb
  Dwfl_Module *mod = NULL;
Packit Service 97d2fb
  Elf *member = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, archive);
Packit Service 97d2fb
  if (unlikely (member == NULL)) /* Empty archive.  */
Packit Service 97d2fb
    {
Packit Service 97d2fb
      __libdwfl_seterrno (DWFL_E_BADELF);
Packit Service 97d2fb
      return NULL;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  while (process_archive_member (dwfl, name, file_name, predicate,
Packit Service 97d2fb
				 fd, member, &mod) != ELF_C_NULL)
Packit Service 97d2fb
    member = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, archive);
Packit Service 97d2fb
Packit Service 97d2fb
  /* We can drop the archive Elf handle even if we're still using members
Packit Service 97d2fb
     in live modules.  When the last module's elf_end on a member returns
Packit Service 97d2fb
     zero, that module will close FD.  If no modules survived the predicate,
Packit Service 97d2fb
     we are all done with the file right here.  */
Packit Service 97d2fb
  if (mod != NULL		/* If no modules, caller will clean up.  */
Packit Service 97d2fb
      && elf_end (archive) == 0)
Packit Service 97d2fb
    close (fd);
Packit Service 97d2fb
Packit Service 97d2fb
  return mod;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
Dwfl_Module *
Packit Service 97d2fb
internal_function
Packit Service 97d2fb
__libdwfl_report_offline (Dwfl *dwfl, const char *name,
Packit Service 97d2fb
			  const char *file_name, int fd, bool closefd,
Packit Service 97d2fb
			  int (*predicate) (const char *module,
Packit Service 97d2fb
					    const char *file))
Packit Service 97d2fb
{
Packit Service 97d2fb
  Elf *elf;
Packit Service 97d2fb
  Dwfl_Error error = __libdw_open_file (&fd, &elf, closefd, true);
Packit Service 97d2fb
  if (error != DWFL_E_NOERROR)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      __libdwfl_seterrno (error);
Packit Service 97d2fb
      return NULL;
Packit Service 97d2fb
    }
Packit Service 97d2fb
  Dwfl_Module *mod = process_file (dwfl, name, file_name, fd, elf, predicate);
Packit Service 97d2fb
  if (mod == NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      elf_end (elf);
Packit Service 97d2fb
      if (closefd)
Packit Service 97d2fb
	close (fd);
Packit Service 97d2fb
    }
Packit Service 97d2fb
  return mod;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
Dwfl_Module *
Packit Service 97d2fb
dwfl_report_offline (Dwfl *dwfl, const char *name,
Packit Service 97d2fb
		     const char *file_name, int fd)
Packit Service 97d2fb
{
Packit Service 97d2fb
  if (dwfl == NULL)
Packit Service 97d2fb
    return NULL;
Packit Service 97d2fb
Packit Service 97d2fb
  bool closefd = false;
Packit Service 97d2fb
  if (fd < 0)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      closefd = true;
Packit Service 97d2fb
      fd = open (file_name, O_RDONLY);
Packit Service 97d2fb
      if (fd < 0)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  __libdwfl_seterrno (DWFL_E_ERRNO);
Packit Service 97d2fb
	  return NULL;
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  return __libdwfl_report_offline (dwfl, name, file_name, fd, closefd, NULL);
Packit Service 97d2fb
}
Packit Service 97d2fb
INTDEF (dwfl_report_offline)