Imported Upstream version 0.155
[platform/upstream/elfutils.git] / src / readelf.c
index 8876688..2954e74 100644 (file)
@@ -1,28 +1,20 @@
 /* Print information from ELF file in human-readable form.
    Copyright (C) 1999-2012 Red Hat, Inc.
-   This file is part of Red Hat elfutils.
+   This file is part of elfutils.
    Written by Ulrich Drepper <drepper@redhat.com>, 1999.
 
-   Red Hat elfutils 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; version 2 of the License.
+   This file 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.
 
-   Red Hat elfutils is distributed in the hope that it will be useful, but
+   elfutils 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.
+   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 Red Hat elfutils; if not, write to the Free Software Foundation,
-   Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
-
-   Red Hat elfutils is an included package of the Open Invention Network.
-   An included package of the Open Invention Network is a package for which
-   Open Invention Network licensees cross-license their patents.  No patent
-   license is granted, either expressly or impliedly, by designation as an
-   included package.  Should you wish to participate in the Open Invention
-   Network licensing program, please visit www.openinventionnetwork.com
-   <http://www.openinventionnetwork.com>.  */
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #ifdef HAVE_CONFIG_H
 # include <config.h>
@@ -59,6 +51,8 @@
 #include "../libdwfl/libdwflP.h"
 #include "../libdw/memory-access.h"
 
+#include "../libdw/known-dwarf.h"
+
 
 /* Name and version of program.  */
 static void print_version (FILE *stream, struct argp_state *state);
@@ -94,7 +88,7 @@ static const struct argp_option options[] =
   { "debug-dump", 'w', "SECTION", OPTION_ARG_OPTIONAL,
     N_("Display DWARF section content.  SECTION can be one of abbrev, "
        "aranges, frame, gdb_index, info, loc, line, ranges, pubnames, str, "
-       "macinfo, or exception"), 0 },
+       "macinfo, macro or exception"), 0 },
   { "hex-dump", 'x', "SECTION", 0,
     N_("Dump the uninterpreted contents of SECTION, by number or name"), 0 },
   { "strings", 'p', "SECTION", OPTION_ARG_OPTIONAL,
@@ -191,10 +185,12 @@ static enum section_e
   section_ranges = 512,        /* .debug_ranges  */
   section_exception = 1024,    /* .eh_frame & al.  */
   section_gdb_index = 2048,    /* .gdb_index  */
+  section_macro = 4096,                /* .debug_macro  */
   section_all = (section_abbrev | section_aranges | section_frame
                 | section_info | section_line | section_loc
                 | section_pubnames | section_str | section_macinfo
-                | section_ranges | section_exception | section_gdb_index)
+                | section_ranges | section_exception | section_gdb_index
+                | section_macro)
 } print_debug_sections, implicit_debug_sections;
 
 /* Select hex dumping of sections.  */
