Squashed 'third_party/elfutils/' content from commit 555e15e
Change-Id: I61cde98949e47e5c8c09c33260de17f30921be79
git-subtree-dir: third_party/elfutils
git-subtree-split: 555e15ebe8bf1eb33d00747173cfc80cc65648a4
diff --git a/libdw/dwarf_getscopes.c b/libdw/dwarf_getscopes.c
new file mode 100644
index 0000000..df480d3
--- /dev/null
+++ b/libdw/dwarf_getscopes.c
@@ -0,0 +1,201 @@
+/* Return scope DIEs containing PC address.
+ Copyright (C) 2005, 2007, 2015 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <assert.h>
+#include <stdlib.h>
+#include "libdwP.h"
+#include <dwarf.h>
+
+
+struct args
+{
+ Dwarf_Addr pc;
+ Dwarf_Die *scopes;
+ unsigned int inlined, nscopes;
+ Dwarf_Die inlined_origin;
+};
+
+/* Preorder visitor: prune the traversal if this DIE does not contain PC. */
+static int
+pc_match (unsigned int depth, struct Dwarf_Die_Chain *die, void *arg)
+{
+ struct args *a = arg;
+
+ if (a->scopes != NULL)
+ die->prune = true;
+ else
+ {
+ /* dwarf_haspc returns an error if there are no appropriate attributes.
+ But we use it indiscriminantly instead of presuming which tags can
+ have PC attributes. So when it fails for that reason, treat it just
+ as a nonmatching return. */
+ int result = INTUSE(dwarf_haspc) (&die->die, a->pc);
+ if (result < 0)
+ {
+ int error = INTUSE(dwarf_errno) ();
+ if (error != DWARF_E_NOERROR && error != DWARF_E_NO_DEBUG_RANGES)
+ {
+ __libdw_seterrno (error);
+ return -1;
+ }
+ result = 0;
+ }
+ if (result == 0)
+ die->prune = true;
+
+ if (!die->prune
+ && INTUSE (dwarf_tag) (&die->die) == DW_TAG_inlined_subroutine)
+ a->inlined = depth;
+ }
+
+ return 0;
+}
+
+/* Preorder visitor for second partial traversal after finding a
+ concrete inlined instance. */
+static int
+origin_match (unsigned int depth, struct Dwarf_Die_Chain *die, void *arg)
+{
+ struct args *a = arg;
+
+ if (die->die.addr != a->inlined_origin.addr)
+ return 0;
+
+ /* We have a winner! This is the abstract definition of the inline
+ function of which A->scopes[A->nscopes - 1] is a concrete instance.
+ */
+
+ unsigned int nscopes = a->nscopes + depth;
+ Dwarf_Die *scopes = realloc (a->scopes, nscopes * sizeof scopes[0]);
+ if (scopes == NULL)
+ {
+ free (a->scopes);
+ __libdw_seterrno (DWARF_E_NOMEM);
+ return -1;
+ }
+
+ a->scopes = scopes;
+ do
+ {
+ die = die->parent;
+ scopes[a->nscopes++] = die->die;
+ }
+ while (a->nscopes < nscopes);
+ assert (die->parent == NULL);
+ return a->nscopes;
+}
+
+/* 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, NULL, &origin_match, NULL, a);
+}
+
+
+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, NULL, &pc_match, &pc_record, &a);
+
+ if (result == 0 && a.scopes != NULL)
+ result = __libdw_visit_scopes (0, &cu, NULL, &origin_match, NULL, &a);
+
+ if (result > 0)
+ *scopes = a.scopes;
+
+ return result;
+}