/* 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(address) : contains_address(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 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(off, &low, &high) && (low != 0 || high != 0)) { printf(" %08" FMT_I64 "X - %08" FMT_I64 "X\n", (unsigned long long)low, (unsigned long long)high); off += 16; } } else { Elf_Word low, high; while (elf_file()->get_range(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; }