@@ -403,6 +399,8 @@ parse_opt (int key, char *arg,
        print_debug_sections |= section_str;
       else if (strcmp (arg, "macinfo") == 0)
        print_debug_sections |= section_macinfo;
+      else if (strcmp (arg, "macro") == 0)
+       print_debug_sections |= section_macro;
       else if (strcmp (arg, "exception") == 0)
        print_debug_sections |= section_exception;
       else if (strcmp (arg, "gdb_index") == 0)
@@ -3188,521 +3186,58 @@ format_dwarf_addr (Dwfl_Module *dwflmod,
 static const char *
 dwarf_tag_string (unsigned int tag)
 {
-  static const char *const known_tags[]  =
-    {
-      [DW_TAG_array_type] = "array_type",
-      [DW_TAG_class_type] = "class_type",
-      [DW_TAG_entry_point] = "entry_point",
-      [DW_TAG_enumeration_type] = "enumeration_type",
-      [DW_TAG_formal_parameter] = "formal_parameter",
-      [DW_TAG_imported_declaration] = "imported_declaration",
-      [DW_TAG_label] = "label",
-      [DW_TAG_lexical_block] = "lexical_block",
-      [DW_TAG_member] = "member",
-      [DW_TAG_pointer_type] = "pointer_type",
-      [DW_TAG_reference_type] = "reference_type",
-      [DW_TAG_compile_unit] = "compile_unit",
-      [DW_TAG_string_type] = "string_type",
-      [DW_TAG_structure_type] = "structure_type",
-      [DW_TAG_subroutine_type] = "subroutine_type",
-      [DW_TAG_typedef] = "typedef",
-      [DW_TAG_union_type] = "union_type",
-      [DW_TAG_unspecified_parameters] = "unspecified_parameters",
-      [DW_TAG_variant] = "variant",
-      [DW_TAG_common_block] = "common_block",
-      [DW_TAG_common_inclusion] = "common_inclusion",
-      [DW_TAG_inheritance] = "inheritance",
-      [DW_TAG_inlined_subroutine] = "inlined_subroutine",
-      [DW_TAG_module] = "module",
-      [DW_TAG_ptr_to_member_type] = "ptr_to_member_type",
-      [DW_TAG_set_type] = "set_type",
-      [DW_TAG_subrange_type] = "subrange_type",
-      [DW_TAG_with_stmt] = "with_stmt",
-      [DW_TAG_access_declaration] = "access_declaration",
-      [DW_TAG_base_type] = "base_type",
-      [DW_TAG_catch_block] = "catch_block",
-      [DW_TAG_const_type] = "const_type",
-      [DW_TAG_constant] = "constant",
-      [DW_TAG_enumerator] = "enumerator",
-      [DW_TAG_file_type] = "file_type",
-      [DW_TAG_friend] = "friend",
-      [DW_TAG_namelist] = "namelist",
-      [DW_TAG_namelist_item] = "namelist_item",
-      [DW_TAG_packed_type] = "packed_type",
-      [DW_TAG_subprogram] = "subprogram",
-      [DW_TAG_template_type_parameter] = "template_type_parameter",
-      [DW_TAG_template_value_parameter] = "template_value_parameter",
-      [DW_TAG_thrown_type] = "thrown_type",
-      [DW_TAG_try_block] = "try_block",
-      [DW_TAG_variant_part] = "variant_part",
-      [DW_TAG_variable] = "variable",
-      [DW_TAG_volatile_type] = "volatile_type",
-      [DW_TAG_dwarf_procedure] = "dwarf_procedure",
-      [DW_TAG_restrict_type] = "restrict_type",
-      [DW_TAG_interface_type] = "interface_type",
-      [DW_TAG_namespace] = "namespace",
-      [DW_TAG_imported_module] = "imported_module",
-      [DW_TAG_unspecified_type] = "unspecified_type",
-      [DW_TAG_partial_unit] = "partial_unit",
-      [DW_TAG_imported_unit] = "imported_unit",
-      [DW_TAG_mutable_type] = "mutable_type",
-      [DW_TAG_condition] = "condition",
-      [DW_TAG_shared_type] = "shared_type",
-      [DW_TAG_type_unit] = "type_unit",
-      [DW_TAG_rvalue_reference_type] = "rvalue_reference_type",
-      [DW_TAG_template_alias] = "template_alias",
-    };
-  const unsigned int nknown_tags = (sizeof (known_tags)
-                                   / sizeof (known_tags[0]));
-  static char buf[40];
-  const char *result = NULL;
-
-  if (likely (tag < nknown_tags))
-    result = known_tags[tag];
-
-  if (unlikely (result == NULL))
-    /* There are a few known extensions.  */
-    switch (tag)
-      {
-      case DW_TAG_MIPS_loop:
-       result = "MIPS_loop";
-       break;
-
-      case DW_TAG_format_label:
-       result = "format_label";
-       break;
-
-      case DW_TAG_function_template:
-       result = "function_template";
-       break;
-
-      case DW_TAG_class_template:
-       result = "class_template";
-       break;
-
-      case DW_TAG_GNU_BINCL:
-       result = "GNU_BINCL";
-       break;
-
-      case DW_TAG_GNU_EINCL:
-       result = "GNU_EINCL";
-       break;
-
-      case DW_TAG_GNU_template_template_param:
-       result = "GNU_template_template_param";
-       break;
-
-      case DW_TAG_GNU_template_parameter_pack:
-       result = "GNU_template_parameter_pack";
-       break;
-
-      case DW_TAG_GNU_formal_parameter_pack:
-       result = "GNU_formal_parameter_pack";
-       break;
-
-      case DW_TAG_GNU_call_site:
-       result = "GNU_call_site";
-       break;
-
-      case DW_TAG_GNU_call_site_parameter:
-       result = "GNU_call_site_parameter";
-       break;
-
-      default:
-       if (tag < DW_TAG_lo_user)
-         snprintf (buf, sizeof buf, gettext ("unknown tag %hx"), tag);
-       else
-         snprintf (buf, sizeof buf, gettext ("unknown user tag %hx"), tag);
-       result = buf;
-       break;
-      }
-
-  return result;
+  switch (tag)
+    {
+#define ONE_KNOWN_DW_TAG(NAME, CODE) case CODE: return #NAME;
+      ALL_KNOWN_DW_TAG
+#undef ONE_KNOWN_DW_TAG
+    default:
+      return NULL;
+    }
 }
 
 
 static const char *
 dwarf_attr_string (unsigned int attrnum)
 {
-  static const char *const known_attrs[] =
-    {
-      [DW_AT_sibling] = "sibling",
-      [DW_AT_location] = "location",
-      [DW_AT_name] = "name",
-      [DW_AT_ordering] = "ordering",
-      [DW_AT_subscr_data] = "subscr_data",
-      [DW_AT_byte_size] = "byte_size",
-      [DW_AT_bit_offset] = "bit_offset",
-      [DW_AT_bit_size] = "bit_size",
-      [DW_AT_element_list] = "element_list",
-      [DW_AT_stmt_list] = "stmt_list",
-      [DW_AT_low_pc] = "low_pc",
-      [DW_AT_high_pc] = "high_pc",
-      [DW_AT_language] = "language",
-      [DW_AT_member] = "member",
-      [DW_AT_discr] = "discr",
-      [DW_AT_discr_value] = "discr_value",
-      [DW_AT_visibility] = "visibility",
-      [DW_AT_import] = "import",
-      [DW_AT_string_length] = "string_length",
-      [DW_AT_common_reference] = "common_reference",
-      [DW_AT_comp_dir] = "comp_dir",
-      [DW_AT_const_value] = "const_value",
-      [DW_AT_containing_type] = "containing_type",
-      [DW_AT_default_value] = "default_value",
-      [DW_AT_inline] = "inline",
-      [DW_AT_is_optional] = "is_optional",
-      [DW_AT_lower_bound] = "lower_bound",
-      [DW_AT_producer] = "producer",
-      [DW_AT_prototyped] = "prototyped",
-      [DW_AT_return_addr] = "return_addr",
-      [DW_AT_start_scope] = "start_scope",
-      [DW_AT_bit_stride] = "bit_stride",
-      [DW_AT_upper_bound] = "upper_bound",
-      [DW_AT_abstract_origin] = "abstract_origin",
-      [DW_AT_accessibility] = "accessibility",
-      [DW_AT_address_class] = "address_class",
-      [DW_AT_artificial] = "artificial",
-      [DW_AT_base_types] = "base_types",
-      [DW_AT_calling_convention] = "calling_convention",
-      [DW_AT_count] = "count",
-      [DW_AT_data_member_location] = "data_member_location",
-      [DW_AT_decl_column] = "decl_column",
-      [DW_AT_decl_file] = "decl_file",
-      [DW_AT_decl_line] = "decl_line",
-      [DW_AT_declaration] = "declaration",
-      [DW_AT_discr_list] = "discr_list",
-      [DW_AT_encoding] = "encoding",
-      [DW_AT_external] = "external",
-      [DW_AT_frame_base] = "frame_base",
-      [DW_AT_friend] = "friend",
-      [DW_AT_identifier_case] = "identifier_case",
-      [DW_AT_macro_info] = "macro_info",
-      [DW_AT_namelist_item] = "namelist_item",
-      [DW_AT_priority] = "priority",
-      [DW_AT_segment] = "segment",
-      [DW_AT_specification] = "specification",
-      [DW_AT_static_link] = "static_link",
-      [DW_AT_type] = "type",
-      [DW_AT_use_location] = "use_location",
-      [DW_AT_variable_parameter] = "variable_parameter",
-      [DW_AT_virtuality] = "virtuality",
-      [DW_AT_vtable_elem_location] = "vtable_elem_location",
-      [DW_AT_allocated] = "allocated",
-      [DW_AT_associated] = "associated",
-      [DW_AT_data_location] = "data_location",
-      [DW_AT_byte_stride] = "byte_stride",
-      [DW_AT_entry_pc] = "entry_pc",
-      [DW_AT_use_UTF8] = "use_UTF8",
-      [DW_AT_extension] = "extension",
-      [DW_AT_ranges] = "ranges",
-      [DW_AT_trampoline] = "trampoline",
-      [DW_AT_call_column] = "call_column",
-      [DW_AT_call_file] = "call_file",
-      [DW_AT_call_line] = "call_line",
-      [DW_AT_description] = "description",
-      [DW_AT_binary_scale] = "binary_scale",
-      [DW_AT_decimal_scale] = "decimal_scale",
-      [DW_AT_small] = "small",
-      [DW_AT_decimal_sign] = "decimal_sign",
-      [DW_AT_digit_count] = "digit_count",
-      [DW_AT_picture_string] = "picture_string",
-      [DW_AT_mutable] = "mutable",
-      [DW_AT_threads_scaled] = "threads_scaled",
-      [DW_AT_explicit] = "explicit",
-      [DW_AT_object_pointer] = "object_pointer",
-      [DW_AT_endianity] = "endianity",
-      [DW_AT_elemental] = "elemental",
-      [DW_AT_pure] = "pure",
-      [DW_AT_recursive] = "recursive",
-      [DW_AT_signature] = "signature",
-      [DW_AT_main_subprogram] = "main_subprogram",
-      [DW_AT_data_bit_offset] = "data_bit_offset",
-      [DW_AT_const_expr] = "const_expr",
-      [DW_AT_enum_class] = "enum_class",
-      [DW_AT_linkage_name] = "linkage_name",
-    };
-  const unsigned int nknown_attrs = (sizeof (known_attrs)
-                                    / sizeof (known_attrs[0]));
-  static char buf[40];
-  const char *result = NULL;
-
-  if (likely (attrnum < nknown_attrs))
-    result = known_attrs[attrnum];
-
-  if (unlikely (result == NULL))
-    /* There are a few known extensions.  */
-    switch (attrnum)
-      {
-      case DW_AT_MIPS_fde:
-       result = "MIPS_fde";
-       break;
-
-      case DW_AT_MIPS_loop_begin:
-       result = "MIPS_loop_begin";
-       break;
-
-      case DW_AT_MIPS_tail_loop_begin:
-       result = "MIPS_tail_loop_begin";
-       break;
-
-      case DW_AT_MIPS_epilog_begin:
-       result = "MIPS_epilog_begin";
-       break;
-
-      case DW_AT_MIPS_loop_unroll_factor:
-       result = "MIPS_loop_unroll_factor";
-       break;
-
-      case DW_AT_MIPS_software_pipeline_depth:
-       result = "MIPS_software_pipeline_depth";
-       break;
-
-      case DW_AT_MIPS_linkage_name:
-       result = "MIPS_linkage_name";
-       break;
-
-      case DW_AT_MIPS_stride:
-       result = "MIPS_stride";
-       break;
-
-      case DW_AT_MIPS_abstract_name:
-       result = "MIPS_abstract_name";
-       break;
-
-      case DW_AT_MIPS_clone_origin:
-       result = "MIPS_clone_origin";
-       break;
-
-      case DW_AT_MIPS_has_inlines:
-       result = "MIPS_has_inlines";
-       break;
-
-      case DW_AT_MIPS_stride_byte:
-       result = "MIPS_stride_byte";
-       break;
-
-      case DW_AT_MIPS_stride_elem:
-       result = "MIPS_stride_elem";
-       break;
-
-      case DW_AT_MIPS_ptr_dopetype:
-       result = "MIPS_ptr_dopetype";
-       break;
-
-      case DW_AT_MIPS_allocatable_dopetype:
-       result = "MIPS_allocatable_dopetype";
-       break;
-
-      case DW_AT_MIPS_assumed_shape_dopetype:
-       result = "MIPS_assumed_shape_dopetype";
-       break;
-
-      case DW_AT_MIPS_assumed_size:
-       result = "MIPS_assumed_size";
-       break;
-
-      case DW_AT_sf_names:
-       result = "sf_names";
-       break;
-
-      case DW_AT_src_info:
-       result = "src_info";
-       break;
-
-      case DW_AT_mac_info:
-       result = "mac_info";
-       break;
-
-      case DW_AT_src_coords:
-       result = "src_coords";
-       break;
-
-      case DW_AT_body_begin:
-       result = "body_begin";
-       break;
-
-      case DW_AT_body_end:
-       result = "body_end";
-       break;
-
-      case DW_AT_GNU_vector:
-       result = "GNU_vector";
-       break;
-
-      case DW_AT_GNU_guarded_by:
-       result = "GNU_guarded_by";
-       break;
-
-      case DW_AT_GNU_pt_guarded_by:
-       result = "GNU_pt_guarded_by";
-       break;
-
-      case DW_AT_GNU_guarded:
-       result = "GNU_guarded";
-       break;
-
-      case DW_AT_GNU_pt_guarded:
-       result = "GNU_pt_guarded";
-       break;
-
-      case DW_AT_GNU_locks_excluded:
-       result = "GNU_locks_excluded";
-       break;
-
-      case DW_AT_GNU_exclusive_locks_required:
-       result = "GNU_exclusive_locks_required";
-       break;
-
-      case DW_AT_GNU_shared_locks_required:
-       result = "GNU_shared_locks_required";
-       break;
-
-      case DW_AT_GNU_odr_signature:
-       result = "GNU_odr_signature";
-       break;
-
-      case DW_AT_GNU_template_name:
-       result = "GNU_template_name";
-       break;
-
-      case DW_AT_GNU_call_site_value:
-       result = "GNU_call_site_value";
-       break;
-
-      case DW_AT_GNU_call_site_data_value:
-       result = "GNU_call_site_data_value";
-       break;
-
-      case DW_AT_GNU_call_site_target:
-       result = "GNU_call_site_target";
-       break;
-
-      case DW_AT_GNU_call_site_target_clobbered:
-       result = "GNU_call_site_target_clobbered";
-       break;
-
-      case DW_AT_GNU_tail_call:
-       result = "GNU_tail_call";
-       break;
-
-      case DW_AT_GNU_all_tail_call_sites:
-       result = "GNU_all_tail_call_sites";
-       break;
-
-      case DW_AT_GNU_all_call_sites:
-       result = "GNU_all_call_sites";
-       break;
-
-      case DW_AT_GNU_all_source_call_sites:
-       result = "GNU_all_source_call_sites";
-       break;
-
-      default:
-       if (attrnum < DW_AT_lo_user)
-         snprintf (buf, sizeof buf, gettext ("unknown attribute %hx"),
-                   attrnum);
-       else
-         snprintf (buf, sizeof buf, gettext ("unknown user attribute %hx"),
-                   attrnum);
-       result = buf;
-       break;
-      }
-
-  return result;
+  switch (attrnum)
+    {
+#define ONE_KNOWN_DW_AT(NAME, CODE) case CODE: return #NAME;
+      ALL_KNOWN_DW_AT
+#undef ONE_KNOWN_DW_AT
+    default:
+      return NULL;
+    }
 }
 
 
 static const char *
 dwarf_form_string (unsigned int form)
 {
-  static const char *const known_forms[] =
-    {
-      [DW_FORM_addr] = "addr",
-      [DW_FORM_block2] = "block2",
-      [DW_FORM_block4] = "block4",
-      [DW_FORM_data2] = "data2",
-      [DW_FORM_data4] = "data4",
-      [DW_FORM_data8] = "data8",
-      [DW_FORM_string] = "string",
-      [DW_FORM_block] = "block",
-      [DW_FORM_block1] = "block1",
-      [DW_FORM_data1] = "data1",
-      [DW_FORM_flag] = "flag",
-      [DW_FORM_sdata] = "sdata",
-      [DW_FORM_strp] = "strp",
-      [DW_FORM_udata] = "udata",
-      [DW_FORM_ref_addr] = "ref_addr",
-      [DW_FORM_ref1] = "ref1",
-      [DW_FORM_ref2] = "ref2",
-      [DW_FORM_ref4] = "ref4",
-      [DW_FORM_ref8] = "ref8",
-      [DW_FORM_ref_udata] = "ref_udata",
-      [DW_FORM_indirect] = "indirect",
-      [DW_FORM_sec_offset] = "sec_offset",
-      [DW_FORM_exprloc] = "exprloc",
-      [DW_FORM_flag_present] = "flag_present",
-      [DW_FORM_ref_sig8] = "ref_sig8",
-    };
-  const unsigned int nknown_forms = (sizeof (known_forms)
-                                    / sizeof (known_forms[0]));
-  static char buf[40];
-  const char *result = NULL;
-
-  if (likely (form < nknown_forms))
-    result = known_forms[form];
-
-  if (unlikely (result == NULL))
+  switch (form)
     {
-      snprintf (buf, sizeof buf, gettext ("unknown form %#" PRIx64),
-               (uint64_t) form);
-      result = buf;
+#define ONE_KNOWN_DW_FORM_DESC(NAME, CODE, DESC) ONE_KNOWN_DW_FORM (NAME, CODE)
+#define ONE_KNOWN_DW_FORM(NAME, CODE) case CODE: return #NAME;
+      ALL_KNOWN_DW_FORM
+#undef ONE_KNOWN_DW_FORM
+#undef ONE_KNOWN_DW_FORM_DESC
+    default:
+      return NULL;
     }
-
-  return result;
 }
 
 
 static const char *
 dwarf_lang_string (unsigned int lang)
 {
-  static const char *const known[] =
+  switch (lang)
     {
-      [DW_LANG_C89] = "ISO C89",
-      [DW_LANG_C] = "C",
-      [DW_LANG_Ada83] = "Ada83",
-      [DW_LANG_C_plus_plus] = "C++",
-      [DW_LANG_Cobol74] = "Cobol74",
-      [DW_LANG_Cobol85] = "Cobol85",
-      [DW_LANG_Fortran77] = "Fortran77",
-      [DW_LANG_Fortran90] = "Fortran90",
-      [DW_LANG_Pascal83] = "Pascal83",
-      [DW_LANG_Modula2] = "Modula2",
-      [DW_LANG_Java] = "Java",
-      [DW_LANG_C99] = "ISO C99",
-      [DW_LANG_Ada95] = "Ada95",
-      [DW_LANG_Fortran95] = "Fortran95",
-      [DW_LANG_PL1] = "PL1",
-      [DW_LANG_Objc] = "Objective C",
-      [DW_LANG_ObjC_plus_plus] = "Objective C++",
-      [DW_LANG_UPC] = "UPC",
-      [DW_LANG_D] = "D",
-    };
-
-  if (likely (lang < sizeof (known) / sizeof (known[0])))
-    return known[lang];
-  else if (lang == DW_LANG_Mips_Assembler)
-    /* This language tag is used for assembler in general.  */
-    return "Assembler";
-
-  if (lang >= DW_LANG_lo_user && lang <= DW_LANG_hi_user)
-    {
-      static char buf[30];
-      snprintf (buf, sizeof (buf), "lo_user+%u", lang - DW_LANG_lo_user);
-      return buf;
+#define ONE_KNOWN_DW_LANG_DESC(NAME, CODE, DESC) case CODE: return #NAME;
+      ALL_KNOWN_DW_LANG
+#undef ONE_KNOWN_DW_LANG_DESC
+    default:
+      return NULL;
     }
-
-  return "???";
 }
 
 
@@ -3711,16 +3246,15 @@ dwarf_inline_string (unsigned int code)
 {
   static const char *const known[] =
     {
-      [DW_INL_not_inlined] = "not_inlined",
-      [DW_INL_inlined] = "inlined",
-      [DW_INL_declared_not_inlined] = "declared_not_inlined",
-      [DW_INL_declared_inlined] = "declared_inlined"
+#define ONE_KNOWN_DW_INL(NAME, CODE) [CODE] = #NAME,
+      ALL_KNOWN_DW_INL
+#undef ONE_KNOWN_DW_INL
     };
 
   if (likely (code < sizeof (known) / sizeof (known[0])))
     return known[code];
 
-  return "???";
+  return NULL;
 }
 
 
@@ -3729,35 +3263,15 @@ dwarf_encoding_string (unsigned int code)
 {
   static const char *const known[] =
     {
-      [DW_ATE_void] = "void",
-      [DW_ATE_address] = "address",
-      [DW_ATE_boolean] = "boolean",
-      [DW_ATE_complex_float] = "complex_float",
-      [DW_ATE_float] = "float",
-      [DW_ATE_signed] = "signed",
-      [DW_ATE_signed_char] = "signed_char",
-      [DW_ATE_unsigned] = "unsigned",
-      [DW_ATE_unsigned_char] = "unsigned_char",
-      [DW_ATE_imaginary_float] = "imaginary_float",
-      [DW_ATE_packed_decimal] = "packed_decimal",
-      [DW_ATE_numeric_string] = "numeric_string",
-      [DW_ATE_edited] = "edited",
-      [DW_ATE_signed_fixed] = "signed_fixed",
-      [DW_ATE_unsigned_fixed] = "unsigned_fixed",
-      [DW_ATE_decimal_float] = "decimal_float",
+#define ONE_KNOWN_DW_ATE(NAME, CODE) [CODE] = #NAME,
+      ALL_KNOWN_DW_ATE
+#undef ONE_KNOWN_DW_ATE
     };
 
   if (likely (code < sizeof (known) / sizeof (known[0])))
     return known[code];
 
-  if (code >= DW_ATE_lo_user && code <= DW_ATE_hi_user)
-    {
-      static char buf[30];
-      snprintf (buf, sizeof (buf), "lo_user+%u", code - DW_ATE_lo_user);
-      return buf;
-    }
-
-  return "???";
+  return NULL;
 }
 
 
@@ -3766,15 +3280,15 @@ dwarf_access_string (unsigned int code)
 {
   static const char *const known[] =
     {
-      [DW_ACCESS_public] = "public",
-      [DW_ACCESS_protected] = "protected",
-      [DW_ACCESS_private] = "private"
+#define ONE_KNOWN_DW_ACCESS(NAME, CODE) [CODE] = #NAME,
+      ALL_KNOWN_DW_ACCESS
+#undef ONE_KNOWN_DW_ACCESS
     };
 
   if (likely (code < sizeof (known) / sizeof (known[0])))
     return known[code];
 
-  return "???";
+  return NULL;
 }
 
 
@@ -3783,15 +3297,15 @@ dwarf_visibility_string (unsigned int code)
 {
   static const char *const known[] =
     {
-      [DW_VIS_local] = "local",
-      [DW_VIS_exported] = "exported",
-      [DW_VIS_qualified] = "qualified"
+#define ONE_KNOWN_DW_VIS(NAME, CODE) [CODE] = #NAME,
+      ALL_KNOWN_DW_VIS
+#undef ONE_KNOWN_DW_VIS
     };
 
   if (likely (code < sizeof (known) / sizeof (known[0])))
     return known[code];
 
-  return "???";
+  return NULL;
 }
 
 
@@ -3800,15 +3314,15 @@ dwarf_virtuality_string (unsigned int code)
 {
   static const char *const known[] =
     {
-      [DW_VIRTUALITY_none] = "none",
-      [DW_VIRTUALITY_virtual] = "virtual",
-      [DW_VIRTUALITY_pure_virtual] = "pure_virtual"
+#define ONE_KNOWN_DW_VIRTUALITY(NAME, CODE) [CODE] = #NAME,
+      ALL_KNOWN_DW_VIRTUALITY
+#undef ONE_KNOWN_DW_VIRTUALITY
     };
 
   if (likely (code < sizeof (known) / sizeof (known[0])))
     return known[code];
 
-  return "???";
+  return NULL;
 }
 
 
@@ -3817,16 +3331,15 @@ dwarf_identifier_case_string (unsigned int code)
 {
   static const char *const known[] =
     {
-      [DW_ID_case_sensitive] = "sensitive",
-      [DW_ID_up_case] = "up_case",
-      [DW_ID_down_case] = "down_case",
-      [DW_ID_case_insensitive] = "insensitive"
+#define ONE_KNOWN_DW_ID(NAME, CODE) [CODE] = #NAME,
+      ALL_KNOWN_DW_ID
+#undef ONE_KNOWN_DW_ID
     };
 
   if (likely (code < sizeof (known) / sizeof (known[0])))
     return known[code];
 
-  return "???";
+  return NULL;
 }
 
 
@@ -3835,22 +3348,15 @@ dwarf_calling_convention_string (unsigned int code)
 {
   static const char *const known[] =
     {
-      [DW_CC_normal] = "normal",
-      [DW_CC_program] = "program",
-      [DW_CC_nocall] = "nocall",
+#define ONE_KNOWN_DW_CC(NAME, CODE) [CODE] = #NAME,
+      ALL_KNOWN_DW_CC
+#undef ONE_KNOWN_DW_CC
     };
 
   if (likely (code < sizeof (known) / sizeof (known[0])))
     return known[code];
 
-  if (code >= DW_CC_lo_user && code <= DW_CC_hi_user)
-    {
-      static char buf[30];
-      snprintf (buf, sizeof (buf), "lo_user+%u", code - DW_CC_lo_user);
-      return buf;
-    }
-
-  return "???";
+  return NULL;
 }
 
 
@@ -3859,14 +3365,15 @@ dwarf_ordering_string (unsigned int code)
 {
   static const char *const known[] =
     {
-      [DW_ORD_row_major] = "row_major",
-      [DW_ORD_col_major] = "col_major"
+#define ONE_KNOWN_DW_ORD(NAME, CODE) [CODE] = #NAME,
+      ALL_KNOWN_DW_ORD
+#undef ONE_KNOWN_DW_ORD
     };
 
   if (likely (code < sizeof (known) / sizeof (known[0])))
     return known[code];
 
-  return "???";
+  return NULL;
 }
 
 
@@ -3875,17 +3382,171 @@ dwarf_discr_list_string (unsigned int code)
 {
   static const char *const known[] =
     {
-      [DW_DSC_label] = "label",
-      [DW_DSC_range] = "range"
+#define ONE_KNOWN_DW_DSC(NAME, CODE) [CODE] = #NAME,
+      ALL_KNOWN_DW_DSC
+#undef ONE_KNOWN_DW_DSC
     };
 
   if (likely (code < sizeof (known) / sizeof (known[0])))
     return known[code];
 
+  return NULL;
+}
+
+
+static const char *
+dwarf_locexpr_opcode_string (unsigned int code)
+{
+  static const char *const known[] =
+    {
+      /* Normally we can't affort building huge table of 64K entries,
+        most of them zero, just because there are a couple defined
+        values at the far end.  In case of opcodes, it's OK.  */
+#define ONE_KNOWN_DW_OP_DESC(NAME, CODE, DESC) ONE_KNOWN_DW_OP (NAME, CODE)
+#define ONE_KNOWN_DW_OP(NAME, CODE) [CODE] = #NAME,
+      ALL_KNOWN_DW_OP
+#undef ONE_KNOWN_DW_OP
+#undef ONE_KNOWN_DW_OP_DESC
+    };
+
+  if (likely (code < sizeof (known) / sizeof (known[0])))
+    return known[code];
+
+  return NULL;
+}
+
+
+/* Used by all dwarf_foo_name functions.  */
+static const char *
+string_or_unknown (const char *known, unsigned int code,
+                   unsigned int lo_user, unsigned int hi_user,
+                  bool print_unknown_num)
+{
+  static char unknown_buf[20];
+
+  if (likely (known != NULL))
+    return known;
+
+  if (lo_user != 0 && code >= lo_user && code <= hi_user)
+    {
+      snprintf (unknown_buf, sizeof unknown_buf, "lo_user+%#x",
+               code - lo_user);
+      return unknown_buf;
+    }
+
+  if (print_unknown_num)
+    {
+      snprintf (unknown_buf, sizeof unknown_buf, "??? (%#x)", code);
+      return unknown_buf;
+    }
+
   return "???";
 }
 
 
+static const char *
+dwarf_tag_name (unsigned int tag)
+{
+  const char *ret = dwarf_tag_string (tag);
+  return string_or_unknown (ret, tag, DW_TAG_lo_user, DW_TAG_hi_user, true);
+}
+
+static const char *
+dwarf_attr_name (unsigned int attr)
+{
+  const char *ret = dwarf_attr_string (attr);
+  return string_or_unknown (ret, attr, DW_AT_lo_user, DW_AT_hi_user, true);
+}
+
+
+static const char *
+dwarf_form_name (unsigned int form)
+{
+  const char *ret = dwarf_form_string (form);
+  return string_or_unknown (ret, form, 0, 0, true);
+}
+
+
+static const char *
+dwarf_lang_name (unsigned int lang)
+{
+  const char *ret = dwarf_lang_string (lang);
+  return string_or_unknown (ret, lang, DW_LANG_lo_user, DW_LANG_hi_user, false);
+}
+
+
+static const char *
+dwarf_inline_name (unsigned int code)
+{
+  const char *ret = dwarf_inline_string (code);
+  return string_or_unknown (ret, code, 0, 0, false);
+}
+
+
+static const char *
+dwarf_encoding_name (unsigned int code)
+{
+  const char *ret = dwarf_encoding_string (code);
+  return string_or_unknown (ret, code, DW_ATE_lo_user, DW_ATE_hi_user, false);
+}
+
+
+static const char *
+dwarf_access_name (unsigned int code)
+{
+  const char *ret = dwarf_access_string (code);
+  return string_or_unknown (ret, code, 0, 0, false);
+}
+
+
+static const char *
+dwarf_visibility_name (unsigned int code)
+{
+  const char *ret = dwarf_visibility_string (code);
+  return string_or_unknown (ret, code, 0, 0, false);
+}
+
+
+static const char *
+dwarf_virtuality_name (unsigned int code)
+{
+  const char *ret = dwarf_virtuality_string (code);
+  return string_or_unknown (ret, code, 0, 0, false);
+}
+
+
+static const char *
+dwarf_identifier_case_name (unsigned int code)
+{
+  const char *ret = dwarf_identifier_case_string (code);
+  return string_or_unknown (ret, code, 0, 0, false);
+}
+
+
+static const char *
+dwarf_calling_convention_name (unsigned int code)
+{
+  const char *ret = dwarf_calling_convention_string (code);
+  return string_or_unknown (ret, code, DW_CC_lo_user, DW_CC_hi_user, false);
+}
+
+
+static const char *
+dwarf_ordering_name (unsigned int code)
+{
+  const char *ret = dwarf_ordering_string (code);
+  return string_or_unknown (ret, code, 0, 0, false);
+}
+
+
+static const char *
+dwarf_discr_list_name (unsigned int code)
+{
+  const char *ret = dwarf_discr_list_string (code);
+  return string_or_unknown (ret, code, 0, 0, false);
+}
+
+
 static void
 print_block (size_t n, const void *block)
 {
@@ -3909,171 +3570,6 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest,
 {
   const unsigned int ref_size = vers < 3 ? addrsize : offset_size;
 
-  static const char *const known[] =
-    {
-      [DW_OP_addr] = "addr",
-      [DW_OP_deref] = "deref",
-      [DW_OP_const1u] = "const1u",
-      [DW_OP_const1s] = "const1s",
-      [DW_OP_const2u] = "const2u",
-      [DW_OP_const2s] = "const2s",
-      [DW_OP_const4u] = "const4u",
-      [DW_OP_const4s] = "const4s",
-      [DW_OP_const8u] = "const8u",
-      [DW_OP_const8s] = "const8s",
-      [DW_OP_constu] = "constu",
-      [DW_OP_consts] = "consts",
-      [DW_OP_dup] = "dup",
-      [DW_OP_drop] = "drop",
-      [DW_OP_over] = "over",
-      [DW_OP_pick] = "pick",
-      [DW_OP_swap] = "swap",
-      [DW_OP_rot] = "rot",
-      [DW_OP_xderef] = "xderef",
-      [DW_OP_abs] = "abs",
-      [DW_OP_and] = "and",
-      [DW_OP_div] = "div",
-      [DW_OP_minus] = "minus",
-      [DW_OP_mod] = "mod",
-      [DW_OP_mul] = "mul",
-      [DW_OP_neg] = "neg",
-      [DW_OP_not] = "not",
-      [DW_OP_or] = "or",
-      [DW_OP_plus] = "plus",
-      [DW_OP_plus_uconst] = "plus_uconst",
-      [DW_OP_shl] = "shl",
-      [DW_OP_shr] = "shr",
-      [DW_OP_shra] = "shra",
-      [DW_OP_xor] = "xor",
-      [DW_OP_bra] = "bra",
-      [DW_OP_eq] = "eq",
-      [DW_OP_ge] = "ge",
-      [DW_OP_gt] = "gt",
-      [DW_OP_le] = "le",
-      [DW_OP_lt] = "lt",
-      [DW_OP_ne] = "ne",
-      [DW_OP_skip] = "skip",
-      [DW_OP_lit0] = "lit0",
-      [DW_OP_lit1] = "lit1",
-      [DW_OP_lit2] = "lit2",
-      [DW_OP_lit3] = "lit3",
-      [DW_OP_lit4] = "lit4",
-      [DW_OP_lit5] = "lit5",
-      [DW_OP_lit6] = "lit6",
-      [DW_OP_lit7] = "lit7",
-      [DW_OP_lit8] = "lit8",
-      [DW_OP_lit9] = "lit9",
-      [DW_OP_lit10] = "lit10",
-      [DW_OP_lit11] = "lit11",
-      [DW_OP_lit12] = "lit12",
-      [DW_OP_lit13] = "lit13",
-      [DW_OP_lit14] = "lit14",
-      [DW_OP_lit15] = "lit15",
-      [DW_OP_lit16] = "lit16",
-      [DW_OP_lit17] = "lit17",
-      [DW_OP_lit18] = "lit18",
-      [DW_OP_lit19] = "lit19",
-      [DW_OP_lit20] = "lit20",
-      [DW_OP_lit21] = "lit21",
-      [DW_OP_lit22] = "lit22",
-      [DW_OP_lit23] = "lit23",
-      [DW_OP_lit24] = "lit24",
-      [DW_OP_lit25] = "lit25",
-      [DW_OP_lit26] = "lit26",
-      [DW_OP_lit27] = "lit27",
-      [DW_OP_lit28] = "lit28",
-      [DW_OP_lit29] = "lit29",
-      [DW_OP_lit30] = "lit30",
-      [DW_OP_lit31] = "lit31",
-      [DW_OP_reg0] = "reg0",
-      [DW_OP_reg1] = "reg1",
-      [DW_OP_reg2] = "reg2",
-      [DW_OP_reg3] = "reg3",
-      [DW_OP_reg4] = "reg4",
-      [DW_OP_reg5] = "reg5",
-      [DW_OP_reg6] = "reg6",
-      [DW_OP_reg7] = "reg7",
-      [DW_OP_reg8] = "reg8",
-      [DW_OP_reg9] = "reg9",
-      [DW_OP_reg10] = "reg10",
-      [DW_OP_reg11] = "reg11",
-      [DW_OP_reg12] = "reg12",
-      [DW_OP_reg13] = "reg13",
-      [DW_OP_reg14] = "reg14",
-      [DW_OP_reg15] = "reg15",
-      [DW_OP_reg16] = "reg16",
-      [DW_OP_reg17] = "reg17",
-      [DW_OP_reg18] = "reg18",
-      [DW_OP_reg19] = "reg19",
-      [DW_OP_reg20] = "reg20",
-      [DW_OP_reg21] = "reg21",
-      [DW_OP_reg22] = "reg22",
-      [DW_OP_reg23] = "reg23",
-      [DW_OP_reg24] = "reg24",
-      [DW_OP_reg25] = "reg25",
-      [DW_OP_reg26] = "reg26",
-      [DW_OP_reg27] = "reg27",
-      [DW_OP_reg28] = "reg28",
-      [DW_OP_reg29] = "reg29",
-      [DW_OP_reg30] = "reg30",
-      [DW_OP_reg31] = "reg31",
-      [DW_OP_breg0] = "breg0",
-      [DW_OP_breg1] = "breg1",
-      [DW_OP_breg2] = "breg2",
-      [DW_OP_breg3] = "breg3",
-      [DW_OP_breg4] = "breg4",
-      [DW_OP_breg5] = "breg5",
-      [DW_OP_breg6] = "breg6",
-      [DW_OP_breg7] = "breg7",
-      [DW_OP_breg8] = "breg8",
-      [DW_OP_breg9] = "breg9",
-      [DW_OP_breg10] = "breg10",
-      [DW_OP_breg11] = "breg11",
-      [DW_OP_breg12] = "breg12",
-      [DW_OP_breg13] = "breg13",
-      [DW_OP_breg14] = "breg14",
-      [DW_OP_breg15] = "breg15",
-      [DW_OP_breg16] = "breg16",
-      [DW_OP_breg17] = "breg17",
-      [DW_OP_breg18] = "breg18",
-      [DW_OP_breg19] = "breg19",
-      [DW_OP_breg20] = "breg20",
-      [DW_OP_breg21] = "breg21",
-      [DW_OP_breg22] = "breg22",
-      [DW_OP_breg23] = "breg23",
-      [DW_OP_breg24] = "breg24",
-      [DW_OP_breg25] = "breg25",
-      [DW_OP_breg26] = "breg26",
-      [DW_OP_breg27] = "breg27",
-      [DW_OP_breg28] = "breg28",
-      [DW_OP_breg29] = "breg29",
-      [DW_OP_breg30] = "breg30",
-      [DW_OP_breg31] = "breg31",
-      [DW_OP_regx] = "regx",
-      [DW_OP_fbreg] = "fbreg",
-      [DW_OP_bregx] = "bregx",
-      [DW_OP_piece] = "piece",
-      [DW_OP_deref_size] = "deref_size",
-      [DW_OP_xderef_size] = "xderef_size",
-      [DW_OP_nop] = "nop",
-      [DW_OP_push_object_address] = "push_object_address",
-      [DW_OP_call2] = "call2",
-      [DW_OP_call4] = "call4",
-      [DW_OP_call_ref] = "call_ref",
-      [DW_OP_form_tls_address] = "form_tls_address",
-      [DW_OP_call_frame_cfa] = "call_frame_cfa",
-      [DW_OP_bit_piece] = "bit_piece",
-      [DW_OP_implicit_value] = "implicit_value",
-      [DW_OP_stack_value] = "stack_value",
-      [DW_OP_GNU_implicit_pointer] = "GNU_implicit_pointer",
-      [DW_OP_GNU_entry_value] = "GNU_entry_value",
-      [DW_OP_GNU_const_type] = "GNU_const_type",
-      [DW_OP_GNU_regval_type] = "GNU_regval_type",
-      [DW_OP_GNU_deref_type] = "GNU_deref_type",
-      [DW_OP_GNU_convert] = "GNU_convert",
-      [DW_OP_GNU_reinterpret] = "GNU_reinterpret",
-    };
-
   if (len == 0)
     {
       printf ("%*s(empty)\n", indent, "");
@@ -4088,6 +3584,17 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest,
     {
       uint_fast8_t op = *data++;
 
+      const char *op_name = dwarf_locexpr_opcode_string (op);
+      if (unlikely (op_name == NULL))
+       {
+         static char buf[20];
+         if (op >= DW_OP_lo_user)
+           snprintf (buf, sizeof buf, "lo_user+%#x", op - DW_OP_lo_user);
+         else
+           snprintf (buf, sizeof buf, "??? (%#x)", op);
+         op_name = buf;
+       }
+
       switch (op)
        {
        case DW_OP_addr:;
@@ -4106,7 +3613,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest,
 
          char *a = format_dwarf_addr (dwflmod, 0, addr);
          printf ("%*s[%4" PRIuMAX "] %s %s\n",
-                 indent, "", (uintmax_t) offset, known[op], a);
+                 indent, "", (uintmax_t) offset, op_name, a);
          free (a);
 
          offset += 1 + addrsize;
@@ -4127,7 +3634,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest,
 
          printf ("%*s[%4" PRIuMAX "] %s %#" PRIxMAX "\n",
                  indent, "", (uintmax_t) offset,
-                 known[op], (uintmax_t) addr);
+                 op_name, (uintmax_t) addr);
          offset += 1 + ref_size;
          break;
 
@@ -4139,7 +3646,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest,
          NEED (1);
          printf ("%*s[%4" PRIuMAX "] %s %" PRIu8 "\n",
                  indent, "", (uintmax_t) offset,
-                 known[op], *((uint8_t *) data));
+                 op_name, *((uint8_t *) data));
          ++data;
          --len;
          offset += 2;
@@ -4150,7 +3657,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest,
          // XXX value might be modified by relocation
          printf ("%*s[%4" PRIuMAX "] %s %" PRIu16 "\n",
                  indent, "", (uintmax_t) offset,
-                 known[op], read_2ubyte_unaligned (dbg, data));
+                 op_name, read_2ubyte_unaligned (dbg, data));
          CONSUME (2);
          data += 2;
          offset += 3;
@@ -4161,7 +3668,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest,
          // XXX value might be modified by relocation
          printf ("%*s[%4" PRIuMAX "] %s %" PRIu32 "\n",
                  indent, "", (uintmax_t) offset,
-                 known[op], read_4ubyte_unaligned (dbg, data));
+                 op_name, read_4ubyte_unaligned (dbg, data));
          CONSUME (4);
          data += 4;
          offset += 5;
@@ -4172,7 +3679,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest,
          // XXX value might be modified by relocation
          printf ("%*s[%4" PRIuMAX "] %s %" PRIu64 "\n",
                  indent, "", (uintmax_t) offset,
-                 known[op], read_8ubyte_unaligned (dbg, data));
+                 op_name, (uint64_t) read_8ubyte_unaligned (dbg, data));
          CONSUME (8);
          data += 8;
          offset += 9;
@@ -4183,7 +3690,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest,
          // XXX value might be modified by relocation
          printf ("%*s[%4" PRIuMAX "] %s %" PRId8 "\n",
                  indent, "", (uintmax_t) offset,
-                 known[op], *((int8_t *) data));
+                 op_name, *((int8_t *) data));
          ++data;
          --len;
          offset += 2;
