/*-
 * Copyright (c) 2007 John Birrell (jb@freebsd.org)
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $FreeBSD: releng/9.2/lib/libdwarf/dwarf_dump.c 240416 2012-09-12 18:15:17Z dim $
 */

#include <stdlib.h>
#include <string.h>
#include "_libdwarf.h"

const char *
get_sht_desc(uint32_t sh_type)
{
	switch (sh_type) {
	case SHT_NULL:
		return "inactive";
	case SHT_PROGBITS:
		return "program defined information";
	case SHT_SYMTAB:
		return "symbol table section";
	case SHT_STRTAB:
		return "string table section";
	case SHT_RELA:
		return "relocation section with addends";
	case SHT_HASH:
		return "symbol hash table section";
	case SHT_DYNAMIC:
		return "dynamic section";
	case SHT_NOTE:
		return "note section";
	case SHT_NOBITS:
		return "no space section";
	case SHT_REL:
		return "relocation section - no addends";
	case SHT_SHLIB:
		return "reserved - purpose unknown";
	case SHT_DYNSYM:
		return "dynamic symbol table section";
	case SHT_INIT_ARRAY:
		return "Initialization function pointers.";
	case SHT_FINI_ARRAY:
		return "Termination function pointers.";
	case SHT_PREINIT_ARRAY:
		return "Pre-initialization function ptrs.";
	case SHT_GROUP:
		return "Section group.";
	case SHT_SYMTAB_SHNDX:
		return "Section indexes (see SHN_XINDEX).";
	case SHT_GNU_verdef:
		return "Symbol versions provided";
	case SHT_GNU_verneed:
		return "Symbol versions required";
	case SHT_GNU_versym:
		return "Symbol version table";
	case SHT_AMD64_UNWIND:
		return "AMD64 unwind";
	default:
		return "Unknown";
	}
}

const char *
get_attr_desc(uint32_t attr)
{
	switch (attr) {
	case DW_AT_abstract_origin:
		return "DW_AT_abstract_origin";
	case DW_AT_accessibility:
		return "DW_AT_accessibility";
	case DW_AT_address_class:
		return "DW_AT_address_class";
	case DW_AT_artificial:
		return "DW_AT_artificial";
	case DW_AT_base_types:
		return "DW_AT_base_types";
	case DW_AT_bit_offset:
		return "DW_AT_bit_offset";
	case DW_AT_bit_size:
		return "DW_AT_bit_size";
	case DW_AT_byte_size:
		return "DW_AT_byte_size";
	case DW_AT_calling_convention:
		return "DW_AT_calling_convention";
	case DW_AT_common_reference:
		return "DW_AT_common_reference";
	case DW_AT_comp_dir:
		return "DW_AT_comp_dir";
	case DW_AT_const_value:
		return "DW_AT_const_value";
	case DW_AT_containing_type:
		return "DW_AT_containing_type";
	case DW_AT_count:
		return "DW_AT_count";
	case DW_AT_data_member_location:
		return "DW_AT_data_member_location";
	case DW_AT_decl_column:
		return "DW_AT_decl_column";
	case DW_AT_decl_file:
		return "DW_AT_decl_file";
	case DW_AT_decl_line:
		return "DW_AT_decl_line";
	case DW_AT_declaration:
		return "DW_AT_declaration";
	case DW_AT_default_value:
		return "DW_AT_default_value";
	case DW_AT_discr:
		return "DW_AT_discr";
	case DW_AT_discr_list:
		return "DW_AT_discr_list";
	case DW_AT_discr_value:
		return "DW_AT_discr_value";
	case DW_AT_element_list:
		return "DW_AT_element_list";
	case DW_AT_encoding:
		return "DW_AT_encoding";
	case DW_AT_external:
		return "DW_AT_external";
	case DW_AT_frame_base:
		return "DW_AT_frame_base";
	case DW_AT_friend:
		return "DW_AT_friend";
	case DW_AT_high_pc:
		return "DW_AT_high_pc";
	case DW_AT_identifier_case:
		return "DW_AT_identifier_case";
	case DW_AT_import:
		return "DW_AT_import";
	case DW_AT_inline:
		return "DW_AT_inline";
	case DW_AT_is_optional:
		return "DW_AT_is_optional";
	case DW_AT_language:
		return "DW_AT_language";
	case DW_AT_location:
		return "DW_AT_location";
	case DW_AT_low_pc:
		return "DW_AT_low_pc";
	case DW_AT_lower_bound:
		return "DW_AT_lower_bound";
	case DW_AT_macro_info:
		return "DW_AT_macro_info";
	case DW_AT_member:
		return "DW_AT_member";
	case DW_AT_name:
		return "DW_AT_name";
	case DW_AT_namelist_item:
		return "DW_AT_namelist_item";
	case DW_AT_ordering:
		return "DW_AT_ordering";
	case DW_AT_priority:
		return "DW_AT_priority";
	case DW_AT_producer:
		return "DW_AT_producer";
	case DW_AT_prototyped:
		return "DW_AT_prototyped";
	case DW_AT_return_addr:
		return "DW_AT_return_addr";
	case DW_AT_segment:
		return "DW_AT_segment";
	case DW_AT_sibling:
		return "DW_AT_sibling";
	case DW_AT_specification:
		return "DW_AT_specification";
	case DW_AT_start_scope:
		return "DW_AT_start_scope";
	case DW_AT_static_link:
		return "DW_AT_static_link";
	case DW_AT_stmt_list:
		return "DW_AT_stmt_list";
	case DW_AT_stride_size:
		return "DW_AT_stride_size";
	case DW_AT_string_length:
		return "DW_AT_string_length";
	case DW_AT_subscr_data:
		return "DW_AT_subscr_data";
	case DW_AT_type:
		return "DW_AT_type";
	case DW_AT_upper_bound:
		return "DW_AT_upper_bound";
	case DW_AT_use_location:
		return "DW_AT_use_location";
	case DW_AT_variable_parameter:
		return "DW_AT_variable_parameter";
	case DW_AT_virtuality:
		return "DW_AT_virtuality";
	case DW_AT_visibility:
		return "DW_AT_visibility";
	case DW_AT_vtable_elem_location:
		return "DW_AT_vtable_elem_location";
	default:
		break;
	}

	return "Unknown attribute";
}

