|  | /* DWARF DIEs | 
|  |  | 
|  | Copyright (C) 1994-2024 Free Software Foundation, Inc. | 
|  |  | 
|  | This file is part of GDB. | 
|  |  | 
|  | This program is free software; you can redistribute it and/or modify | 
|  | it under the terms of the GNU General Public License as published by | 
|  | the Free Software Foundation; either version 3 of the License, or | 
|  | (at your option) any later version. | 
|  |  | 
|  | 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. | 
|  |  | 
|  | You should have received a copy of the GNU General Public License | 
|  | along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ | 
|  |  | 
|  | #include "defs.h" | 
|  | #include "dwarf2/die.h" | 
|  | #include "dwarf2/stringify.h" | 
|  |  | 
|  | /* See die.h.  */ | 
|  |  | 
|  | struct die_info * | 
|  | die_info::allocate (struct obstack *obstack, int num_attrs) | 
|  | { | 
|  | size_t size = sizeof (struct die_info); | 
|  |  | 
|  | if (num_attrs > 1) | 
|  | size += (num_attrs - 1) * sizeof (struct attribute); | 
|  |  | 
|  | struct die_info *die = (struct die_info *) obstack_alloc (obstack, size); | 
|  | memset (die, 0, size); | 
|  | return die; | 
|  | } | 
|  |  | 
|  | /* See die.h.  */ | 
|  |  | 
|  | hashval_t | 
|  | die_info::hash (const void *item) | 
|  | { | 
|  | const struct die_info *die = (const struct die_info *) item; | 
|  |  | 
|  | return to_underlying (die->sect_off); | 
|  | } | 
|  |  | 
|  | /* See die.h.  */ | 
|  |  | 
|  | int | 
|  | die_info::eq (const void *item_lhs, const void *item_rhs) | 
|  | { | 
|  | const struct die_info *die_lhs = (const struct die_info *) item_lhs; | 
|  | const struct die_info *die_rhs = (const struct die_info *) item_rhs; | 
|  |  | 
|  | return die_lhs->sect_off == die_rhs->sect_off; | 
|  | } | 
|  |  | 
|  | static void | 
|  | dump_die_shallow (struct ui_file *f, int indent, struct die_info *die) | 
|  | { | 
|  | unsigned int i; | 
|  |  | 
|  | gdb_printf (f, "%*sDie: %s (abbrev %d, offset %s)\n", | 
|  | indent, "", | 
|  | dwarf_tag_name (die->tag), die->abbrev, | 
|  | sect_offset_str (die->sect_off)); | 
|  |  | 
|  | if (die->parent != NULL) | 
|  | gdb_printf (f, "%*s  parent at offset: %s\n", | 
|  | indent, "", | 
|  | sect_offset_str (die->parent->sect_off)); | 
|  |  | 
|  | gdb_printf (f, "%*s  has children: %s\n", | 
|  | indent, "", | 
|  | dwarf_bool_name (die->child != NULL)); | 
|  |  | 
|  | gdb_printf (f, "%*s  attributes:\n", indent, ""); | 
|  |  | 
|  | for (i = 0; i < die->num_attrs; ++i) | 
|  | { | 
|  | gdb_printf (f, "%*s    %s (%s) ", | 
|  | indent, "", | 
|  | dwarf_attr_name (die->attrs[i].name), | 
|  | dwarf_form_name (die->attrs[i].form)); | 
|  |  | 
|  | switch (die->attrs[i].form) | 
|  | { | 
|  | case DW_FORM_addr: | 
|  | case DW_FORM_addrx: | 
|  | case DW_FORM_GNU_addr_index: | 
|  | gdb_printf (f, "address: "); | 
|  | gdb_puts (hex_string ((CORE_ADDR) die->attrs[i].as_address ()), f); | 
|  | break; | 
|  | case DW_FORM_block2: | 
|  | case DW_FORM_block4: | 
|  | case DW_FORM_block: | 
|  | case DW_FORM_block1: | 
|  | gdb_printf (f, "block: size %s", | 
|  | pulongest (die->attrs[i].as_block ()->size)); | 
|  | break; | 
|  | case DW_FORM_exprloc: | 
|  | gdb_printf (f, "expression: size %s", | 
|  | pulongest (die->attrs[i].as_block ()->size)); | 
|  | break; | 
|  | case DW_FORM_data16: | 
|  | gdb_printf (f, "constant of 16 bytes"); | 
|  | break; | 
|  | case DW_FORM_ref_addr: | 
|  | gdb_printf (f, "ref address: "); | 
|  | gdb_puts (hex_string (die->attrs[i].as_unsigned ()), f); | 
|  | break; | 
|  | case DW_FORM_GNU_ref_alt: | 
|  | gdb_printf (f, "alt ref address: "); | 
|  | gdb_puts (hex_string (die->attrs[i].as_unsigned ()), f); | 
|  | break; | 
|  | case DW_FORM_ref1: | 
|  | case DW_FORM_ref2: | 
|  | case DW_FORM_ref4: | 
|  | case DW_FORM_ref8: | 
|  | case DW_FORM_ref_udata: | 
|  | gdb_printf (f, "constant ref: 0x%lx (adjusted)", | 
|  | (long) (die->attrs[i].as_unsigned ())); | 
|  | break; | 
|  | case DW_FORM_data1: | 
|  | case DW_FORM_data2: | 
|  | case DW_FORM_data4: | 
|  | case DW_FORM_data8: | 
|  | case DW_FORM_udata: | 
|  | gdb_printf (f, "constant: %s", | 
|  | pulongest (die->attrs[i].as_unsigned ())); | 
|  | break; | 
|  | case DW_FORM_sec_offset: | 
|  | gdb_printf (f, "section offset: %s", | 
|  | pulongest (die->attrs[i].as_unsigned ())); | 
|  | break; | 
|  | case DW_FORM_ref_sig8: | 
|  | gdb_printf (f, "signature: %s", | 
|  | hex_string (die->attrs[i].as_signature ())); | 
|  | break; | 
|  | case DW_FORM_string: | 
|  | case DW_FORM_strp: | 
|  | case DW_FORM_line_strp: | 
|  | case DW_FORM_strx: | 
|  | case DW_FORM_GNU_str_index: | 
|  | case DW_FORM_GNU_strp_alt: | 
|  | gdb_printf (f, "string: \"%s\" (%s canonicalized)", | 
|  | die->attrs[i].as_string () | 
|  | ? die->attrs[i].as_string () : "", | 
|  | die->attrs[i].canonical_string_p () ? "is" : "not"); | 
|  | break; | 
|  | case DW_FORM_flag: | 
|  | if (die->attrs[i].as_boolean ()) | 
|  | gdb_printf (f, "flag: TRUE"); | 
|  | else | 
|  | gdb_printf (f, "flag: FALSE"); | 
|  | break; | 
|  | case DW_FORM_flag_present: | 
|  | gdb_printf (f, "flag: TRUE"); | 
|  | break; | 
|  | case DW_FORM_indirect: | 
|  | /* The reader will have reduced the indirect form to | 
|  | the "base form" so this form should not occur.  */ | 
|  | gdb_printf (f, | 
|  | "unexpected attribute form: DW_FORM_indirect"); | 
|  | break; | 
|  | case DW_FORM_sdata: | 
|  | case DW_FORM_implicit_const: | 
|  | gdb_printf (f, "constant: %s", | 
|  | plongest (die->attrs[i].as_signed ())); | 
|  | break; | 
|  | default: | 
|  | gdb_printf (f, "unsupported attribute form: %d.", | 
|  | die->attrs[i].form); | 
|  | break; | 
|  | } | 
|  | gdb_printf (f, "\n"); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void | 
|  | dump_die_1 (struct ui_file *f, int level, int max_level, struct die_info *die) | 
|  | { | 
|  | int indent = level * 4; | 
|  |  | 
|  | gdb_assert (die != NULL); | 
|  |  | 
|  | if (level >= max_level) | 
|  | return; | 
|  |  | 
|  | dump_die_shallow (f, indent, die); | 
|  |  | 
|  | if (die->child != NULL) | 
|  | { | 
|  | gdb_printf (f, "%*s  Children:", indent, ""); | 
|  | if (level + 1 < max_level) | 
|  | { | 
|  | gdb_printf (f, "\n"); | 
|  | dump_die_1 (f, level + 1, max_level, die->child); | 
|  | } | 
|  | else | 
|  | { | 
|  | gdb_printf (f, | 
|  | " [not printed, max nesting level reached]\n"); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (die->sibling != NULL && level > 0) | 
|  | { | 
|  | dump_die_1 (f, level, max_level, die->sibling); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* See die.h.  */ | 
|  |  | 
|  | void | 
|  | die_info::dump (int max_level) | 
|  | { | 
|  | dump_die_1 (gdb_stdlog, 0, max_level, this); | 
|  | } | 
|  |  | 
|  | /* See die.h.  */ | 
|  |  | 
|  | void | 
|  | die_info::error_dump () | 
|  | { | 
|  | dump_die_shallow (gdb_stderr, 0, this); | 
|  | } |