@@ -4194,7 +3701,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest,
          // XXX value might be modified by relocation
          printf ("%*s[%4" PRIuMAX "] %s %" PRId16 "\n",
                  indent, "", (uintmax_t) offset,
-                 known[op], read_2sbyte_unaligned (dbg, data));
+                 op_name, read_2sbyte_unaligned (dbg, data));
          CONSUME (2);
          data += 2;
          offset += 3;
@@ -4205,7 +3712,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest,
          // XXX value might be modified by relocation
          printf ("%*s[%4" PRIuMAX "] %s %" PRId32 "\n",
                  indent, "", (uintmax_t) offset,
-                 known[op], read_4sbyte_unaligned (dbg, data));
+                 op_name, read_4sbyte_unaligned (dbg, data));
          CONSUME (4);
          data += 4;
          offset += 5;
@@ -4216,7 +3723,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest,
          // XXX value might be modified by relocation
          printf ("%*s[%4" PRIuMAX "] %s %" PRId64 "\n",
                  indent, "", (uintmax_t) offset,
-                 known[op], read_8sbyte_unaligned (dbg, data));
+                 op_name, read_8sbyte_unaligned (dbg, data));
          CONSUME (8);
          data += 8;
          offset += 9;
@@ -4231,7 +3738,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest,
          NEED (1);
          get_uleb128 (uleb, data); /* XXX check overrun */
          printf ("%*s[%4" PRIuMAX "] %s %" PRIu64 "\n",
-                 indent, "", (uintmax_t) offset, known[op], uleb);
+                 indent, "", (uintmax_t) offset, op_name, uleb);
          CONSUME (data - start);
          offset += 1 + (data - start);
          break;