const char *
get_form_desc(uint32_t form)
{
	switch (form) {
	case DW_FORM_addr:
		return "DW_FORM_addr";
	case DW_FORM_block:
		return "DW_FORM_block";
	case DW_FORM_block1:
		return "DW_FORM_block1";
	case DW_FORM_block2:
		return "DW_FORM_block2";
	case DW_FORM_block4:
		return "DW_FORM_block4";
	case DW_FORM_data1:
		return "DW_FORM_data1";
	case DW_FORM_data2:
		return "DW_FORM_data2";
	case DW_FORM_data4:
		return "DW_FORM_data4";
	case DW_FORM_data8:
		return "DW_FORM_data8";
	case DW_FORM_flag:
		return "DW_FORM_flag";
	case DW_FORM_flag_present:
		return "DW_FORM_flag_present";
	case DW_FORM_indirect:
		return "DW_FORM_indirect";
	case DW_FORM_ref1:
		return "DW_FORM_ref1";
	case DW_FORM_ref2:
		return "DW_FORM_ref2";
	case DW_FORM_ref4:
		return "DW_FORM_ref4";
	case DW_FORM_ref8:
		return "DW_FORM_ref8";
	case DW_FORM_ref_addr:
		return "DW_FORM_ref_addr";
	case DW_FORM_ref_udata:
		return "DW_FORM_ref_udata";
	case DW_FORM_sdata:
		return "DW_FORM_sdata";
	case DW_FORM_string:
		return "DW_FORM_string";
	case DW_FORM_strp:
		return "DW_FORM_strp";
	case DW_FORM_udata:
		return "DW_FORM_udata";
	default:
		break;
	}

	return "Unknown attribute";
}

