int
dwarf_getscopes (Dwarf_Die *cudie, Dwarf_Addr pc, Dwarf_Die **scopes)
{
  if (cudie == NULL)
    return -1;

  struct Dwarf_Die_Chain cu = { .parent = NULL, .die = *cudie };
  struct args a = { .pc = pc };

  int result = __libdw_visit_scopes (0, &cu, &pc_match, &pc_record, &a);

  if (result == 0 && a.scopes != NULL)
    result = __libdw_visit_scopes (0, &cu, &origin_match, NULL, &a);

  if (result > 0)
    *scopes = a.scopes;

  return result;
}
/* Postorder visitor: first (innermost) call wins.  */
static int
pc_record (unsigned int depth, struct Dwarf_Die_Chain *die, void *arg)
{
  struct args *a = arg;

  if (die->prune)
    return 0;

  if (a->scopes == NULL)
    {
      /* We have hit the innermost DIE that contains the target PC.  */

      a->nscopes = depth + 1 - a->inlined;
      a->scopes = malloc (a->nscopes * sizeof a->scopes[0]);
      if (a->scopes == NULL)
	{
	  __libdw_seterrno (DWARF_E_NOMEM);
	  return -1;
	}

      for (unsigned int i = 0; i < a->nscopes; ++i)
	{
	  a->scopes[i] = die->die;
	  die = die->parent;
	}

      if (a->inlined == 0)
	{
	  assert (die == NULL);
	  return a->nscopes;
	}

      /* This is the concrete inlined instance itself.
	 Record its abstract_origin pointer.  */
      Dwarf_Die *const inlinedie = &a->scopes[depth - a->inlined];

      assert (INTUSE (dwarf_tag) (inlinedie) == DW_TAG_inlined_subroutine);
      Dwarf_Attribute attr_mem;
      Dwarf_Attribute *attr = INTUSE (dwarf_attr) (inlinedie,
						   DW_AT_abstract_origin,
						   &attr_mem);
      if (INTUSE (dwarf_formref_die) (attr, &a->inlined_origin) == NULL)
	return -1;
      return 0;
    }


  /* We've recorded the scopes back to one that is a concrete inlined
     instance.  Now return out of the traversal back to the scope
     containing that instance.  */

  assert (a->inlined);
  if (depth >= a->inlined)
    /* Not there yet.  */
    return 0;

  /* Now we are in a scope that contains the concrete inlined instance.
     Search it for the inline function's abstract definition.
     If we don't find it, return to search the containing scope.
     If we do find it, the nonzero return value will bail us out
     of the postorder traversal.  */
  return __libdw_visit_scopes (depth, die, &origin_match, NULL, a);
}
示例#3
0
int
internal_function
__libdw_visit_scopes (unsigned int depth, struct Dwarf_Die_Chain *root,
		      struct Dwarf_Die_Chain *imports,
		      int (*previsit) (unsigned int,
				       struct Dwarf_Die_Chain *,
				       void *),
		      int (*postvisit) (unsigned int,
					struct Dwarf_Die_Chain *,
					void *),
		      void *arg)
{
  struct Dwarf_Die_Chain child;
  int ret;

  child.parent = root;
  if ((ret = INTUSE(dwarf_child) (&root->die, &child.die)) != 0)
    return ret < 0 ? -1 : 0; // Having zero children is legal.

  inline int recurse (void)
    {
      return __libdw_visit_scopes (depth + 1, &child, imports,
				   previsit, postvisit, arg);
    }

  /* Checks the given DIE hasn't been imported yet to prevent cycles.  */
  inline bool imports_contains (Dwarf_Die *die)
  {
    for (struct Dwarf_Die_Chain *import = imports; import != NULL;
	 import = import->parent)
      if (import->die.addr == die->addr)
	return true;

    return false;
  }

  inline int walk_children (void)
{
    do
      {
	/* For an imported unit, it is logically as if the children of
	   that unit are siblings of the other children.  So don't do
	   a full recursion into the imported unit, but just walk the
	   children in place before moving to the next real child.  */
	while (INTUSE(dwarf_tag) (&child.die) == DW_TAG_imported_unit)
	  {
	    Dwarf_Die orig_child_die = child.die;
	    Dwarf_Attribute attr_mem;
	    Dwarf_Attribute *attr = INTUSE(dwarf_attr) (&child.die,
							DW_AT_import,
							&attr_mem);
	    if (INTUSE(dwarf_formref_die) (attr, &child.die) != NULL
                && INTUSE(dwarf_child) (&child.die, &child.die) == 0)
	      {
		if (imports_contains (&orig_child_die))
		  {
		    __libdw_seterrno (DWARF_E_INVALID_DWARF);
		    return -1;
		  }
		struct Dwarf_Die_Chain *orig_imports = imports;
		struct Dwarf_Die_Chain import = { .die = orig_child_die,
						  .parent = orig_imports };
		imports = &import;
		int result = walk_children ();
		imports = orig_imports;
		if (result != DWARF_CB_OK)
		  return result;
	      }

	    /* Any "real" children left?  */
	    if ((ret = INTUSE(dwarf_siblingof) (&orig_child_die,
						&child.die)) != 0)
	      return ret < 0 ? -1 : 0;
	  };

	child.prune = false;

	/* previsit is declared NN */
	int result = (*previsit) (depth + 1, &child, arg);
	if (result != DWARF_CB_OK)
	  return result;

	if (!child.prune && may_have_scopes (&child.die)
	    && INTUSE(dwarf_haschildren) (&child.die))
	  {
	    result = recurse ();
	    if (result != DWARF_CB_OK)
	      return result;
	  }

	if (postvisit != NULL)
	  {
	    result = (*postvisit) (depth + 1, &child, arg);
	    if (result != DWARF_CB_OK)
	      return result;
	  }
      }
    while ((ret = INTUSE(dwarf_siblingof) (&child.die, &child.die)) == 0);

    return ret < 0 ? -1 : 0;
  }

  return walk_children ();
}