@@ -4243,7 +3750,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest,
          get_uleb128 (uleb, data); /* XXX check overrun */
          get_uleb128 (uleb2, data); /* XXX check overrun */
          printf ("%*s[%4" PRIuMAX "] %s %" PRIu64 ", %" PRIu64 "\n",
-                 indent, "", (uintmax_t) offset, known[op], uleb, uleb2);
+                 indent, "", (uintmax_t) offset, op_name, uleb, uleb2);
          CONSUME (data - start);
          offset += 1 + (data - start);
          break;
@@ -4256,7 +3763,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest,
          NEED (1);
          get_sleb128 (sleb, data); /* XXX check overrun */
          printf ("%*s[%4" PRIuMAX "] %s %" PRId64 "\n",
-                 indent, "", (uintmax_t) offset, known[op], sleb);
+                 indent, "", (uintmax_t) offset, op_name, sleb);
          CONSUME (data - start);
          offset += 1 + (data - start);
          break;
@@ -4267,7 +3774,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest,
          get_uleb128 (uleb, data); /* XXX check overrun */
          get_sleb128 (sleb, data); /* XXX check overrun */
          printf ("%*s[%4" PRIuMAX "] %s %" PRIu64 " %" PRId64 "\n",
-                 indent, "", (uintmax_t) offset, known[op], uleb, sleb);
+                 indent, "", (uintmax_t) offset, op_name, uleb, sleb);
          CONSUME (data - start);
          offset += 1 + (data - start);
          break;