const char *
get_tag_desc(uint32_t tag)
{
	switch (tag) {
	case DW_TAG_access_declaration:
		return "DW_TAG_access_declaration";
	case DW_TAG_array_type:
		return "DW_TAG_array_type";
	case DW_TAG_base_type:
		return "DW_TAG_base_type";
	case DW_TAG_catch_block:
		return "DW_TAG_catch_block";
	case DW_TAG_class_type:
		return "DW_TAG_class_type";
	case DW_TAG_common_block:
		return "DW_TAG_common_block";
	case DW_TAG_common_inclusion:
		return "DW_TAG_common_inclusion";
	case DW_TAG_compile_unit:
		return "DW_TAG_compile_unit";
	case DW_TAG_condition:
		return "DW_TAG_condition";
	case DW_TAG_const_type:
		return "DW_TAG_const_type";
	case DW_TAG_constant:
		return "DW_TAG_constant";
	case DW_TAG_dwarf_procedure:
		return "DW_TAG_dwarf_procedure";
	case DW_TAG_entry_point:
		return "DW_TAG_entry_point";
	case DW_TAG_enumeration_type:
		return "DW_TAG_enumeration_type";
	case DW_TAG_enumerator:
		return "DW_TAG_enumerator";
	case DW_TAG_formal_parameter:
		return "DW_TAG_formal_parameter";
	case DW_TAG_friend:
		return "DW_TAG_friend";
	case DW_TAG_imported_declaration:
		return "DW_TAG_imported_declaration";
	case DW_TAG_imported_module:
		return "DW_TAG_imported_module";
	case DW_TAG_imported_unit:
		return "DW_TAG_imported_unit";
	case DW_TAG_inheritance:
		return "DW_TAG_inheritance";
	case DW_TAG_inlined_subroutine:
		return "DW_TAG_inlined_subroutine";
	case DW_TAG_interface_type:
		return "DW_TAG_interface_type";
	case DW_TAG_label:
		return "DW_TAG_label";
	case DW_TAG_lexical_block:
		return "DW_TAG_lexical_block";
	case DW_TAG_member:
		return "DW_TAG_member";
	case DW_TAG_module:
		return "DW_TAG_module";
	case DW_TAG_namelist:
		return "DW_TAG_namelist";
	case DW_TAG_namelist_item:
		return "DW_TAG_namelist_item";
	case DW_TAG_namespace:
		return "DW_TAG_namespace";
	case DW_TAG_packed_type:
		return "DW_TAG_packed_type";
	case DW_TAG_partial_unit:
		return "DW_TAG_partial_unit";
	case DW_TAG_pointer_type:
		return "DW_TAG_pointer_type";
	case DW_TAG_ptr_to_member_type:
		return "DW_TAG_ptr_to_member_type";
	case DW_TAG_reference_type:
		return "DW_TAG_reference_type";
	case DW_TAG_restrict_type:
		return "DW_TAG_restrict_type";
	case DW_TAG_set_type:
		return "DW_TAG_set_type";
	case DW_TAG_shared_type:
		return "DW_TAG_shared_type";
	case DW_TAG_string_type:
		return "DW_TAG_string_type";
	case DW_TAG_structure_type:
		return "DW_TAG_structure_type";
	case DW_TAG_subprogram:
		return "DW_TAG_subprogram";
	case DW_TAG_subrange_type:
		return "DW_TAG_subrange_type";
	case DW_TAG_subroutine_type:
		return "DW_TAG_subroutine_type";
	case DW_TAG_template_type_parameter:
		return "DW_TAG_template_type_parameter";
	case DW_TAG_template_value_parameter:
		return "DW_TAG_template_value_parameter";
	case DW_TAG_thrown_type:
		return "DW_TAG_thrown_type";
	case DW_TAG_try_block:
		return "DW_TAG_try_block";
	case DW_TAG_typedef:
		return "DW_TAG_typedef";
	case DW_TAG_union_type:
		return "DW_TAG_union_type";
	case DW_TAG_unspecified_parameters:
		return "DW_TAG_unspecified_parameters";
	case DW_TAG_unspecified_type:
		return "DW_TAG_unspecified_type";
	case DW_TAG_variable:
		return "DW_TAG_variable";
	case DW_TAG_variant:
		return "DW_TAG_variant";
	case DW_TAG_variant_part:
		return "DW_TAG_variant_part";
	case DW_TAG_volatile_type:
		return "DW_TAG_volatile_type";
	case DW_TAG_with_stmt:
		return "DW_TAG_with_stmt";
	default:
		break;
	}

	return "Unknown tag";
}

