Blame libdw/dwarf_getscopes.c

Packit Service 97d2fb
/* Return scope DIEs containing PC address.
Packit Service 97d2fb
   Copyright (C) 2005, 2007, 2015 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 <assert.h>
Packit Service 97d2fb
#include <stdlib.h>
Packit Service 97d2fb
#include "libdwP.h"
Packit Service 97d2fb
#include <dwarf.h>
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
struct args
Packit Service 97d2fb
{
Packit Service 97d2fb
  Dwarf_Addr pc;
Packit Service 97d2fb
  Dwarf_Die *scopes;
Packit Service 97d2fb
  unsigned int inlined, nscopes;
Packit Service 97d2fb
  Dwarf_Die inlined_origin;
Packit Service 97d2fb
};
Packit Service 97d2fb
Packit Service 97d2fb
/* Preorder visitor: prune the traversal if this DIE does not contain PC.  */
Packit Service 97d2fb
static int
Packit Service 97d2fb
pc_match (unsigned int depth, struct Dwarf_Die_Chain *die, void *arg)
Packit Service 97d2fb
{
Packit Service 97d2fb
  struct args *a = arg;
Packit Service 97d2fb
Packit Service 97d2fb
  if (a->scopes != NULL)
Packit Service 97d2fb
    die->prune = true;
Packit Service 97d2fb
  else
Packit Service 97d2fb
    {
Packit Service 97d2fb
      /* dwarf_haspc returns an error if there are no appropriate attributes.
Packit Service 97d2fb
	 But we use it indiscriminantly instead of presuming which tags can
Packit Service 97d2fb
	 have PC attributes.  So when it fails for that reason, treat it just
Packit Service 97d2fb
	 as a nonmatching return.  */
Packit Service 97d2fb
      int result = INTUSE(dwarf_haspc) (&die->die, a->pc);
Packit Service 97d2fb
      if (result < 0)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  int error = INTUSE(dwarf_errno) ();
Packit Service 97d2fb
	  if (error != DWARF_E_NOERROR
Packit Service 97d2fb
	      && error != DWARF_E_NO_DEBUG_RANGES
Packit Service 97d2fb
	      && error != DWARF_E_NO_DEBUG_RNGLISTS)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      __libdw_seterrno (error);
Packit Service 97d2fb
	      return -1;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  result = 0;
Packit Service 97d2fb
	}
Packit Service 97d2fb
      if (result == 0)
Packit Service 97d2fb
    	die->prune = true;
Packit Service 97d2fb
Packit Service 97d2fb
      if (!die->prune
Packit Service 97d2fb
	  && INTUSE (dwarf_tag) (&die->die) == DW_TAG_inlined_subroutine)
Packit Service 97d2fb
	a->inlined = depth;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  return 0;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
/* Preorder visitor for second partial traversal after finding a
Packit Service 97d2fb
   concrete inlined instance.  */
Packit Service 97d2fb
static int
Packit Service 97d2fb
origin_match (unsigned int depth, struct Dwarf_Die_Chain *die, void *arg)
Packit Service 97d2fb
{
Packit Service 97d2fb
  struct args *a = arg;
Packit Service 97d2fb
Packit Service 97d2fb
  if (die->die.addr != a->inlined_origin.addr)
Packit Service 97d2fb
    return 0;
Packit Service 97d2fb
Packit Service 97d2fb
  /* We have a winner!  This is the abstract definition of the inline
Packit Service 97d2fb
     function of which A->scopes[A->nscopes - 1] is a concrete instance.
Packit Service 97d2fb
  */
Packit Service 97d2fb
Packit Service 97d2fb
  unsigned int nscopes = a->nscopes + depth;
Packit Service 97d2fb
  Dwarf_Die *scopes = realloc (a->scopes, nscopes * sizeof scopes[0]);
Packit Service 97d2fb
  if (scopes == NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      free (a->scopes);
Packit Service 97d2fb
      __libdw_seterrno (DWARF_E_NOMEM);
Packit Service 97d2fb
      return -1;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  a->scopes = scopes;
Packit Service 97d2fb
  do
Packit Service 97d2fb
    {
Packit Service 97d2fb
      die = die->parent;
Packit Service 97d2fb
      scopes[a->nscopes++] = die->die;
Packit Service 97d2fb
    }
Packit Service 97d2fb
  while (a->nscopes < nscopes);
Packit Service 97d2fb
  assert (die->parent == NULL);
Packit Service 97d2fb
  return a->nscopes;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
/* Postorder visitor: first (innermost) call wins.  */
Packit Service 97d2fb
static int
Packit Service 97d2fb
pc_record (unsigned int depth, struct Dwarf_Die_Chain *die, void *arg)
Packit Service 97d2fb
{
Packit Service 97d2fb
  struct args *a = arg;
Packit Service 97d2fb
Packit Service 97d2fb
  if (die->prune)
Packit Service 97d2fb
    return 0;
Packit Service 97d2fb
Packit Service 97d2fb
  if (a->scopes == NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      /* We have hit the innermost DIE that contains the target PC.  */
Packit Service 97d2fb
Packit Service 97d2fb
      a->nscopes = depth + 1 - a->inlined;
Packit Service 97d2fb
      a->scopes = malloc (a->nscopes * sizeof a->scopes[0]);
Packit Service 97d2fb
      if (a->scopes == NULL)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  __libdw_seterrno (DWARF_E_NOMEM);
Packit Service 97d2fb
	  return -1;
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      for (unsigned int i = 0; i < a->nscopes; ++i)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  a->scopes[i] = die->die;
Packit Service 97d2fb
	  die = die->parent;
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      if (a->inlined == 0)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  assert (die == NULL);
Packit Service 97d2fb
	  return a->nscopes;
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      /* This is the concrete inlined instance itself.
Packit Service 97d2fb
	 Record its abstract_origin pointer.  */
Packit Service 97d2fb
      Dwarf_Die *const inlinedie = &a->scopes[depth - a->inlined];
Packit Service 97d2fb
Packit Service 97d2fb
      assert (INTUSE (dwarf_tag) (inlinedie) == DW_TAG_inlined_subroutine);
Packit Service 97d2fb
      Dwarf_Attribute attr_mem;
Packit Service 97d2fb
      Dwarf_Attribute *attr = INTUSE (dwarf_attr) (inlinedie,
Packit Service 97d2fb
						   DW_AT_abstract_origin,
Packit Service 97d2fb
						   &attr_mem);
Packit Service 97d2fb
      if (INTUSE (dwarf_formref_die) (attr, &a->inlined_origin) == NULL)
Packit Service 97d2fb
	return -1;
Packit Service 97d2fb
      return 0;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
  /* We've recorded the scopes back to one that is a concrete inlined
Packit Service 97d2fb
     instance.  Now return out of the traversal back to the scope
Packit Service 97d2fb
     containing that instance.  */
Packit Service 97d2fb
Packit Service 97d2fb
  assert (a->inlined);
Packit Service 97d2fb
  if (depth >= a->inlined)
Packit Service 97d2fb
    /* Not there yet.  */
Packit Service 97d2fb
    return 0;
Packit Service 97d2fb
Packit Service 97d2fb
  /* Now we are in a scope that contains the concrete inlined instance.
Packit Service 97d2fb
     Search it for the inline function's abstract definition.
Packit Service 97d2fb
     If we don't find it, return to search the containing scope.
Packit Service 97d2fb
     If we do find it, the nonzero return value will bail us out
Packit Service 97d2fb
     of the postorder traversal.  */
Packit Service 97d2fb
  return __libdw_visit_scopes (depth, die, NULL, &origin_match, NULL, a);
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
int
Packit Service 97d2fb
dwarf_getscopes (Dwarf_Die *cudie, Dwarf_Addr pc, Dwarf_Die **scopes)
Packit Service 97d2fb
{
Packit Service 97d2fb
  if (cudie == NULL)
Packit Service 97d2fb
    return -1;
Packit Service 97d2fb
Packit Service 97d2fb
  struct Dwarf_Die_Chain cu = { .parent = NULL, .die = *cudie };
Packit Service 97d2fb
  struct args a = { .pc = pc };
Packit Service 97d2fb
Packit Service 97d2fb
  int result = __libdw_visit_scopes (0, &cu, NULL, &pc_match, &pc_record, &a);
Packit Service 97d2fb
Packit Service 97d2fb
  if (result == 0 && a.scopes != NULL)
Packit Service 97d2fb
    result = __libdw_visit_scopes (0, &cu, NULL, &origin_match, NULL, &a);
Packit Service 97d2fb
Packit Service 97d2fb
  if (result > 0)
Packit Service 97d2fb
    *scopes = a.scopes;
Packit Service 97d2fb
Packit Service 97d2fb
  return result;
Packit Service 97d2fb
}