@@ -4275,7 +3782,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest,
        case DW_OP_call2:
          NEED (2);
          printf ("%*s[%4" PRIuMAX "] %s %" PRIu16 "\n",
-                 indent, "", (uintmax_t) offset, known[op],
+                 indent, "", (uintmax_t) offset, op_name,
                  read_2ubyte_unaligned (dbg, data));
          CONSUME (2);
          offset += 3;
@@ -4284,7 +3791,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest,
        case DW_OP_call4:
          NEED (4);
          printf ("%*s[%4" PRIuMAX "] %s %" PRIu32 "\n",
-                 indent, "", (uintmax_t) offset, known[op],
+                 indent, "", (uintmax_t) offset, op_name,
                  read_4ubyte_unaligned (dbg, data));
          CONSUME (4);
          offset += 5;
@@ -4294,7 +3801,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest,
        case DW_OP_bra:
          NEED (2);
          printf ("%*s[%4" PRIuMAX "] %s %" PRIuMAX "\n",
-                 indent, "", (uintmax_t) offset, known[op],
+                 indent, "", (uintmax_t) offset, op_name,
                  (uintmax_t) (offset + read_2sbyte_unaligned (dbg, data)));
          CONSUME (2);
          data += 2;
@@ -4306,7 +3813,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest,
          NEED (1);
          get_uleb128 (uleb, data); /* XXX check overrun */
          printf ("%*s[%4" PRIuMAX "] %s: ",
-                 indent, "", (uintmax_t) offset, known[op]);
+                 indent, "", (uintmax_t) offset, op_name);
          NEED (uleb);
          print_block (uleb, data);
          data += uleb;
@@ -4331,7 +3838,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest,
 
          printf ("%*s[%4" PRIuMAX "] %s %#" PRIxMAX ", %+" PRId64 "\n",
                  indent, "", (intmax_t) offset,
-                 known[op], (uintmax_t) addr, sleb);
+                 op_name, (uintmax_t) addr, sleb);
          CONSUME (data - start);
          offset += 1 + (data - start);
          break;
@@ -4342,7 +3849,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest,
          NEED (1);
          get_uleb128 (uleb, data); /* XXX check overrun */
          printf ("%*s[%4" PRIuMAX "] %s:\n",
-                 indent, "", (uintmax_t) offset, known[op]);
+                 indent, "", (uintmax_t) offset, op_name);
          NEED (uleb);
          print_ops (dwflmod, dbg, indent + 6, indent + 6, vers,
                     addrsize, offset_size, uleb, data);
@@ -4359,7 +3866,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest,
          uint8_t usize = *(uint8_t *) data++;
          NEED (usize);
          printf ("%*s[%4" PRIuMAX "] %s [%6" PRIxMAX "] ",
-                 indent, "", (uintmax_t) offset, known[op], uleb);
+                 indent, "", (uintmax_t) offset, op_name, uleb);
          print_block (usize, data);
          data += usize;
          CONSUME (data - start);
@@ -4372,7 +3879,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest,
          get_uleb128 (uleb, data); /* XXX check overrun */
          get_uleb128 (uleb2, data); /* XXX check overrun */
          printf ("%*s[%4" PRIuMAX "] %s %" PRIu64 " %#" PRIx64 "\n",
-                 indent, "", (uintmax_t) offset, known[op], uleb, uleb2);
+                 indent, "", (uintmax_t) offset, op_name, uleb, uleb2);
          CONSUME (data - start);
          offset += 1 + (data - start);
          break;
@@ -4384,7 +3891,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest,
          get_uleb128 (uleb, data); /* XXX check overrun */
          printf ("%*s[%4" PRIuMAX "] %s %" PRIu8 " [%6" PRIxMAX "]\n",
                  indent, "", (uintmax_t) offset,
-                 known[op], usize, uleb);
+                 op_name, usize, uleb);
          CONSUME (data - start);
          offset += 1 + (data - start);
          break;
@@ -4395,19 +3902,27 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest,
          NEED (1);
          get_uleb128 (uleb, data); /* XXX check overrun */
          printf ("%*s[%4" PRIuMAX "] %s [%6" PRIxMAX "]\n",
-                 indent, "", (uintmax_t) offset, known[op], uleb);
+                 indent, "", (uintmax_t) offset, op_name, uleb);
          CONSUME (data - start);
          offset += 1 + (data - start);
          break;
 
+       case DW_OP_GNU_parameter_ref:
+         /* 4 byte CU relative reference to the abstract optimized away
+            DW_TAG_formal_parameter.  */
+         NEED (4);
+         printf ("%*s[%4" PRIuMAX "] %s [%6" PRIxMAX "]\n",
+                 indent, "", (uintmax_t) offset, op_name,
+                 (uintmax_t) read_4ubyte_unaligned (dbg, data));
+         CONSUME (4);
+         data += 4;
+         offset += 5;
+         break;
+
        default:
          /* No Operand.  */