void
dwarf_dump_abbrev(Dwarf_Debug dbg)
{
	Dwarf_Abbrev a;
	Dwarf_Attribute at;
	Dwarf_CU cu;

	printf("Contents of the .debug_abbrev section:\n\nEntry Tag\n");

	STAILQ_FOREACH(cu, &dbg->dbg_cu, cu_next) {
		STAILQ_FOREACH(a, &cu->cu_abbrev, a_next) {
			printf("%5lu %-30s [%s children]\n",
			    (u_long) a->a_entry, get_tag_desc(a->a_tag),
			    (a->a_children == DW_CHILDREN_yes) ? "has" : "no");

			STAILQ_FOREACH(at, &a->a_attrib, at_next)
				printf("      %-30s %s\n", get_attr_desc(at->at_attrib),
				    get_form_desc(at->at_form));
		}
	}
}
#ifdef DOODAD
    case DW_AT_inline:
      switch (uvalue)
	{
	case DW_INL_not_inlined:
	  printf (_("(not inlined)"));
	  break;
	case DW_INL_inlined:
	  printf (_("(inlined)"));
	  break;
	case DW_INL_declared_not_inlined:
	  printf (_("(declared as inline but ignored)"));
	  break;
	case DW_INL_declared_inlined:
	  printf (_("(declared as inline and inlined)"));
	  break;
	default:
	  printf (_("  (Unknown inline attribute value: %lx)"), uvalue);
	  break;
	}
      break;

    case DW_AT_language:
      switch (uvalue)
	{
	case DW_LANG_C:			printf ("(non-ANSI C)"); break;
	case DW_LANG_C89:		printf ("(ANSI C)"); break;
	case DW_LANG_C_plus_plus:	printf ("(C++)"); break;
	case DW_LANG_Fortran77:		printf ("(FORTRAN 77)"); break;
	case DW_LANG_Fortran90:		printf ("(Fortran 90)"); break;
	case DW_LANG_Modula2:		printf ("(Modula 2)"); break;
	case DW_LANG_Pascal83:		printf ("(ANSI Pascal)"); break;
	case DW_LANG_Ada83:		printf ("(Ada)"); break;
	case DW_LANG_Cobol74:		printf ("(Cobol 74)"); break;
	case DW_LANG_Cobol85:		printf ("(Cobol 85)"); break;
	  /* DWARF 2.1 values.	*/
	case DW_LANG_C99:		printf ("(ANSI C99)"); break;
	case DW_LANG_Ada95:		printf ("(ADA 95)"); break;
	case DW_LANG_Fortran95:		printf ("(Fortran 95)"); break;
	  /* MIPS extension.  */
	case DW_LANG_Mips_Assembler:	printf ("(MIPS assembler)"); break;
	  /* UPC extension.  */
	case DW_LANG_Upc:		printf ("(Unified Parallel C)"); break;
	default:
	  printf ("(Unknown: %lx)", uvalue);
	  break;
	}
      break;

    case DW_AT_encoding:
      switch (uvalue)
	{
	case DW_ATE_void:		printf ("(void)"); break;
	case DW_ATE_address:		printf ("(machine address)"); break;
	case DW_ATE_boolean:		printf ("(boolean)"); break;
	case DW_ATE_complex_float:	printf ("(complex float)"); break;
	case DW_ATE_float:		printf ("(float)"); break;
	case DW_ATE_signed:		printf ("(signed)"); break;
	case DW_ATE_signed_char:	printf ("(signed char)"); break;
	case DW_ATE_unsigned:		printf ("(unsigned)"); break;
	case DW_ATE_unsigned_char:	printf ("(unsigned char)"); break;
	  /* DWARF 2.1 value.  */
	case DW_ATE_imaginary_float:	printf ("(imaginary float)"); break;
	default:
	  if (uvalue >= DW_ATE_lo_user
	      && uvalue <= DW_ATE_hi_user)
	    printf ("(user defined type)");
	  else
	    printf ("(unknown type)");
	  break;
	}
      break;

    case DW_AT_accessibility:
      switch (uvalue)
	{
	case DW_ACCESS_public:		printf ("(public)"); break;
	case DW_ACCESS_protected:	printf ("(protected)"); break;
	case DW_ACCESS_private:		printf ("(private)"); break;
	default:
	  printf ("(unknown accessibility)");
	  break;
	}
      break;

    case DW_AT_visibility:
      switch (uvalue)
	{
	case DW_VIS_local:		printf ("(local)"); break;
	case DW_VIS_exported:		printf ("(exported)"); break;
	case DW_VIS_qualified:		printf ("(qualified)"); break;
	default:			printf ("(unknown visibility)"); break;
	}
      break;

    case DW_AT_virtuality:
      switch (uvalue)
	{
	case DW_VIRTUALITY_none:	printf ("(none)"); break;
	case DW_VIRTUALITY_virtual:	printf ("(virtual)"); break;
	case DW_VIRTUALITY_pure_virtual:printf ("(pure_virtual)"); break;
	default:			printf ("(unknown virtuality)"); break;
	}
      break;

    case DW_AT_identifier_case:
      switch (uvalue)
	{
	case DW_ID_case_sensitive:	printf ("(case_sensitive)"); break;
	case DW_ID_up_case:		printf ("(up_case)"); break;
	case DW_ID_down_case:		printf ("(down_case)"); break;
	case DW_ID_case_insensitive:	printf ("(case_insensitive)"); break;
	default:			printf ("(unknown case)"); break;
	}
      break;

    case DW_AT_calling_convention:
      switch (uvalue)
	{
	case DW_CC_normal:	printf ("(normal)"); break;
	case DW_CC_program:	printf ("(program)"); break;
	case DW_CC_nocall:	printf ("(nocall)"); break;
	default:
	  if (uvalue >= DW_CC_lo_user
	      && uvalue <= DW_CC_hi_user)
	    printf ("(user defined)");
	  else
	    printf ("(unknown convention)");
	}
      break;

    case DW_AT_ordering:
      switch (uvalue)
	{
	case -1: printf ("(undefined)"); break;
	case 0:  printf ("(row major)"); break;
	case 1:  printf ("(column major)"); break;
	}
      break;

    case DW_AT_frame_base:
    case DW_AT_location:
    case DW_AT_data_member_location:
    case DW_AT_vtable_elem_location:
    case DW_AT_allocated:
    case DW_AT_associated:
    case DW_AT_data_location:
    case DW_AT_stride:
    case DW_AT_upper_bound:
    case DW_AT_lower_bound:
      if (block_start)
	{
	  printf ("(");
	  decode_location_expression (block_start, pointer_size, uvalue);
	  printf (")");
	}
      else if (form == DW_FORM_data4 || form == DW_FORM_data8)
	{
	  printf ("(");
	  printf ("location list");
	  printf (")");
	}
      break;
