summaryrefslogtreecommitdiff
path: root/libdw/dwarf_getscopes.c
diff options
context:
space:
mode:
Diffstat (limited to 'libdw/dwarf_getscopes.c')
-rw-r--r--libdw/dwarf_getscopes.c334
1 files changed, 334 insertions, 0 deletions
diff --git a/libdw/dwarf_getscopes.c b/libdw/dwarf_getscopes.c
new file mode 100644
index 00000000..21d6f20a
--- /dev/null
+++ b/libdw/dwarf_getscopes.c
@@ -0,0 +1,334 @@
+/* Return scope DIEs containing PC address.
+ Copyright (C) 2005 Red Hat, Inc.
+
+ This program is Open Source software; you can redistribute it and/or
+ modify it under the terms of the Open Software License version 1.0 as
+ published by the Open Source Initiative.
+
+ You should have received a copy of the Open Software License along
+ with this program; if not, you may obtain a copy of the Open Software
+ License version 1.0 from http://www.opensource.org/licenses/osl.php or
+ by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
+ 3001 King Ranch Road, Ukiah, CA 95482. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#include "libdwP.h"
+#include <dwarf.h>
+
+
+enum die_class { ignore, match, match_inline, walk, imported };
+
+static enum die_class
+classify_die (Dwarf_Die *die)
+{
+ switch (INTUSE(dwarf_tag) (die))
+ {
+ /* DIEs with addresses we can try to match. */
+ case DW_TAG_compile_unit:
+ case DW_TAG_module:
+ case DW_TAG_lexical_block:
+ case DW_TAG_with_stmt:
+ case DW_TAG_catch_block:
+ case DW_TAG_try_block:
+ case DW_TAG_entry_point:
+ return match;
+ case DW_TAG_inlined_subroutine:
+ return match_inline;
+ case DW_TAG_subprogram:
+ /* This might be a concrete out-of-line instance of an inline, in
+ which case it is not guaranteed to be owned by the right scope and
+ we will search for its origin as for DW_TAG_inlined_subroutine. */
+ return (INTUSE(dwarf_hasattr) (die, DW_AT_abstract_origin)
+ ? match_inline : match);
+
+ /* DIEs without addresses that can own DIEs with addresses. */
+ case DW_TAG_namespace:
+ return walk;
+
+ /* Special indirection required. */
+ case DW_TAG_imported_unit:
+ return imported;
+
+ /* Other DIEs we have no reason to descend. */
+ default:
+ break;
+ }
+ return ignore;
+}
+
+/* DIE contains PC. Find its child that contains PC. Returns -1 for
+ errors, 0 for no matches. On success, *SCOPES gets the malloc'd array
+ of containing scopes. A positive return value is the number of those
+ scopes. A return value < -1 is -1 - number of those scopes, when the
+ outermost scope is a concrete instance of an inline subroutine. */
+static int
+find_pc (unsigned int depth, Dwarf_Die *die, Dwarf_Addr pc, Dwarf_Die **scopes)
+{
+ Dwarf_Die child;
+ if (INTUSE(dwarf_child) (die, &child) != 0)
+ return -1;
+
+ /* Recurse on this DIE to search within its children.
+ Return nonzero if this gets an error or a final result. */
+ inline int search_child (void)
+ {
+ int n = find_pc (depth + 1, &child, pc, scopes);
+ if (n > 0)
+ /* That stored the N innermost scopes. Now store ours. */
+ (*scopes)[n++] = child;
+ return n;
+ }
+
+ /* Check each of our child DIEs. */
+ enum die_class got = ignore;
+ do
+ {
+ enum die_class child_class = classify_die (&child);
+ switch (child_class)
+ {
+ case match:
+ case match_inline:
+ if (INTUSE(dwarf_haspc) (&child, pc) > 0)
+ break;
+ continue;
+
+ case walk:
+ if (INTUSE(dwarf_haschildren) (&child))
+ got = walk;
+ continue;
+
+ case imported:
+ got = walk;
+ continue;
+
+ default:
+ case ignore:
+ continue;
+ }
+
+ /* We get here only when the PC has matched. */
+ got = child_class;
+ break;
+ }
+ while (INTUSE(dwarf_siblingof) (&child, &child) == 0);
+
+ switch (got)
+ {
+ case match:
+ case match_inline:
+ /* We have a DIE that matched the PC. */
+ if (INTUSE(dwarf_haschildren) (&child))
+ {
+ /* Recurse on this DIE to narrow within its children.
+ Return now if this gets an error or a final result. */
+ int result = search_child ();
+ if (result < 0 || (got == match && result > 0))
+ return result;
+ if (result > 0) /* got == match_inline */
+ /* We have a winner, but CHILD is a concrete inline instance
+ so DIE and its containing scopes do not actually apply.
+ DIE is the scope that inlined the function. Our root
+ caller must find the abstract scope that defines us. */
+ return -1 - result;
+ }
+
+ /* This DIE has no children containing the PC, so this is it. */
+ *scopes = malloc (depth * sizeof (*scopes)[0]);
+ if (*scopes == NULL)
+ {
+ __libdw_seterrno (DWARF_E_NOMEM);
+ return -1;
+ }
+ (*scopes)[0] = child;
+ return got == match ? 1 : -2;
+
+ case walk:
+ /* We don't have anything matching the PC, but we have some things
+ we might descend to find one. Recurse on each of those. */
+ if (INTUSE(dwarf_child) (die, &child) != 0)
+ return -1;
+ do
+ switch (classify_die (&child))
+ {
+ case walk:
+ if (INTUSE(dwarf_haschildren) (&child))
+ {
+ /* Recurse on this DIE to look for the PC within its children.
+ Return now if this gets an error or a final result. */
+ int result = search_child ();
+ if (result != 0)
+ return result;
+ }
+ break;
+
+ case imported:
+ {
+ /* This imports another compilation unit to appear
+ as part of this one, inside the current scope.
+ Recurse to search the referenced unit, but without
+ recording it as an inner scoping level. */
+
+ Dwarf_Attribute attr_mem;
+ Dwarf_Attribute *attr = INTUSE(dwarf_attr) (&child, DW_AT_import,
+ &attr_mem);
+ if (INTUSE(dwarf_formref_die) (attr, &child) != NULL)
+ {
+ int result = find_pc (depth, &child, pc, scopes);
+ if (result != 0)
+ return result;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ while (INTUSE(dwarf_siblingof) (&child, &child) == 0);
+ break;
+
+ default:
+ case ignore:
+ /* Nothing to see here. */
+ break;
+ }
+
+ /* No matches. */
+ return 0;
+}
+
+
+/* OWNER owns OWNED. Find intermediate scopes. *SCOPES was allocated by
+ find_pc and has SKIP elements. We realloc it, append more containing
+ scopes, and return 1 + the number appended. Returns -1 on errors,
+ or 0 when OWNED was not found within OWNER. */
+static int
+find_die (unsigned int depth, Dwarf_Die *owner, Dwarf_Die *owned,
+ Dwarf_Die **scopes, unsigned int skip)
+{
+ Dwarf_Die child;
+ if (INTUSE(dwarf_child) (owner, &child) != 0)
+ return -1;
+
+ do
+ {
+ if (child.addr == owned->addr)
+ /* This is the one. OWNER is the innermost owner. */
+ return 1;
+
+ /* Unfortunately we cannot short-circuit the dead-end paths just by
+ checking the physical layout to see if OWNED falls within CHILD.
+ If it doesn't, there may still be a DW_TAG_imported_unit that
+ refers to its true owner indirectly. */
+
+ switch (classify_die (&child))
+ {
+ case match:
+ case match_inline:
+ case walk:
+ if (INTUSE(dwarf_haschildren) (&child))
+ {
+ /* Recurse on this DIE to look for OWNED within its children.
+ Return now if this gets an error or a final result. */
+ int n = find_die (depth + 1, &child, owned, scopes, skip);
+ if (n < 0)
+ return n;
+ if (n > 1)
+ {
+ /* We have a winner. CHILD owns the owner of OWNED. */
+ (*scopes)[skip + n - 1] = child;
+ return n + 1;
+ }
+ if (n > 0) /* n == 1 */
+ {
+ /* CHILD is the direct owner of OWNED. */
+ Dwarf_Die *nscopes = realloc (*scopes,
+ (skip + depth)
+ * sizeof nscopes[0]);
+ if (nscopes == NULL)
+ {
+ free (*scopes);
+ *scopes = NULL;
+ __libdw_seterrno (DWARF_E_NOMEM);
+ return -1;
+ }
+ nscopes[skip] = child;
+ *scopes = nscopes;
+ return 2;
+ }
+ }
+ break;
+
+ case imported:
+ {
+ /* This is imports another compilation unit to appear
+ as part of this one, inside the current scope.
+ Recurse to search the referenced unit, but without
+ recording it as an inner scoping level. */
+
+ Dwarf_Attribute attr_mem;
+ Dwarf_Attribute *attr = INTUSE(dwarf_attr) (&child, DW_AT_import,
+ &attr_mem);
+ if (INTUSE(dwarf_formref_die) (attr, &child) != NULL)
+ {
+ int result = find_die (depth, &child, owner, scopes, skip);
+ if (result != 0)
+ return result;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ while (INTUSE(dwarf_siblingof) (&child, &child) == 0);
+
+ return 0;
+}
+
+
+int
+dwarf_getscopes (Dwarf_Die *cudie, Dwarf_Addr pc, Dwarf_Die **scopes)
+{
+ if (cudie == NULL)
+ return -1;
+
+ int n = find_pc (1, cudie, pc, scopes);
+ if (likely (n >= -1))
+ /* We have an error or a final result. */
+ return n;
+
+ /* We have the scopes out to one that is a concrete instance of an
+ inlined subroutine (usually DW_TAG_inlined_subroutine, but can
+ be DW_TAG_subprogram for a concrete out-of-line instance).
+ Now we must find the lexical scopes that contain the
+ corresponding abstract inline subroutine definition. */
+
+ n = -n - 1;
+
+ Dwarf_Attribute attr_mem;
+ Dwarf_Die die_mem;
+ Dwarf_Die *origin = INTUSE(dwarf_formref_die)
+ (INTUSE(dwarf_attr) (&(*scopes)[n - 1], DW_AT_abstract_origin, &attr_mem),
+ &die_mem);
+ if (unlikely (origin == NULL))
+ goto invalid;
+
+ int result = find_die (0, cudie, origin, scopes, n);
+ if (likely (result > 0))
+ return n + result - 1;
+
+ if (result == 0) /* No match, shouldn't happen. */
+ {
+ invalid:
+ __libdw_seterrno (DWARF_E_INVALID_DWARF);
+ }
+
+ free (*scopes);
+ *scopes = NULL;
+ return -1;
+}