Blame libdwfl/offline.c

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