#endif

static void
dwarf_dump_av_attr(Dwarf_Die die __unused, Dwarf_AttrValue av)
{
	switch (av->av_attrib) {
	case DW_AT_accessibility:
		break;

	case DW_AT_calling_convention:
		break;

	case DW_AT_encoding:
		break;

	case DW_AT_identifier_case:
		break;

	case DW_AT_inline:
		break;

	case DW_AT_language:
		break;

	case DW_AT_ordering:
		break;

	case DW_AT_virtuality:
		break;

	case DW_AT_visibility:
		break;

	case DW_AT_frame_base:
	case DW_AT_location:
	case DW_AT_data_member_location:
	case DW_AT_vtable_elem_location:
	case DW_AT_upper_bound:
	case DW_AT_lower_bound:
		break;

	default:
		break;
	}
}

void
dwarf_dump_av(Dwarf_Die die, Dwarf_AttrValue av)
{
	uint64_t i;

	printf("      %-30s : %-16s ",
	    get_attr_desc(av->av_attrib),
	    get_form_desc(av->av_form));

	switch (av->av_form) {
	case DW_FORM_addr:
		printf("0x%llx", (unsigned long long) av->u[0].u64);
		break;
	case DW_FORM_block:
	case DW_FORM_block1:
	case DW_FORM_block2:
	case DW_FORM_block4:
		printf("%lu byte block:", (u_long) av->u[0].u64);
		for (i = 0; i < av->u[0].u64; i++)
			printf(" %02x", av->u[1].u8p[i]);
		break;
	case DW_FORM_data1:
	case DW_FORM_data2:
	case DW_FORM_data4:
	case DW_FORM_data8:
	case DW_FORM_flag:
	case DW_FORM_flag_present:
		printf("%llu", (unsigned long long) av->u[0].u64);
		break;
	case DW_FORM_ref1:
	case DW_FORM_ref2:
	case DW_FORM_ref4:
	case DW_FORM_ref8:
	case DW_FORM_ref_udata:
		printf("<%llx>", (unsigned long long) (av->u[0].u64 +
		    die->die_cu->cu_offset));
		break;
	case DW_FORM_string:
		printf("%s", av->u[0].s);
		break;
	case DW_FORM_strp:
		printf("(indirect string, offset 0x%llx): %s",
		    (unsigned long long) av->u[0].u64, av->u[1].s);
		break;
	default:
		printf("unknown form");
		break;
	}

	/* Dump any extra attribute-specific information. */
	dwarf_dump_av_attr(die, av);

	printf("\n");
}