-         if (op < sizeof known / sizeof known[0] && known[op] != NULL)
-           printf ("%*s[%4" PRIuMAX "] %s\n",
-                   indent, "", (uintmax_t) offset, known[op]);
-         else
-           printf ("%*s[%4" PRIuMAX "] %#x\n",
-                   indent, "", (uintmax_t) offset, op);
+         printf ("%*s[%4" PRIuMAX "] %s\n",
+                 indent, "", (uintmax_t) offset, op_name);
          ++offset;
          break;
        }
@@ -4417,7 +3932,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest,
 
     invalid:
       printf (gettext ("%*s[%4" PRIuMAX "] %s  <TRUNCATED>\n"),
-             indent, "", (uintmax_t) offset, known[op]);
+             indent, "", (uintmax_t) offset, op_name);
       break;
     }
 }
@@ -4566,13 +4081,16 @@ print_debug_abbrev_section (Dwfl_Module *dwflmod __attribute__ ((unused)),
                            Ebl *ebl, GElf_Ehdr *ehdr,
                            Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg)
 {
+  const size_t sh_size = (dbg->sectiondata[IDX_debug_abbrev] ?
+                         dbg->sectiondata[IDX_debug_abbrev]->d_size : 0);
+
   printf (gettext ("\nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n"
                   " [ Code]\n"),
          elf_ndxscn (scn), section_name (ebl, ehdr, shdr),
          (uint64_t) shdr->sh_offset);
 
   Dwarf_Off offset = 0;
-  while (offset < dbg->sectiondata[IDX_debug_abbrev]->d_size)
+  while (offset < sh_size)
     {
       printf (gettext ("\nAbbreviation section at offset %" PRIu64 ":\n"),
              offset);
@@ -4607,7 +4125,7 @@ print_debug_abbrev_section (Dwfl_Module *dwflmod __attribute__ ((unused)),
                           ", children: %s, tag: %s\n"),
                  code, (int64_t) offset,
                  has_children ? gettext ("yes") : gettext ("no"),
-                 dwarf_tag_string (tag));
+                 dwarf_tag_name (tag));
 
          size_t cnt = 0;
          unsigned int name;
@@ -4617,7 +4135,7 @@ print_debug_abbrev_section (Dwfl_Module *dwflmod __attribute__ ((unused)),
                                      &name, &form, &enoffset) == 0)
            {
              printf ("          attr: %s, form: %s, offset: %#" PRIx64 "\n",
-                     dwarf_attr_string (name), dwarf_form_string (form),
+                     dwarf_attr_name (name), dwarf_form_name (form),
                      (uint64_t) enoffset);
 
              ++cnt;
@@ -4787,9 +4305,12 @@ register_info (Ebl *ebl, unsigned int regno, const Ebl_Register_Location *loc,
                                 bits ?: &ignore, type ?: &ignore);
   if (n <= 0)
     {
-      snprintf (name, REGNAMESZ, "reg%u", loc->regno);
+      if (loc != NULL)
+       snprintf (name, REGNAMESZ, "reg%u", loc->regno);
+      else
+       snprintf (name, REGNAMESZ, "??? 0x%x", regno);
       if (bits != NULL)
-       *bits = loc->bits;
+       *bits = loc != NULL ? loc->bits : 0;
       if (type != NULL)
        *type = DW_ATE_unsigned;
       set = "??? unrecognized";
@@ -4797,7 +4318,7 @@ register_info (Ebl *ebl, unsigned int regno, const Ebl_Register_Location *loc,
   else
     {
       if (bits != NULL && *bits <= 0)
-       *bits = loc->bits;
+       *bits = loc != NULL ? loc->bits : 0;
       if (type != NULL && *type == DW_ATE_void)
        *type = DW_ATE_unsigned;
 
@@ -5566,8 +5087,8 @@ attr_callback (Dwarf_Attribute *attrp, void *arg)
            }
          char *a = format_dwarf_addr (cbargs->dwflmod, cbargs->addrsize, addr);
          printf ("           %*s%-20s (%s) %s\n",
-                 (int) (level * 2), "", dwarf_attr_string (attr),
-                 dwarf_form_string (form), a);
+                 (int) (level * 2), "", dwarf_attr_name (attr),
+                 dwarf_form_name (form), a);
          free (a);
        }
       break;
@@ -5575,14 +5096,15 @@ attr_callback (Dwarf_Attribute *attrp, void *arg)
     case DW_FORM_indirect:
     case DW_FORM_strp:
     case DW_FORM_string:
+    case DW_FORM_GNU_strp_alt:
       if (cbargs->silent)
        break;
       const char *str = dwarf_formstring (attrp);
       if (unlikely (str == NULL))
        goto attrval_out;
       printf ("           %*s%-20s (%s) \"%s\"\n",
-             (int) (level * 2), "", dwarf_attr_string (attr),
-             dwarf_form_string (form), str);
+             (int) (level * 2), "", dwarf_attr_name (attr),
+             dwarf_form_name (form), str);
       break;
 
     case DW_FORM_ref_addr:
@@ -5590,7 +5112,8 @@ attr_callback (Dwarf_Attribute *attrp, void *arg)
     case DW_FORM_ref8:
     case DW_FORM_ref4:
     case DW_FORM_ref2:
-    case DW_FORM_ref1:;
+    case DW_FORM_ref1:
+    case DW_FORM_GNU_ref_alt:
       if (cbargs->silent)
        break;
       Dwarf_Die ref;
@@ -5598,17 +5121,17 @@ attr_callback (Dwarf_Attribute *attrp, void *arg)
        goto attrval_out;
 
       printf ("           %*s%-20s (%s) [%6" PRIxMAX "]\n",
-             (int) (level * 2), "", dwarf_attr_string (attr),
-             dwarf_form_string (form), (uintmax_t) dwarf_dieoffset (&ref));
+             (int) (level * 2), "", dwarf_attr_name (attr),
+             dwarf_form_name (form), (uintmax_t) dwarf_dieoffset (&ref));
       break;
 
     case DW_FORM_ref_sig8:
       if (cbargs->silent)
        break;
       printf ("           %*s%-20s (%s) {%6" PRIx64 "}\n",
-             (int) (level * 2), "", dwarf_attr_string (attr),
-             dwarf_form_string (form),
-             read_8ubyte_unaligned (attrp->cu->dbg, attrp->valp));
+             (int) (level * 2), "", dwarf_attr_name (attr),
+             dwarf_form_name (form),
+             (uint64_t) read_8ubyte_unaligned (attrp->cu->dbg, attrp->valp));
       break;
 
     case DW_FORM_sec_offset:
@@ -5633,8 +5156,8 @@ attr_callback (Dwarf_Attribute *attrp, void *arg)
            {
              if (!cbargs->silent)
                printf ("           %*s%-20s (%s) %" PRIxMAX "\n",
-                       (int) (level * 2), "", dwarf_attr_string (attr),
-                       dwarf_form_string (form), (uintmax_t) num);
+                       (int) (level * 2), "", dwarf_attr_name (attr),
+                       dwarf_form_name (form), (uintmax_t) num);
              return DWARF_CB_OK;
            }
          /* else fallthrough */
@@ -5656,8 +5179,8 @@ attr_callback (Dwarf_Attribute *attrp, void *arg)
                          cbargs->addrsize, cbargs->offset_size, num);
          if (!cbargs->silent)
            printf ("           %*s%-20s (%s) location list [%6" PRIxMAX "]\n",
-                   (int) (level * 2), "", dwarf_attr_string (attr),
-                   dwarf_form_string (form), (uintmax_t) num);
+                   (int) (level * 2), "", dwarf_attr_name (attr),
+                   dwarf_form_name (form), (uintmax_t) num);
          return DWARF_CB_OK;
 
        case DW_AT_ranges:
@@ -5665,39 +5188,39 @@ attr_callback (Dwarf_Attribute *attrp, void *arg)
                          cbargs->addrsize, cbargs->offset_size, num);
          if (!cbargs->silent)
            printf ("           %*s%-20s (%s) range list [%6" PRIxMAX "]\n",
-                   (int) (level * 2), "", dwarf_attr_string (attr),
-                   dwarf_form_string (form), (uintmax_t) num);
+                   (int) (level * 2), "", dwarf_attr_name (attr),
+                   dwarf_form_name (form), (uintmax_t) num);
          return DWARF_CB_OK;
 
        case DW_AT_language:
-         valuestr = dwarf_lang_string (num);
+         valuestr = dwarf_lang_name (num);
          break;
        case DW_AT_encoding:
-         valuestr = dwarf_encoding_string (num);
+         valuestr = dwarf_encoding_name (num);
          break;
        case DW_AT_accessibility:
-         valuestr = dwarf_access_string (num);
+         valuestr = dwarf_access_name (num);
          break;
        case DW_AT_visibility:
-         valuestr = dwarf_visibility_string (num);
+         valuestr = dwarf_visibility_name (num);
          break;
        case DW_AT_virtuality:
-         valuestr = dwarf_virtuality_string (num);
+         valuestr = dwarf_virtuality_name (num);
          break;
        case DW_AT_identifier_case:
-         valuestr = dwarf_identifier_case_string (num);
+         valuestr = dwarf_identifier_case_name (num);
          break;
        case DW_AT_calling_convention:
-         valuestr = dwarf_calling_convention_string (num);
+         valuestr = dwarf_calling_convention_name (num);
          break;
        case DW_AT_inline:
-         valuestr = dwarf_inline_string (num);
+         valuestr = dwarf_inline_name (num);
          break;
        case DW_AT_ordering:
-         valuestr = dwarf_ordering_string (num);
+         valuestr = dwarf_ordering_name (num);
          break;
        case DW_AT_discr_list:
-         valuestr = dwarf_discr_list_string (num);
+         valuestr = dwarf_discr_list_name (num);
          break;
        default:
          /* Nothing.  */
@@ -5709,12 +5232,12 @@ attr_callback (Dwarf_Attribute *attrp, void *arg)
 
       if (valuestr == NULL)
        printf ("           %*s%-20s (%s) %" PRIuMAX "\n",
-               (int) (level * 2), "", dwarf_attr_string (attr),
-               dwarf_form_string (form), (uintmax_t) num);
+               (int) (level * 2), "", dwarf_attr_name (attr),
+               dwarf_form_name (form), (uintmax_t) num);
       else
        printf ("           %*s%-20s (%s) %s (%" PRIuMAX ")\n",
-               (int) (level * 2), "", dwarf_attr_string (attr),
-               dwarf_form_string (form), valuestr, (uintmax_t) num);
+               (int) (level * 2), "", dwarf_attr_name (attr),
+               dwarf_form_name (form), valuestr, (uintmax_t) num);
       break;
 
     case DW_FORM_flag:
@@ -5725,16 +5248,16 @@ attr_callback (Dwarf_Attribute *attrp, void *arg)
        goto attrval_out;
 
       printf ("           %*s%-20s (%s) %s\n",
-             (int) (level * 2), "", dwarf_attr_string (attr),
-             dwarf_form_string (form), nl_langinfo (flag ? YESSTR : NOSTR));
+             (int) (level * 2), "", dwarf_attr_name (attr),
+             dwarf_form_name (form), nl_langinfo (flag ? YESSTR : NOSTR));
       break;
 
     case DW_FORM_flag_present:
       if (cbargs->silent)
        break;
       printf ("           %*s%-20s (%s) %s\n",
-             (int) (level * 2), "", dwarf_attr_string (attr),
-             dwarf_form_string (form), nl_langinfo (YESSTR));
+             (int) (level * 2), "", dwarf_attr_name (attr),
+             dwarf_form_name (form), nl_langinfo (YESSTR));
       break;
 
     case DW_FORM_exprloc:
