aboutsummaryrefslogtreecommitdiffstats
path: root/elff/dwarf_die.cc
diff options
context:
space:
mode:
Diffstat (limited to 'elff/dwarf_die.cc')
-rw-r--r--elff/dwarf_die.cc269
1 files changed, 269 insertions, 0 deletions
diff --git a/elff/dwarf_die.cc b/elff/dwarf_die.cc
new file mode 100644
index 0000000..2085e31
--- /dev/null
+++ b/elff/dwarf_die.cc
@@ -0,0 +1,269 @@
+/* Copyright (C) 2007-2010 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program 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.
+*/
+
+/*
+ * Contains implementations of classes defined for a variety of DWARF objects.
+ */
+
+#include "stdio.h"
+#include "dwarf_die.h"
+#include "dwarf_cu.h"
+#include "dwarf_utils.h"
+#include "elf_file.h"
+
+DIEObject::~DIEObject() {
+ /* Delete all children of this object. */
+ DIEObject* to_del = last_child();
+ while (to_del != NULL) {
+ DIEObject* next = to_del->prev_sibling();
+ delete to_del;
+ to_del = next;
+ }
+}
+
+ElfFile* DIEObject::elf_file() const {
+ return parent_cu()->elf_file();
+}
+
+Dwarf_Tag DIEObject::get_tag() const {
+ Dwarf_Tag tag;
+ return advance(NULL, &tag) != NULL ? tag : 0;
+}
+
+const char* DIEObject::get_name() const {
+ DIEAttrib die_attr;
+ /* Start with the obvious. */
+ if (get_attrib(DW_AT_name, &die_attr)) {
+ return die_attr.value()->str;
+ }
+
+ /* Lets see if there is a reference to the abstract origin, or specification,
+ * and use its name as the name for this DIE. */
+ if (get_attrib(DW_AT_abstract_origin, &die_attr) ||
+ get_attrib(DW_AT_specification, &die_attr)) {
+ DIEObject* org_die_obj =
+ parent_cu()->get_referenced_die_object(die_attr.value()->u32);
+ if (org_die_obj != NULL) {
+ return org_die_obj->get_name();
+ }
+ }
+
+ /* Lets see if there is a reference to the type DIE, and use
+ * its name as the name for this DIE. */
+ if (get_attrib(DW_AT_type, &die_attr)) {
+ DIEObject* org_die_obj =
+ parent_cu()->get_referenced_die_object(die_attr.value()->u32);
+ if (org_die_obj != NULL) {
+ return org_die_obj->get_name();
+ }
+ }
+
+ /* Can't figure the name for this DIE. */
+ return NULL;
+}
+
+bool DIEObject::get_attrib(Dwarf_At at_id, DIEAttrib* attr) const {
+ const Dwarf_Abbr_AT* at_abbr;
+
+ /* Advance to DIE attributes. */
+ const Elf_Byte* die_attr = advance(&at_abbr, NULL);
+ if (die_attr == NULL) {
+ _set_errno(EINVAL);
+ return false;
+ }
+
+ /* Loop through all DIE attributes, looking for the one that's being
+ * requested. */
+ while (!at_abbr->is_separator()) {
+ at_abbr = at_abbr->process(&attr->at_, &attr->form_);
+ die_attr = parent_cu()->process_attrib(die_attr, attr->form_, &attr->value_);
+ if (at_id == attr->at()) {
+ return true;
+ }
+ }
+
+ _set_errno(EINVAL);
+
+ return false;
+}
+
+DIEObject* DIEObject::get_leaf_for_address(Elf_Xword address) {
+ const bool contains = parent_cu()->is_CU_address_64() ?
+ contains_address<Elf_Xword>(address) :
+ contains_address<Elf_Word>(address);
+ if (!contains && !is_cu_die()) {
+ /* For CU DIEs address range may be zero size, even though its child DIEs
+ * occupie some address space. So, if CU DIE's address range doesn't
+ * contain the given address, we still want to go and check the children.
+ */
+ _set_errno(EINVAL);
+ return NULL;
+ }
+
+ /* This DIE contains given address (or may contain it, if this is a CU DIE).
+ * Lets iterate through child DIEs to find the leaf (last DIE) that contains
+ * this address. */
+ DIEObject* child = last_child();
+ while (child != NULL) {
+ DIEObject* leaf = child->get_leaf_for_address(address);
+ if (leaf != NULL) {
+ return leaf;
+ }
+ child = child->prev_sibling();
+ }
+ /* No child DIE contains this address. This DIE is the leaf. */
+ return contains || !is_cu_die() ? this : NULL;
+}
+
+template <typename AddrType>
+bool DIEObject::contains_address(Elf_Xword address) {
+ DIEAttrib die_ranges;
+ /* DIE can contain either list of ranges (f.i. DIEs that represent a routine
+ * that is inlined in multiple places will contain list of address ranges
+ * where that routine is inlined), or a pair "low PC, and high PC" describing
+ * contiguos address space where routine has been placed by compiler. */
+ if (get_attrib(DW_AT_ranges, &die_ranges)) {
+ /* Iterate through this DIE's ranges list, looking for the one that
+ * contains the given address. */
+ AddrType low;
+ AddrType high;
+ Elf_Word range_off = die_ranges.value()->u32;
+ while (elf_file()->get_range(range_off, &low, &high) &&
+ (low != 0 || high != 0)) {
+ if (address >= low && address < high) {
+ return true;
+ }
+ range_off += sizeof(AddrType) * 2;
+ }
+ return false;
+ } else {
+ /* This DIE doesn't have ranges. Lets see if it has low_pc and high_pc
+ * attributes. */
+ DIEAttrib low_pc;
+ DIEAttrib high_pc;
+ if (!get_attrib(DW_AT_low_pc, &low_pc) ||
+ !get_attrib(DW_AT_high_pc, &high_pc) ||
+ address < low_pc.value()->u64 ||
+ address >= high_pc.value()->u64) {
+ return false;
+ }
+ return true;
+ }
+}
+
+DIEObject* DIEObject::find_die_object(const Dwarf_DIE* die_to_find) {
+ if (die_to_find == die()) {
+ return this;
+ }
+
+ /* First we will iterate through the list of children, since chances to
+ * find requested DIE decrease as we go deeper into DIE tree. */
+ DIEObject* iter = last_child();
+ while (iter != NULL) {
+ if (iter->die() == die_to_find) {
+ return iter;
+ }
+ iter = iter->prev_sibling();
+ };
+
+ /* DIE has not been found among the children. Lets go deeper now. */
+ iter = last_child();
+ while (iter != NULL) {
+ DIEObject* ret = iter->find_die_object(die_to_find);
+ if (ret != NULL) {
+ return ret;
+ }
+ iter = iter->prev_sibling();
+ }
+
+ _set_errno(EINVAL);
+ return NULL;
+}
+
+void DIEObject::dump(bool only_this) const {
+ const Dwarf_Abbr_AT* at_abbr;
+ Dwarf_Tag tag;
+
+ const Elf_Byte* die_attr = advance(&at_abbr, &tag);
+ if (die_attr != NULL) {
+ printf("\n********** DIE[%p(%04X)] %s: %s **********\n",
+ die_, parent_cu()->get_die_reference(die_), dwarf_tag_name(tag),
+ get_name());
+
+ /* Dump this DIE attributes. */
+ while (!at_abbr->is_separator()) {
+ DIEAttrib attr;
+ at_abbr = at_abbr->process(&attr.at_, &attr.form_);
+ die_attr = parent_cu()->process_attrib(die_attr, attr.form(), &attr.value_);
+ dump_attrib(attr.at(), attr.form(), attr.value());
+ if (attr.at() == DW_AT_ranges) {
+ /* Dump all ranges for this DIE. */
+ Elf_Word off = attr.value()->u32;
+ if (parent_cu()->is_CU_address_64()) {
+ Elf_Xword low, high;
+ while (elf_file()->get_range<Elf_Xword>(off, &low, &high) &&
+ (low != 0 || high != 0)) {
+ printf(" %08I64X - %08I64X\n",
+ low, high);
+ off += 16;
+ }
+ } else {
+ Elf_Word low, high;
+ while (elf_file()->get_range<Elf_Word>(off, &low, &high) &&
+ (low != 0 || high != 0)) {
+ printf(" %08X - %08X\n",
+ low, high);
+ off += 8;
+ }
+ }
+ }
+ }
+ }
+
+ if (only_this) {
+ if (parent_die_ != NULL && !parent_die_->is_cu_die()) {
+ printf("\n-----------> CHILD OF:\n");
+ parent_die_->dump(true);
+ }
+ } else {
+ /* Dump this DIE's children. */
+ if (last_child() != NULL) {
+ last_child()->dump(false);
+ }
+
+ /* Dump this DIE's siblings. */
+ if (prev_sibling() != NULL) {
+ prev_sibling()->dump(false);
+ }
+ }
+}
+
+const Elf_Byte* DIEObject::advance(const Dwarf_Abbr_AT** at_abbr,
+ Dwarf_Tag* tag) const {
+ Dwarf_AbbrNum abbr_num;
+ Dwarf_Tag die_tag;
+
+ const Elf_Byte* die_attr = die()->process(&abbr_num);
+ const Dwarf_Abbr_DIE* abbr = parent_cu()->get_die_abbr(abbr_num);
+ if (abbr == NULL) {
+ return NULL;
+ }
+
+ const Dwarf_Abbr_AT* attrib_abbr = abbr->process(NULL, &die_tag);
+ if (at_abbr != NULL) {
+ *at_abbr = attrib_abbr;
+ }
+ if (tag != NULL) {
+ *tag = die_tag;
+ }
+ return die_attr;
+}