void
dwarf_dump_die_at_offset(Dwarf_Debug dbg, Dwarf_Off off)
{
	Dwarf_CU cu;
	Dwarf_Die die;

	if (dbg == NULL)
		return;

	STAILQ_FOREACH(cu, &dbg->dbg_cu, cu_next) {
		STAILQ_FOREACH(die, &cu->cu_die, die_next) {
			if ((off_t) die->die_offset == off) {
				dwarf_dump_die(die);
				return;
			}
		}
	}
}

void
dwarf_dump_die(Dwarf_Die die)
{
	Dwarf_AttrValue av;

	printf("<%d><%llx>: Abbrev number: %llu (%s)\n",
	    die->die_level, (unsigned long long) die->die_offset,
	    (unsigned long long) die->die_abnum,
	    get_tag_desc(die->die_a->a_tag));

	STAILQ_FOREACH(av, &die->die_attrval, av_next)
		dwarf_dump_av(die, av);
}

void
dwarf_dump_raw(Dwarf_Debug dbg)
{
	Dwarf_CU cu;
	char *p = (char *) dbg;
	int i;

	printf("dbg %p\n",dbg);

	if (dbg == NULL)
		return;

	for (i = 0; i < (int) sizeof(*dbg); i++) {
		if (*p >= 0x20 && *p < 0x7f) {
			printf(" %c",*p++ & 0xff);
		} else {
			printf(" %02x",*p++ & 0xff);
		}
	}
	printf("\n");

	STAILQ_FOREACH(cu, &dbg->dbg_cu, cu_next) {
		p = (char *) cu;
		printf("cu %p\n",cu);
		for (i = 0; i < (int) sizeof(*cu); i++) {
			if (*p >= 0x20 && *p < 0x7f) {
				printf(" %c",*p++ & 0xff);
			} else {
				printf(" %02x",*p++ & 0xff);
			}
		}
		printf("\n");
	}
}

static void
dwarf_dump_tree_dies(Dwarf_Debug dbg, Dwarf_Die die, Dwarf_Error *error)
{
	Dwarf_Die child;
	int ret;

	do {
		dwarf_dump_die(die);

		if ((ret = dwarf_child(die, &child, error) == DWARF_E_NO_ENTRY)) {
			/* No children. */
		} else if (ret != DWARF_E_NONE) {
			printf("Error %s\n", dwarf_errmsg(error));
			return;
		} else
			dwarf_dump_tree_dies(dbg, child, error);

		if (dwarf_siblingof(dbg, die, &die, error) != DWARF_E_NONE)
			die = NULL;
		
	} while (die != NULL);
}