@@ -5749,8 +5272,8 @@ attr_callback (Dwarf_Attribute *attrp, void *arg)
        goto attrval_out;
 
       printf ("           %*s%-20s (%s) ",
-             (int) (level * 2), "", dwarf_attr_string (attr),
-             dwarf_form_string (form));
+             (int) (level * 2), "", dwarf_attr_name (attr),
+             dwarf_form_name (form));
 
       switch (attr)
        {
@@ -5798,7 +5321,7 @@ attr_callback (Dwarf_Attribute *attrp, void *arg)
       if (cbargs->silent)
        break;
       printf ("           %*s%-20s (form: %#x) ???\n",
-             (int) (level * 2), "", dwarf_attr_string (attr),
+             (int) (level * 2), "", dwarf_attr_name (attr),
              (int) form);
       break;
     }
@@ -5913,7 +5436,7 @@ print_debug_units (Dwfl_Module *dwflmod,
       if (!silent)
        printf (" [%6" PRIx64 "]  %*s%s\n",
                (uint64_t) offset, (int) (level * 2), "",
-               dwarf_tag_string (tag));
+               dwarf_tag_name (tag));
 
       /* Print the attribute values.  */
       args.level = level;
@@ -6746,6 +6269,412 @@ print_debug_macinfo_section (Dwfl_Module *dwflmod __attribute__ ((unused)),
 }
 
 
