/* 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(" %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<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; | |
} |