void
dwarf_dump_tree(Dwarf_Debug dbg)
{
	Dwarf_CU cu;
	Dwarf_Die die;
	Dwarf_Error error;
	Dwarf_Half cu_pointer_size;
	Dwarf_Half cu_version;
	Dwarf_Unsigned cu_abbrev_offset;
	Dwarf_Unsigned cu_header_length;
	Dwarf_Unsigned cu_next_offset;

	STAILQ_FOREACH(cu, &dbg->dbg_cu, cu_next) {
		printf ("\nCompilation Unit @ offset %llx:\n",
		    (unsigned long long) cu->cu_offset);
		printf ("    Length:          %lu\n", (u_long) cu->cu_length);
		printf ("    Version:         %hu\n", cu->cu_version);
		printf ("    Abbrev Offset:   %lu\n", (u_long) cu->cu_abbrev_offset);
		printf ("    Pointer Size:    %u\n", (u_int) cu->cu_pointer_size);

		if (dwarf_next_cu_header(dbg, &cu_header_length,
		    &cu_version, &cu_abbrev_offset, &cu_pointer_size,
		    &cu_next_offset, &error) != DWARF_E_NONE) {
			printf("Error %s\n", dwarf_errmsg(&error));
			return;
		}

		if (dwarf_siblingof(dbg, NULL, &die, &error) != DWARF_E_NONE) {
			printf("Error %s\n", dwarf_errmsg(&error));
			return;
		}

		dwarf_dump_tree_dies(dbg, die, &error);

	}
}

void
dwarf_dump_info(Dwarf_Debug dbg)
{
	Dwarf_CU cu;
	Dwarf_Die die;

	printf("Contents of the .debug_info section:\n");

	STAILQ_FOREACH(cu, &dbg->dbg_cu, cu_next) {
		printf ("\nCompilation Unit @ offset %llx:\n",
		    (unsigned long long) cu->cu_offset);
		printf ("    Length:          %lu\n", (u_long) cu->cu_length);
		printf ("    Version:         %hu\n", cu->cu_version);
		printf ("    Abbrev Offset:   %lu\n", (u_long) cu->cu_abbrev_offset);
		printf ("    Pointer Size:    %u\n", (u_int) cu->cu_pointer_size);

		STAILQ_FOREACH(die, &cu->cu_die, die_next)
			dwarf_dump_die(die);
	}
}


void
dwarf_dump_shstrtab(Dwarf_Debug dbg)
{
	char *name;
	int indx = 0;

	printf("---------------------\nSection header string table contents:\n");
	while ((name = elf_strptr(dbg->dbg_elf, dbg->dbg_stnum, indx)) != NULL) {
		printf("%5d '%s'\n",indx,name);
		indx += strlen(name) + 1;
	}
}

void
dwarf_dump_strtab(Dwarf_Debug dbg)
{
	char *name;
	int indx = 0;

	printf("---------------------\nString table contents:\n");
	while ((name = elf_strptr(dbg->dbg_elf, dbg->dbg_s[DWARF_strtab].s_shnum, indx)) != NULL) {
		printf("%5d '%s'\n",indx,name);
		indx += strlen(name) + 1;
	}
}

void
dwarf_dump_dbgstr(Dwarf_Debug dbg)
{
	char *name;
	int indx = 0;

	printf("---------------------\nDebug string table contents:\n");
	while ((name = elf_strptr(dbg->dbg_elf, dbg->dbg_s[DWARF_debug_str].s_shnum, indx)) != NULL) {
		printf("%5d '%s'\n",indx,name);
		indx += strlen(name) + 1;
	}
}

void
dwarf_dump_symtab(Dwarf_Debug dbg)
{
	GElf_Sym sym;
	char *name;
	int indx = 0;

	printf("---------------------\nSymbol table contents:\n");
	while (gelf_getsym(dbg->dbg_s[DWARF_symtab].s_data,  indx++, &sym) != NULL) {
		if ((name = elf_strptr(dbg->dbg_elf, dbg->dbg_s[DWARF_strtab].s_shnum, sym.st_name)) == NULL)
			printf("sym.st_name %u indx %d sym.st_size %lu\n",sym.st_name,indx,(u_long) sym.st_size);
		else
			printf("'%s' sym.st_name %u indx %d sym.st_size %lu\n",name,sym.st_name,indx,(u_long) sym.st_size);
	}
}

void
dwarf_dump(Dwarf_Debug dbg)
{
	dwarf_dump_strtab(dbg);
	dwarf_dump_shstrtab(dbg);
	dwarf_dump_dbgstr(dbg);
	dwarf_dump_symtab(dbg);
	dwarf_dump_info(dbg);
}