+static void
+print_debug_macro_section (Dwfl_Module *dwflmod __attribute__ ((unused)),
+                          Ebl *ebl, GElf_Ehdr *ehdr,
+                          Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg)
+{
+  printf (gettext ("\
+\nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n"),
+         elf_ndxscn (scn), section_name (ebl, ehdr, shdr),
+         (uint64_t) shdr->sh_offset);
+  putc_unlocked ('\n', stdout);
+
+  Elf_Data *data = elf_getdata (scn, NULL);
+  if (unlikely (data == NULL || data->d_buf == NULL))
+    {
+      error (0, 0, gettext ("cannot get macro information section data: %s"),
+            elf_errmsg (-1));
+      return;
+    }
+
+  /* Get the source file information for all CUs.  Uses same
+     datastructure as macinfo.  But uses offset field to directly
+     match .debug_line offset.  And just stored in a list.  */
+  Dwarf_Off offset;
+  Dwarf_Off ncu = 0;
+  size_t hsize;
+  struct mac_culist *culist = NULL;
+  size_t nculist = 0;
+  while (dwarf_nextcu (dbg, offset = ncu, &ncu, &hsize, NULL, NULL, NULL) == 0)
+    {
+      Dwarf_Die cudie;
+      if (dwarf_offdie (dbg, offset + hsize, &cudie) == NULL)
+       continue;
+
+      Dwarf_Attribute attr;
+      if (dwarf_attr (&cudie, DW_AT_stmt_list, &attr) == NULL)
+       continue;
+
+      Dwarf_Word lineoff;
+      if (dwarf_formudata (&attr, &lineoff) != 0)
+       continue;
+
+      struct mac_culist *newp = (struct mac_culist *) alloca (sizeof (*newp));
+      newp->die = cudie;
+      newp->offset = lineoff;
+      newp->files = NULL;
+      newp->next = culist;
+      culist = newp;
+      ++nculist;
+    }
+
+  const unsigned char *readp = (const unsigned char *) data->d_buf;
+  const unsigned char *readendp = readp + data->d_size;
+
+  while (readp < readendp)
+    {
+      printf (gettext (" Offset:             0x%" PRIx64 "\n"),
+             (uint64_t) (readp - (const unsigned char *) data->d_buf));
+
+      // Header, 2 byte version, 1 byte flag, optional .debug_line offset,
+      // optional vendor extension macro entry table.
+      if (readp + 2 > readendp)
+       {
+       invalid_data:
+         error (0, 0, gettext ("invalid data"));
+         return;
+       }
+      const uint16_t vers = read_2ubyte_unaligned_inc (dbg, readp);
+      printf (gettext (" Version:            %" PRIu16 "\n"), vers);
+
+      // Version 4 is the GNU extension for DWARF4.  DWARF5 will use version
+      // 5 when it gets standardized.
+      if (vers != 4)
+       {
+         printf (gettext ("  unknown version, cannot parse section\n"));
+         return;
+       }
+
+      if (readp + 1 > readendp)
+       goto invalid_data;
+      const unsigned char flag = *readp++;
+      printf (gettext (" Flag:               0x%" PRIx8 "\n"), flag);
+
+      unsigned int offset_len = (flag & 0x01) ? 8 : 4;
+      printf (gettext (" Offset length:      %" PRIu8 "\n"), offset_len);
+      Dwarf_Off line_offset = -1;
+      if (flag & 0x02)
+       {
+         if (offset_len == 8)
+           line_offset = read_8ubyte_unaligned_inc (dbg, readp);
+         else
+           line_offset = read_4ubyte_unaligned_inc (dbg, readp);
+         printf (gettext (" .debug_line offset: 0x%" PRIx64 "\n"),
+                 line_offset);
+       }
+
+      const unsigned char *vendor[DW_MACRO_GNU_hi_user - DW_MACRO_GNU_lo_user];
+      if (flag & 0x04)
+       {
+         // 1 byte length, for each item, 1 byte opcode, uleb128 number
+         // of arguments, for each argument 1 byte form code.
+         if (readp + 1 > readendp)
+           goto invalid_data;
+         unsigned int tlen = *readp++;
+         printf (gettext ("  extension opcode table, %" PRIu8 " items:\n"),
+                 tlen);
+         for (unsigned int i = 0; i < tlen; i++)
+           {
+             if (readp + 1 > readendp)
+               goto invalid_data;
+             unsigned int opcode = *readp++;
+             printf (gettext ("    [%" PRIx8 "]"), opcode);
+             if (opcode < DW_MACRO_GNU_lo_user
+                 || opcode > DW_MACRO_GNU_hi_user)
+               goto invalid_data;
+             // Record the start of description for this vendor opcode.
+             // uleb128 nr args, 1 byte per arg form.
+             vendor[opcode - DW_MACRO_GNU_lo_user] = readp;
+             if (readp + 1 > readendp)
+               goto invalid_data;
+             unsigned int args = *readp++;
+             if (args > 0)
+               {
+                 printf (gettext (" %" PRIu8 " arguments:"), args);
+                 while (args > 0)
+                   {
+                     if (readp + 1 > readendp)
+                       goto invalid_data;
+                     unsigned int form = *readp++;
+                     printf (" %s", dwarf_form_string (form));
+                     if (form != DW_FORM_data1
+                         && form != DW_FORM_data2
+                         && form != DW_FORM_data4
+                         && form != DW_FORM_data8
+                         && form != DW_FORM_sdata
+                         && form != DW_FORM_udata
+                         && form != DW_FORM_block
+                         && form != DW_FORM_block1
+                         && form != DW_FORM_block2
+                         && form != DW_FORM_block4
+                         && form != DW_FORM_flag
+                         && form != DW_FORM_string
+                         && form != DW_FORM_strp
+                         && form != DW_FORM_sec_offset)
+                       goto invalid_data;
+                     args--;
+                     if (args > 0)
+                       putchar_unlocked (',');
+                   }
+               }
+             else
+               printf (gettext (" no arguments."));
+             putchar_unlocked ('\n');
+           }
+       }
+      putchar_unlocked ('\n');
+
+      int level = 1;
+      if (readp + 1 > readendp)
+       goto invalid_data;
+      unsigned int opcode = *readp++;
+      while (opcode != 0)
+       {
+         unsigned int u128;
+         unsigned int u128_2;
+         const unsigned char *endp;
+         uint64_t off;
+
+          switch (opcode)
+            {
+            case DW_MACRO_GNU_start_file:
+             get_uleb128 (u128, readp);
+             get_uleb128 (u128_2, readp);
+
+             /* Find the CU DIE that matches this line offset.  */
+             const char *fname = "???";
+             if (line_offset != (Dwarf_Off) -1)
+               {
+                 struct mac_culist *cu = culist;
+                 while (cu != NULL && line_offset != cu->offset)
+                   cu = cu->next;
+                 if (cu != NULL)
+                   {
+                     if (cu->files == NULL
+                         && dwarf_getsrcfiles (&cu->die, &cu->files,
+                                               NULL) != 0)
+                       cu->files = (Dwarf_Files *) -1l;
+
+                     if (cu->files != (Dwarf_Files *) -1l)
+                       fname = (dwarf_filesrc (cu->files, u128_2,
+                                               NULL, NULL) ?: "???");
+                   }
+               }
+             printf ("%*sstart_file %u, [%u] %s\n",
+                     level, "", u128, u128_2, fname);
+             ++level;
+             break;
+
+           case DW_MACRO_GNU_end_file:
+             --level;
+             printf ("%*send_file\n", level, "");
+             break;
+
+           case DW_MACRO_GNU_define:
+             get_uleb128 (u128, readp);
+             endp = memchr (readp, '\0', readendp - readp);
+             if (endp == NULL)
+               goto invalid_data;
+             printf ("%*s#define %s, line %u\n",
+                     level, "", readp, u128);
+             readp = endp + 1;
+             break;
+
+           case DW_MACRO_GNU_undef:
+             get_uleb128 (u128, readp);
+             endp = memchr (readp, '\0', readendp - readp);
+             if (endp == NULL)
+               goto invalid_data;
+             printf ("%*s#undef %s, line %u\n",
+                     level, "", readp, u128);
+             readp = endp + 1;
+             break;
+
+           case DW_MACRO_GNU_define_indirect:
+             get_uleb128 (u128, readp);
+             if (readp + offset_len > readendp)
+               goto invalid_data;
+             if (offset_len == 8)
+               off = read_8ubyte_unaligned_inc (dbg, readp);
+             else
+               off = read_4ubyte_unaligned_inc (dbg, readp);
+             printf ("%*s#define %s, line %u (indirect)\n",
+                     level, "", dwarf_getstring (dbg, off, NULL), u128);
+             break;
+
+           case DW_MACRO_GNU_undef_indirect:
+             get_uleb128 (u128, readp);
+             if (readp + offset_len > readendp)
+               goto invalid_data;
+             if (offset_len == 8)
+               off = read_8ubyte_unaligned_inc (dbg, readp);
+             else
+               off = read_4ubyte_unaligned_inc (dbg, readp);
+             printf ("%*s#undef %s, line %u (indirect)\n",
+                     level, "", dwarf_getstring (dbg, off, NULL), u128);
+             break;
+
+           case DW_MACRO_GNU_transparent_include:
+             if (readp + offset_len > readendp)
+               goto invalid_data;
+             if (offset_len == 8)
+               off = read_8ubyte_unaligned_inc (dbg, readp);
+             else
+               off = read_4ubyte_unaligned_inc (dbg, readp);
+             printf ("%*s#include offset 0x%" PRIx64 "\n",
+                     level, "", off);
+             break;
+
+           default:
+             printf ("%*svendor opcode 0x%" PRIx8, level, "", opcode);
+             if (opcode < DW_MACRO_GNU_lo_user
+                 || opcode > DW_MACRO_GNU_lo_user
+                 || vendor[opcode - DW_MACRO_GNU_lo_user] == NULL)
+               goto invalid_data;
+
+             const unsigned char *op_desc;
+             op_desc = vendor[opcode - DW_MACRO_GNU_lo_user];
+
+             // Just skip the arguments, we cannot really interpret them,
+             // but print as much as we can.
+             unsigned int args = *op_desc++;
+             while (args > 0)
+               {
+                 unsigned int form = *op_desc++;
+                 Dwarf_Word val;
+                 switch (form)
+                   {
+                   case DW_FORM_data1:
+                     if (readp + 1 > readendp)
+                       goto invalid_data;
+                     val = *readp++;
+                     printf (" %" PRIx8, (unsigned int) val);
+                     break;
+
+                   case DW_FORM_data2:
+                     if (readp + 2 > readendp)
+                       goto invalid_data;
+                     val = read_2ubyte_unaligned_inc (dbg, readp);
+                     printf(" %" PRIx16, (unsigned int) val);
+                     break;
+
+                   case DW_FORM_data4:
+                     if (readp + 4 > readendp)
+                       goto invalid_data;
+                     val = read_4ubyte_unaligned_inc (dbg, readp);
+                     printf (" %" PRIx32, (unsigned int) val);
+                     break;
+
+                   case DW_FORM_data8:
+                     if (readp + 8 > readendp)
+                       goto invalid_data;
+                     val = read_8ubyte_unaligned_inc (dbg, readp);
+                     printf (" %" PRIx64, val);
+                     break;
+
+                   case DW_FORM_sdata:
+                     get_sleb128 (val, readp);
+                     printf (" %" PRIx64, val);
+                     break;
+
+                   case DW_FORM_udata:
+                     get_uleb128 (val, readp);
+                     printf (" %" PRIx64, val);
+                     break;
+
+                   case DW_FORM_block:
+                     get_uleb128 (val, readp);
+                     printf (" block[%" PRIu64 "]", val);
+                     if (readp + val > readendp)
+                       goto invalid_data;
+                     readp += val;
+                     break;
+
+                   case DW_FORM_block1:
+                     if (readp + 1 > readendp)
+                       goto invalid_data;
+                     val = *readp++;
+                     printf (" block[%" PRIu64 "]", val);
+                     if (readp + val > readendp)
+                       goto invalid_data;
+                     break;
+
+                   case DW_FORM_block2:
+                     if (readp + 2 > readendp)
+                       goto invalid_data;
+                     val = read_2ubyte_unaligned_inc (dbg, readp);
+                     printf (" block[%" PRIu64 "]", val);
+                     if (readp + val > readendp)
+                       goto invalid_data;
+                     break;
+
+                   case DW_FORM_block4:
+                     if (readp + 2 > readendp)
+                       goto invalid_data;
+                     val =read_4ubyte_unaligned_inc (dbg, readp);
+                     printf (" block[%" PRIu64 "]", val);
+                     if (readp + val > readendp)
+                       goto invalid_data;
+                     break;
+
+                   case DW_FORM_flag:
+                     if (readp + 1 > readendp)
+                       goto invalid_data;
+                     val = *readp++;
+                     printf (" %s", nl_langinfo (val != 0 ? YESSTR : NOSTR));
+                     break;
+
+                   case DW_FORM_string:
+                     endp = memchr (readp, '\0', readendp - readp);
+                     if (endp == NULL)
+                       goto invalid_data;
+                     printf (" %s", readp);
+                     readp = endp + 1;
+                     break;
+
+                   case DW_FORM_strp:
+                     if (readp + offset_len > readendp)
+                       goto invalid_data;
+                     if (offset_len == 8)
+                       val = read_8ubyte_unaligned_inc (dbg, readp);
+                     else
+                       val = read_4ubyte_unaligned_inc (dbg, readp);
+                     printf (" %s", dwarf_getstring (dbg, val, NULL));
+                     break;
+
+                   case DW_FORM_sec_offset:
+                     if (readp + offset_len > readendp)
+                       goto invalid_data;
+                     if (offset_len == 8)
+                       val = read_8ubyte_unaligned_inc (dbg, readp);
+                     else
+                       val = read_4ubyte_unaligned_inc (dbg, readp);
+                     printf (" %" PRIx64, val);
+                     break;
+
+                     default:
+                       error (0, 0, gettext ("vendor opcode not verified?"));
+                       return;
+                   }
+
+                 args--;
+                 if (args > 0)
+                   putchar_unlocked (',');
+               }
+             putchar_unlocked ('\n');
+           }
+
+         if (readp + 1 > readendp)
+           goto invalid_data;
+         opcode = *readp++;
+         if (opcode == 0)
+           putchar_unlocked ('\n');
+       }
+    }
+}
+
+
 /* Callback for printing global names.  */
 static int
 print_pubnames (Dwarf *dbg __attribute__ ((unused)), Dwarf_Global *global,
@@ -6781,7 +6710,8 @@ print_debug_str_section (Dwfl_Module *dwflmod __attribute__ ((unused)),
                         Ebl *ebl, GElf_Ehdr *ehdr,
                         Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg)
 {
-  const size_t sh_size = dbg->sectiondata[IDX_debug_str]->d_size;
+  const size_t sh_size = (dbg->sectiondata[IDX_debug_str] ?
+                         dbg->sectiondata[IDX_debug_str]->d_size : 0);
 
   /* Compute floor(log16(shdr->sh_size)).  */
   GElf_Addr tmp = sh_size;
@@ -7118,8 +7048,9 @@ print_gdb_index_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr,
   printf (gettext (" Version:         %" PRId32 "\n"), vers);
 
   // The only difference between version 4 and version 5 is the
-  // hash used for generating the table.
-  if (vers < 4 || vers > 5)
+  // hash used for generating the table.  Version 6 contains symbols
+  // for inlined functions, older versions didn't.
+  if (vers < 4 || vers > 7)
     {
       printf (gettext ("  unknown version, cannot parse section\n"));
       return;
@@ -7163,14 +7094,14 @@ print_gdb_index_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr,
   readp = data->d_buf + cu_off;
 
   const unsigned char *nextp = data->d_buf + tu_off;
-  size_t nr = (nextp - readp) / 16;
+  size_t cu_nr = (nextp - readp) / 16;
 
   printf (gettext ("\n CU list at offset %#" PRIx32
                   " contains %zu entries:\n"),
-         cu_off, nr);
+         cu_off, cu_nr);
 
   size_t n = 0;
-  while (readp + 16 <= dataend && n < nr)
+  while (readp + 16 <= dataend && n < cu_nr)
     {
       uint64_t off = read_8ubyte_unaligned (dbg, readp);
       readp += 8;
@@ -7185,14 +7116,14 @@ print_gdb_index_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr,
 
   readp = data->d_buf + tu_off;
   nextp = data->d_buf + addr_off;
-  nr = (nextp - readp) / 24;
+  size_t tu_nr = (nextp - readp) / 24;
 
   printf (gettext ("\n TU list at offset %#" PRIx32
                   " contains %zu entries:\n"),
-         tu_off, nr);
+         tu_off, tu_nr);
 
   n = 0;
-  while (readp + 24 <= dataend && n < nr)
+  while (readp + 24 <= dataend && n < tu_nr)
     {
       uint64_t off = read_8ubyte_unaligned (dbg, readp);
       readp += 8;
@@ -7211,14 +7142,14 @@ print_gdb_index_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr,
 
   readp = data->d_buf + addr_off;
   nextp = data->d_buf + sym_off;
-  nr = (nextp - readp) / 20;
+  size_t addr_nr = (nextp - readp) / 20;
 
   printf (gettext ("\n Address list at offset %#" PRIx32
                   " contains %zu entries:\n"),
-         addr_off, nr);
+         addr_off, addr_nr);
 
   n = 0;
-  while (readp + 20 <= dataend && n < nr)
+  while (readp + 20 <= dataend && n < addr_nr)
     {
       uint64_t low = read_8ubyte_unaligned (dbg, readp);
       readp += 8;
@@ -7238,14 +7169,14 @@ print_gdb_index_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr,
 
   readp = data->d_buf + sym_off;
   nextp = data->d_buf + const_off;
-  nr = (nextp - readp) / 8;
+  size_t sym_nr = (nextp - readp) / 8;
 
   printf (gettext ("\n Symbol table at offset %#" PRIx32
                   " contains %zu slots:\n"),
-         addr_off, nr);
+         addr_off, sym_nr);
 
   n = 0;
-  while (readp + 8 <= dataend && n < nr)
+  while (readp + 8 <= dataend && n < sym_nr)
     {
       uint32_t name = read_4ubyte_unaligned (dbg, readp);
       readp += 4;
@@ -7268,10 +7199,42 @@ print_gdb_index_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr,
          uint32_t cus = read_4ubyte_unaligned (dbg, readcus);
          while (cus--)
            {
-             uint32_t cu;
+             uint32_t cu_kind, cu, kind;
+             bool is_static;
              readcus += 4;
-             cu = read_4ubyte_unaligned (dbg, readcus);
-             printf ("%" PRId32 "%s", cu, ((cus > 0) ? ", " : ""));
+             cu_kind = read_4ubyte_unaligned (dbg, readcus);
+             cu = cu_kind & ((1 << 24) - 1);
+             kind = (cu_kind >> 28) & 7;
+             is_static = cu_kind & (1 << 31);
+             if (cu > cu_nr - 1)
+               printf ("%" PRId32 "T", cu - (uint32_t) cu_nr);
+             else
+               printf ("%" PRId32, cu);
+             if (kind != 0)
+               {
+                 printf (" (");
+                 switch (kind)
+                   {
+                   case 1:
+                     printf ("type");
+                     break;
+                   case 2:
+                     printf ("var");
+                     break;
+                   case 3:
+                     printf ("func");
+                     break;
+                   case 4:
+                     printf ("other");
+                     break;
+                   default:
+                     printf ("unknown-0x%" PRIx32, kind);
+                     break;
+                   }
+                 printf (":%c)", (is_static ? 'S' : 'G'));
+               }
+             if (cus > 0)
+               printf (", ");
            }
          printf ("\n");
        }
@@ -7335,6 +7298,7 @@ print_debug (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr)
              NEW_SECTION (pubnames),
              NEW_SECTION (str),
              NEW_SECTION (macinfo),
+             NEW_SECTION (macro),
              NEW_SECTION (ranges),
              { ".eh_frame", section_frame | section_exception,
                print_debug_frame_section },